From ab7962260332f9b4049091bddcea4c17548662aa Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 22 May 2023 14:56:06 +0800 Subject: [PATCH 001/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20inpage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ios/Portkey.xcodeproj/project.pbxproj | 4 + .../js/utils/EntryScriptWeb3.ts | 23 ++++ .../js/utils/InpageBridgeWeb3.js | 106 ++++++++++++++++++ packages/mobile-app-did/package.json | 1 + yarn.lock | 15 ++- 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 packages/mobile-app-did/js/utils/EntryScriptWeb3.ts create mode 100644 packages/mobile-app-did/js/utils/InpageBridgeWeb3.js diff --git a/packages/mobile-app-did/ios/Portkey.xcodeproj/project.pbxproj b/packages/mobile-app-did/ios/Portkey.xcodeproj/project.pbxproj index 0a8bdea092..92a7e598ec 100644 --- a/packages/mobile-app-did/ios/Portkey.xcodeproj/project.pbxproj +++ b/packages/mobile-app-did/ios/Portkey.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 19EC8BC9327908E9BDB886C0 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4EB37D631CAABEBC0CC18B /* ExpoModulesProvider.swift */; }; + 2B25ECFC2A1B48DC00586D7D /* InpageBridgeWeb3.js in Resources */ = {isa = PBXBuildFile; fileRef = 2B25ECFB2A1B48DC00586D7D /* InpageBridgeWeb3.js */; }; 3652EFEAF7157AE32F0A183B /* libPods-Portkey-PortkeyTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2DDB7964DE97D61553C3007 /* libPods-Portkey-PortkeyTests.a */; }; 6067DF7E96A3512FE02CDF4C /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB8AAA944A581AA64A54B32 /* ExpoModulesProvider.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; @@ -40,6 +41,7 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Portkey/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Portkey/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Portkey/main.m; sourceTree = ""; }; + 2B25ECFB2A1B48DC00586D7D /* InpageBridgeWeb3.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = InpageBridgeWeb3.js; path = ../js/utils/InpageBridgeWeb3.js; sourceTree = ""; }; 2B4CF73429344C200071ED92 /* hermes.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = hermes.xcframework; path = "Pods/hermes-engine/destroot/Library/Frameworks/universal/hermes.xcframework"; sourceTree = ""; }; 2B96396A28ACF299003CD665 /* Portkey.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Portkey.entitlements; path = Portkey/Portkey.entitlements; sourceTree = ""; }; 3B4392A12AC88292D35C810B /* Pods-Portkey.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Portkey.debug.xcconfig"; path = "Target Support Files/Pods-Portkey/Pods-Portkey.debug.xcconfig"; sourceTree = ""; }; @@ -98,6 +100,7 @@ 13B07FAE1A68108700A75B9A /* Portkey */ = { isa = PBXGroup; children = ( + 2B25ECFB2A1B48DC00586D7D /* InpageBridgeWeb3.js */, 2B96396A28ACF299003CD665 /* Portkey.entitlements */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.mm */, @@ -290,6 +293,7 @@ buildActionMask = 2147483647; files = ( 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 2B25ECFC2A1B48DC00586D7D /* InpageBridgeWeb3.js in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, E5A1B79EA5754765B45B0F05 /* SplashScreen.storyboard in Resources */, 9C403D3329E5603000035DBC /* GoogleService-Info.plist in Resources */, diff --git a/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts b/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts new file mode 100644 index 0000000000..85b2c47d96 --- /dev/null +++ b/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts @@ -0,0 +1,23 @@ +import RNFS from 'react-native-fs'; +import { isIos } from '@portkey-wallet/utils/mobile/device'; + +const EntryScriptWeb3 = { + entryScriptWeb3: '', + // Cache InpageBridgeWeb3 so that it is immediately available + async init() { + this.entryScriptWeb3 = isIos + ? await RNFS.readFile(`${RNFS.MainBundlePath}/InpageBridgeWeb3.js`, 'utf8') + : await RNFS.readFileAssets(`InpageBridgeWeb3.js`); + + return this.entryScriptWeb3; + }, + async get() { + // Return from cache + if (this.entryScriptWeb3) return this.entryScriptWeb3; + + // If for some reason it is not available, get it again + return await this.init(); + }, +}; + +export default EntryScriptWeb3; diff --git a/packages/mobile-app-did/js/utils/InpageBridgeWeb3.js b/packages/mobile-app-did/js/utils/InpageBridgeWeb3.js new file mode 100644 index 0000000000..ed92f21cb9 --- /dev/null +++ b/packages/mobile-app-did/js/utils/InpageBridgeWeb3.js @@ -0,0 +1,106 @@ +!(function (e) { + var t = {}; + function r(n) { + if (t[n]) return t[n].exports; + var i = (t[n] = { i: n, l: !1, exports: {} }); + return e[n].call(i.exports, i, i.exports, r), (i.l = !0), i.exports; + } + (r.m = e), + (r.c = t), + (r.d = function (e, t, n) { + r.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: n }); + }), + (r.r = function (e) { + typeof Symbol !== 'undefined' && + Symbol.toStringTag && + Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }), + Object.defineProperty(e, '__esModule', { value: !0 }); + }), + (r.t = function (e, t) { + if ((1 & t && (e = r(e)), 8 & t)) return e; + if (4 & t && typeof e === 'object' && e && e.__esModule) return e; + var n = Object.create(null); + if ((r.r(n), Object.defineProperty(n, 'default', { enumerable: !0, value: e }), 2 & t && typeof e !== 'string')) + for (var i in e) + r.d( + n, + i, + function (t) { + return e[t]; + }.bind(null, i), + ); + return n; + }), + (r.n = function (e) { + var t = + e && e.__esModule + ? function () { + return e.default; + } + : function () { + return e; + }; + return r.d(t, 'a', t), t; + }), + (r.o = function (e, t) { + return Object.prototype.hasOwnProperty.call(e, t); + }), + (r.p = ''), + r((r.s = 0)); +})([ + function (e, t) { + (function () { + const { doctype: e } = window.document; + return !e || e.name === 'html'; + })() && + (function () { + const e = [/\\.xml$/u, /\\.pdf$/u], + t = window.location.pathname; + for (let r = 0; r < e.length; r++) if (e[r].test(t)) return !1; + return !0; + })() && + (function () { + const e = document.documentElement.nodeName; + return !e || e.toLowerCase() === 'html'; + })() && + !(function () { + const e = [ + 'uscourts.gov', + 'dropbox.com', + 'webbyawards.com', + 'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html', + 'adyen.com', + 'gravityforms.com', + 'harbourair.com', + 'ani.gamer.com.tw', + 'blueskybooking.com', + 'sharefile.com', + ], + t = window.location.href; + let r; + for (let n = 0; n < e.length; n++) { + const i = e[n].replace('.', '\\.'); + if (((r = new RegExp(`(?:https?:\\/\\/)(?:(?!${i}).)*$`, 'u')), !r.test(t))) return !0; + } + return !1; + })() && + ((function (e) { + try { + const t = document.head || document.documentElement, + r = document.createElement('script'); + r.setAttribute('async', !1), (r.textContent = e), t.insertBefore(r, t.children[0]), t.removeChild(r); + } catch (e) { + console.error('MetaMask script injection failed', e); + } + })( + '!function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=22)}([function(e,t,r){"use strict";var n=r(7),i=Object.keys||function(e){var t=[];for(var r in e)t.push(r);return t};e.exports=h;var o=Object.create(r(4));o.inherits=r(2);var a=r(15),s=r(10);o.inherits(h,a);for(var f=i(s.prototype),c=0;c1)for(var r=1;r\n * @license MIT\n */\nvar n=r(23),i=r(24),o=r(11);function a(){return f.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function p(e,t){if(f.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var r=e.length;if(0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return H(e).length;default:if(n)return F(e).length;t=(""+t).toLowerCase(),n=!0}}function y(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return I(this,t,r);case"utf8":case"utf-8":return O(this,t,r);case"ascii":return x(this,t,r);case"latin1":case"binary":return T(this,t,r);case"base64":return k(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return M(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function b(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function v(e,t,r,n,i){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof t&&(t=f.from(t,n)),f.isBuffer(t))return 0===t.length?-1:g(e,t,r,n,i);if("number"==typeof t)return t&=255,f.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):g(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function g(e,t,r,n,i){var o,a=1,s=e.length,f=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;a=2,s/=2,f/=2,r/=2}function c(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(i){var u=-1;for(o=r;os&&(r=s-f),o=r;o>=0;o--){for(var h=!0,l=0;li&&(n=i):n=i;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");n>o/2&&(n=o/2);for(var a=0;a>8,i=r%256,o.push(i),o.push(n);return o}(t,e.length-r),e,r,n)}function k(e,t,r){return 0===t&&r===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,r))}function O(e,t,r){r=Math.min(e.length,r);for(var n=[],i=t;i239?4:c>223?3:c>191?2:1;if(i+h<=r)switch(h){case 1:c<128&&(u=c);break;case 2:128==(192&(o=e[i+1]))&&(f=(31&c)<<6|63&o)>127&&(u=f);break;case 3:o=e[i+1],a=e[i+2],128==(192&o)&&128==(192&a)&&(f=(15&c)<<12|(63&o)<<6|63&a)>2047&&(f<55296||f>57343)&&(u=f);break;case 4:o=e[i+1],a=e[i+2],s=e[i+3],128==(192&o)&&128==(192&a)&&128==(192&s)&&(f=(15&c)<<18|(63&o)<<12|(63&a)<<6|63&s)>65535&&f<1114112&&(u=f)}null===u?(u=65533,h=1):u>65535&&(u-=65536,n.push(u>>>10&1023|55296),u=56320|1023&u),n.push(u),i+=h}return function(e){var t=e.length;if(t<=4096)return String.fromCharCode.apply(String,e);var r="",n=0;for(;n0&&(e=this.toString("hex",0,r).match(/.{2}/g).join(" "),this.length>r&&(e+=" ... ")),""},f.prototype.compare=function(e,t,r,n,i){if(!f.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return-1;if(t>=r)return 1;if(this===e)return 0;for(var o=(i>>>=0)-(n>>>=0),a=(r>>>=0)-(t>>>=0),s=Math.min(o,a),c=this.slice(n,i),u=e.slice(t,r),h=0;hi)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return m(this,e,t,r);case"utf8":case"utf-8":return w(this,e,t,r);case"ascii":return _(this,e,t,r);case"latin1":case"binary":return E(this,e,t,r);case"base64":return S(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return A(this,e,t,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},f.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function x(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;in)&&(r=n);for(var i="",o=t;or)throw new RangeError("Trying to access beyond buffer length")}function P(e,t,r,n,i,o){if(!f.isBuffer(e))throw new TypeError(\'"buffer" argument must be a Buffer instance\');if(t>i||te.length)throw new RangeError("Index out of range")}function j(e,t,r,n){t<0&&(t=65535+t+1);for(var i=0,o=Math.min(e.length-r,2);i>>8*(n?i:1-i)}function B(e,t,r,n){t<0&&(t=4294967295+t+1);for(var i=0,o=Math.min(e.length-r,4);i>>8*(n?i:3-i)&255}function C(e,t,r,n,i,o){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function N(e,t,r,n,o){return o||C(e,0,r,4),i.write(e,t,r,n,23,4),r+4}function L(e,t,r,n,o){return o||C(e,0,r,8),i.write(e,t,r,n,52,8),r+8}f.prototype.slice=function(e,t){var r,n=this.length;if((e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t0&&(i*=256);)n+=this[e+--t]*i;return n},f.prototype.readUInt8=function(e,t){return t||R(e,1,this.length),this[e]},f.prototype.readUInt16LE=function(e,t){return t||R(e,2,this.length),this[e]|this[e+1]<<8},f.prototype.readUInt16BE=function(e,t){return t||R(e,2,this.length),this[e]<<8|this[e+1]},f.prototype.readUInt32LE=function(e,t){return t||R(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},f.prototype.readUInt32BE=function(e,t){return t||R(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},f.prototype.readIntLE=function(e,t,r){e|=0,t|=0,r||R(e,t,this.length);for(var n=this[e],i=1,o=0;++o=(i*=128)&&(n-=Math.pow(2,8*t)),n},f.prototype.readIntBE=function(e,t,r){e|=0,t|=0,r||R(e,t,this.length);for(var n=t,i=1,o=this[e+--n];n>0&&(i*=256);)o+=this[e+--n]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*t)),o},f.prototype.readInt8=function(e,t){return t||R(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},f.prototype.readInt16LE=function(e,t){t||R(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},f.prototype.readInt16BE=function(e,t){t||R(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},f.prototype.readInt32LE=function(e,t){return t||R(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},f.prototype.readInt32BE=function(e,t){return t||R(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},f.prototype.readFloatLE=function(e,t){return t||R(e,4,this.length),i.read(this,e,!0,23,4)},f.prototype.readFloatBE=function(e,t){return t||R(e,4,this.length),i.read(this,e,!1,23,4)},f.prototype.readDoubleLE=function(e,t){return t||R(e,8,this.length),i.read(this,e,!0,52,8)},f.prototype.readDoubleBE=function(e,t){return t||R(e,8,this.length),i.read(this,e,!1,52,8)},f.prototype.writeUIntLE=function(e,t,r,n){(e=+e,t|=0,r|=0,n)||P(this,e,t,r,Math.pow(2,8*r)-1,0);var i=1,o=0;for(this[t]=255&e;++o=0&&(o*=256);)this[t+i]=e/o&255;return t+r},f.prototype.writeUInt8=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,1,255,0),f.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},f.prototype.writeUInt16LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,65535,0),f.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},f.prototype.writeUInt16BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,65535,0),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},f.prototype.writeUInt32LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,4294967295,0),f.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):B(this,e,t,!0),t+4},f.prototype.writeUInt32BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,4294967295,0),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):B(this,e,t,!1),t+4},f.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);P(this,e,t,r,i-1,-i)}var o=0,a=1,s=0;for(this[t]=255&e;++o>0)-s&255;return t+r},f.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);P(this,e,t,r,i-1,-i)}var o=r-1,a=1,s=0;for(this[t+o]=255&e;--o>=0&&(a*=256);)e<0&&0===s&&0!==this[t+o+1]&&(s=1),this[t+o]=(e/a>>0)-s&255;return t+r},f.prototype.writeInt8=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,1,127,-128),f.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},f.prototype.writeInt16LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,32767,-32768),f.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},f.prototype.writeInt16BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,32767,-32768),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},f.prototype.writeInt32LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,2147483647,-2147483648),f.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):B(this,e,t,!0),t+4},f.prototype.writeInt32BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):B(this,e,t,!1),t+4},f.prototype.writeFloatLE=function(e,t,r){return N(this,e,t,!0,r)},f.prototype.writeFloatBE=function(e,t,r){return N(this,e,t,!1,r)},f.prototype.writeDoubleLE=function(e,t,r){return L(this,e,t,!0,r)},f.prototype.writeDoubleBE=function(e,t,r){return L(this,e,t,!1,r)},f.prototype.copy=function(e,t,r,n){if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t=0;--i)e[i+t]=this[i+r];else if(o<1e3||!f.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(o=t;o55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(a+1===n){(t-=3)>-1&&o.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&o.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;o.push(r)}else if(r<2048){if((t-=2)<0)break;o.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;o.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return o}function H(e){return n.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\\s+|\\s+$/g,"")}(e).replace(D,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function q(e,t,r,n){for(var i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}}).call(this,r(1))},function(e,t,r){"use strict";var n,i="object"==typeof Reflect?Reflect:null,o=i&&"function"==typeof i.apply?i.apply:function(e,t,r){return Function.prototype.apply.call(e,t,r)};n=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var a=Number.isNaN||function(e){return e!=e};function s(){s.init.call(this)}e.exports=s,e.exports.once=function(e,t){return new Promise((function(r,n){function i(r){e.removeListener(t,o),n(r)}function o(){"function"==typeof e.removeListener&&e.removeListener("error",i),r([].slice.call(arguments))}v(e,t,o,{once:!0}),"error"!==t&&function(e,t,r){"function"==typeof e.on&&v(e,"error",t,r)}(e,i,{once:!0})}))},s.EventEmitter=s,s.prototype._events=void 0,s.prototype._eventsCount=0,s.prototype._maxListeners=void 0;var f=10;function c(e){if("function"!=typeof e)throw new TypeError(\'The "listener" argument must be of type Function. Received type \'+typeof e)}function u(e){return void 0===e._maxListeners?s.defaultMaxListeners:e._maxListeners}function h(e,t,r,n){var i,o,a,s;if(c(r),void 0===(o=e._events)?(o=e._events=Object.create(null),e._eventsCount=0):(void 0!==o.newListener&&(e.emit("newListener",t,r.listener?r.listener:r),o=e._events),a=o[t]),void 0===a)a=o[t]=r,++e._eventsCount;else if("function"==typeof a?a=o[t]=n?[r,a]:[a,r]:n?a.unshift(r):a.push(r),(i=u(e))>0&&a.length>i&&!a.warned){a.warned=!0;var f=new Error("Possible EventEmitter memory leak detected. "+a.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");f.name="MaxListenersExceededWarning",f.emitter=e,f.type=t,f.count=a.length,s=f,console&&console.warn&&console.warn(s)}return e}function l(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function d(e,t,r){var n={fired:!1,wrapFn:void 0,target:e,type:t,listener:r},i=l.bind(n);return i.listener=r,n.wrapFn=i,i}function p(e,t,r){var n=e._events;if(void 0===n)return[];var i=n[t];return void 0===i?[]:"function"==typeof i?r?[i.listener||i]:[i]:r?function(e){for(var t=new Array(e.length),r=0;r0&&(a=t[0]),a instanceof Error)throw a;var s=new Error("Unhandled error."+(a?" ("+a.message+")":""));throw s.context=a,s}var f=i[e];if(void 0===f)return!1;if("function"==typeof f)o(f,this,t);else{var c=f.length,u=b(f,c);for(r=0;r=0;o--)if(r[o]===t||r[o].listener===t){a=r[o].listener,i=o;break}if(i<0)return this;0===i?r.shift():function(e,t){for(;t+1=0;n--)this.removeListener(e,t[n]);return this},s.prototype.listeners=function(e){return p(this,e,!0)},s.prototype.rawListeners=function(e){return p(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):y.call(e,t)},s.prototype.listenerCount=y,s.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]}},function(e,t,r){"use strict";(function(t){void 0===t||!t.version||0===t.version.indexOf("v0.")||0===t.version.indexOf("v1.")&&0!==t.version.indexOf("v1.8.")?e.exports={nextTick:function(e,r,n,i){if("function"!=typeof e)throw new TypeError(\'"callback" argument must be a function\');var o,a,s=arguments.length;switch(s){case 0:case 1:return t.nextTick(e);case 2:return t.nextTick((function(){e.call(null,r)}));case 3:return t.nextTick((function(){e.call(null,r,n)}));case 4:return t.nextTick((function(){e.call(null,r,n,i)}));default:for(o=new Array(s-1),a=0;a-1?n:o.nextTick;g.WritableState=v;var c=Object.create(r(4));c.inherits=r(2);var u={deprecate:r(35)},h=r(16),l=r(9).Buffer,d=(void 0!==i?i:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{}).Uint8Array||function(){};var p,y=r(17);function b(){}function v(e,t){s=s||r(0),e=e||{};var n=t instanceof s;this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var i=e.highWaterMark,c=e.writableHighWaterMark,u=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(c||0===c)?c:u,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var h=!1===e.decodeStrings;this.decodeStrings=!h,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var r=e._writableState,n=r.sync,i=r.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(r),t)!function(e,t,r,n,i){--t.pendingcb,r?(o.nextTick(i,n),o.nextTick(A,e,t),e._writableState.errorEmitted=!0,e.emit("error",n)):(i(n),e._writableState.errorEmitted=!0,e.emit("error",n),A(e,t))}(e,r,n,t,i);else{var a=E(r);a||r.corked||r.bufferProcessing||!r.bufferedRequest||_(e,r),n?f(w,e,r,a,i):w(e,r,a,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new a(this)}function g(e){if(s=s||r(0),!(p.call(g,this)||this instanceof s))return new g(e);this._writableState=new v(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),h.call(this)}function m(e,t,r,n,i,o,a){t.writelen=n,t.writecb=a,t.writing=!0,t.sync=!0,r?e._writev(i,t.onwrite):e._write(i,o,t.onwrite),t.sync=!1}function w(e,t,r,n){r||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,n(),A(e,t)}function _(e,t){t.bufferProcessing=!0;var r=t.bufferedRequest;if(e._writev&&r&&r.next){var n=t.bufferedRequestCount,i=new Array(n),o=t.corkedRequestsFree;o.entry=r;for(var s=0,f=!0;r;)i[s]=r,r.isBuf||(f=!1),r=r.next,s+=1;i.allBuffers=f,m(e,t,!0,t.length,i,"",o.finish),t.pendingcb++,t.lastBufferedRequest=null,o.next?(t.corkedRequestsFree=o.next,o.next=null):t.corkedRequestsFree=new a(t),t.bufferedRequestCount=0}else{for(;r;){var c=r.chunk,u=r.encoding,h=r.callback;if(m(e,t,!1,t.objectMode?1:c.length,c,u,h),r=r.next,t.bufferedRequestCount--,t.writing)break}null===r&&(t.lastBufferedRequest=null)}t.bufferedRequest=r,t.bufferProcessing=!1}function E(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function S(e,t){e._final((function(r){t.pendingcb--,r&&e.emit("error",r),t.prefinished=!0,e.emit("prefinish"),A(e,t)}))}function A(e,t){var r=E(t);return r&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,o.nextTick(S,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),r}c.inherits(g,h),v.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(v.prototype,"buffer",{get:u.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(p=Function.prototype[Symbol.hasInstance],Object.defineProperty(g,Symbol.hasInstance,{value:function(e){return!!p.call(this,e)||this===g&&(e&&e._writableState instanceof v)}})):p=function(e){return e instanceof this},g.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},g.prototype.write=function(e,t,r){var n,i=this._writableState,a=!1,s=!i.objectMode&&(n=e,l.isBuffer(n)||n instanceof d);return s&&!l.isBuffer(e)&&(e=function(e){return l.from(e)}(e)),"function"==typeof t&&(r=t,t=null),s?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof r&&(r=b),i.ended?function(e,t){var r=new Error("write after end");e.emit("error",r),o.nextTick(t,r)}(this,r):(s||function(e,t,r,n){var i=!0,a=!1;return null===r?a=new TypeError("May not write null values to stream"):"string"==typeof r||void 0===r||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),o.nextTick(n,a),i=!1),i}(this,i,e,r))&&(i.pendingcb++,a=function(e,t,r,n,i,o){if(!r){var a=function(e,t,r){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=l.from(t,r));return t}(t,n,i);n!==a&&(r=!0,i="buffer",n=a)}var s=t.objectMode?1:n.length;t.length+=s;var f=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(g.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),g.prototype._write=function(e,t,r){r(new Error("_write() is not implemented"))},g.prototype._writev=null,g.prototype.end=function(e,t,r){var n=this._writableState;"function"==typeof e?(r=e,e=null,t=null):"function"==typeof t&&(r=t,t=null),null!=e&&this.write(e,t),n.corked&&(n.corked=1,this.uncork()),n.ending||function(e,t,r){t.ending=!0,A(e,t),r&&(t.finished?o.nextTick(r):e.once("finish",r));t.ended=!0,e.writable=!1}(this,n,r)},Object.defineProperty(g.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),g.prototype.destroy=y.destroy,g.prototype._undestroy=y.undestroy,g.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,r(3),r(33).setImmediate,r(1))},function(e,t){var r={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==r.call(e)}},function(e,t,r){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(){/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */i=function(){return e};var e={},t=Object.prototype,r=t.hasOwnProperty,o=Object.defineProperty||function(e,t,r){e[t]=r.value},a="function"==typeof Symbol?Symbol:{},s=a.iterator||"@@iterator",f=a.asyncIterator||"@@asyncIterator",c=a.toStringTag||"@@toStringTag";function u(e,t,r){return Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{u({},"")}catch(e){u=function(e,t,r){return e[t]=r}}function h(e,t,r,n){var i=t&&t.prototype instanceof p?t:p,a=Object.create(i.prototype),s=new x(n||[]);return o(a,"_invoke",{value:S(e,r,s)}),a}function l(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}e.wrap=h;var d={};function p(){}function y(){}function b(){}var v={};u(v,s,(function(){return this}));var g=Object.getPrototypeOf,m=g&&g(g(T([])));m&&m!==t&&r.call(m,s)&&(v=m);var w=b.prototype=p.prototype=Object.create(v);function _(e){["next","throw","return"].forEach((function(t){u(e,t,(function(e){return this._invoke(t,e)}))}))}function E(e,t){var i;o(this,"_invoke",{value:function(o,a){function s(){return new t((function(i,s){!function i(o,a,s,f){var c=l(e[o],e,a);if("throw"!==c.type){var u=c.arg,h=u.value;return h&&"object"==n(h)&&r.call(h,"__await")?t.resolve(h.__await).then((function(e){i("next",e,s,f)}),(function(e){i("throw",e,s,f)})):t.resolve(h).then((function(e){u.value=e,s(u)}),(function(e){return i("throw",e,s,f)}))}f(c.arg)}(o,a,i,s)}))}return i=i?i.then(s,s):s()}})}function S(e,t,r){var n="suspendedStart";return function(i,o){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===i)throw o;return I()}for(r.method=i,r.arg=o;;){var a=r.delegate;if(a){var s=A(a,r);if(s){if(s===d)continue;return s}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var f=l(e,t,r);if("normal"===f.type){if(n=r.done?"completed":"suspendedYield",f.arg===d)continue;return{value:f.arg,done:r.done}}"throw"===f.type&&(n="completed",r.method="throw",r.arg=f.arg)}}}function A(e,t){var r=t.method,n=e.iterator[r];if(void 0===n)return t.delegate=null,"throw"===r&&e.iterator.return&&(t.method="return",t.arg=void 0,A(e,t),"throw"===t.method)||"return"!==r&&(t.method="throw",t.arg=new TypeError("The iterator does not provide a \'"+r+"\' method")),d;var i=l(n,e.iterator,t.arg);if("throw"===i.type)return t.method="throw",t.arg=i.arg,t.delegate=null,d;var o=i.arg;return o?o.done?(t[e.resultName]=o.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,d):o:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,d)}function k(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function O(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function x(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(k,this),this.reset(!0)}function T(e){if(e){var t=e[s];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,i=function t(){for(;++n=0;--i){var o=this.tryEntries[i],a=o.completion;if("root"===o.tryLoc)return n("end");if(o.tryLoc<=this.prev){var s=r.call(o,"catchLoc"),f=r.call(o,"finallyLoc");if(s&&f){if(this.prev=0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),O(r),d}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var i=n.arg;O(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:T(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),d}},e}function o(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:999999;return(new Date).getTime()+"_"+Math.floor(Math.random()*e)},t.companionStream=r,t.setMaxListeners(f),t._log=o,t}return t=u,(r=[{key:"on",value:function(e,t){return a(l(u.prototype),"on",this).call(this,e,t),this}},{key:"once",value:function(e,t){return a(l(u.prototype),"once",this).call(this,e,t),this}},{key:"addListener",value:function(e,t){return this.on(e,t)}},{key:"removeListener",value:function(e,t){return a(l(u.prototype),"removeListener",this).call(this,e,t),this}},{key:"emit",value:function(e,t){return a(l(u.prototype),"emit",this).call(this,e,t)}}])&&o(t.prototype,r),n&&o(t,n),Object.defineProperty(t,"prototype",{writable:!1}),u}(p.EventEmitter);t.default=b},function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var i=Object.getOwnPropertyDescriptor(t,r);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,i)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),i=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),i(r(26),t),i(r(27),t),i(r(28),t),i(r(29),t),i(r(42),t),i(r(43),t)},function(e,t,r){e.exports=i;var n=r(6).EventEmitter;function i(){n.call(this)}r(2)(i,n),i.Readable=r(8),i.Writable=r(38),i.Duplex=r(39),i.Transform=r(40),i.PassThrough=r(41),i.Stream=i,i.prototype.pipe=function(e,t){var r=this;function i(t){e.writable&&!1===e.write(t)&&r.pause&&r.pause()}function o(){r.readable&&r.resume&&r.resume()}r.on("data",i),e.on("drain",o),e._isStdio||t&&!1===t.end||(r.on("end",s),r.on("close",f));var a=!1;function s(){a||(a=!0,e.end())}function f(){a||(a=!0,"function"==typeof e.destroy&&e.destroy())}function c(e){if(u(),0===n.listenerCount(this,"error"))throw e}function u(){r.removeListener("data",i),e.removeListener("drain",o),r.removeListener("end",s),r.removeListener("close",f),r.removeListener("error",c),e.removeListener("error",c),r.removeListener("end",u),r.removeListener("close",u),e.removeListener("close",u)}return r.on("error",c),e.on("error",c),r.on("end",u),r.on("close",u),e.on("close",u),e.emit("pipe",r),e}},function(e,t,r){"use strict";(function(t,n){var i=r(7);e.exports=m;var o,a=r(11);m.ReadableState=g;r(6).EventEmitter;var s=function(e,t){return e.listeners(t).length},f=r(16),c=r(9).Buffer,u=(void 0!==t?t:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{}).Uint8Array||function(){};var h=Object.create(r(4));h.inherits=r(2);var l=r(30),d=void 0;d=l&&l.debuglog?l.debuglog("stream"):function(){};var p,y=r(31),b=r(17);h.inherits(m,f);var v=["error","close","destroy","pause","resume"];function g(e,t){e=e||{};var n=t instanceof(o=o||r(0));this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.readableObjectMode);var i=e.highWaterMark,a=e.readableHighWaterMark,s=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(a||0===a)?a:s,this.highWaterMark=Math.floor(this.highWaterMark),this.buffer=new y,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(p||(p=r(18).StringDecoder),this.decoder=new p(e.encoding),this.encoding=e.encoding)}function m(e){if(o=o||r(0),!(this instanceof m))return new m(e);this._readableState=new g(e,this),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),f.call(this)}function w(e,t,r,n,i){var o,a=e._readableState;null===t?(a.reading=!1,function(e,t){if(t.ended)return;if(t.decoder){var r=t.decoder.end();r&&r.length&&(t.buffer.push(r),t.length+=t.objectMode?1:r.length)}t.ended=!0,S(e)}(e,a)):(i||(o=function(e,t){var r;n=t,c.isBuffer(n)||n instanceof u||"string"==typeof t||void 0===t||e.objectMode||(r=new TypeError("Invalid non-string/buffer chunk"));var n;return r}(a,t)),o?e.emit("error",o):a.objectMode||t&&t.length>0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===c.prototype||(t=function(e){return c.from(e)}(t)),n?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):_(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!r?(t=a.decoder.write(t),a.objectMode||0!==t.length?_(e,a,t,!1):k(e,a)):_(e,a,t,!1))):n||(a.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=8388608?e=8388608:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function S(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(d("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?i.nextTick(A,e):A(e))}function A(e){d("emit readable"),e.emit("readable"),I(e)}function k(e,t){t.readingMore||(t.readingMore=!0,i.nextTick(O,e,t))}function O(e,t){for(var r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):r=function(e,t,r){var n;eo.length?o.length:e;if(a===o.length?i+=o:i+=o.slice(0,e),0===(e-=a)){a===o.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=o.slice(a));break}++n}return t.length-=n,i}(e,t):function(e,t){var r=c.allocUnsafe(e),n=t.head,i=1;n.data.copy(r),e-=n.data.length;for(;n=n.next;){var o=n.data,a=e>o.length?o.length:e;if(o.copy(r,r.length-e,0,a),0===(e-=a)){a===o.length?(++i,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=o.slice(a));break}++i}return t.length-=i,r}(e,t);return n}(e,t.buffer,t.decoder),r);var r}function R(e){var t=e._readableState;if(t.length>0)throw new Error(\'"endReadable()" called on non-empty stream\');t.endEmitted||(t.ended=!0,i.nextTick(P,t,e))}function P(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function j(e,t){for(var r=0,n=e.length;r=t.highWaterMark||t.ended))return d("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?R(this):S(this),null;if(0===(e=E(e,t))&&t.ended)return 0===t.length&&R(this),null;var n,i=t.needReadable;return d("need readable",i),(0===t.length||t.length-e0?M(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),r!==e&&t.ended&&R(this)),null!==n&&this.emit("data",n),n},m.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},m.prototype.pipe=function(e,t){var r=this,o=this._readableState;switch(o.pipesCount){case 0:o.pipes=e;break;case 1:o.pipes=[o.pipes,e];break;default:o.pipes.push(e)}o.pipesCount+=1,d("pipe count=%d opts=%j",o.pipesCount,t);var f=(!t||!1!==t.end)&&e!==n.stdout&&e!==n.stderr?u:m;function c(t,n){d("onunpipe"),t===r&&n&&!1===n.hasUnpiped&&(n.hasUnpiped=!0,d("cleanup"),e.removeListener("close",v),e.removeListener("finish",g),e.removeListener("drain",h),e.removeListener("error",b),e.removeListener("unpipe",c),r.removeListener("end",u),r.removeListener("end",m),r.removeListener("data",y),l=!0,!o.awaitDrain||e._writableState&&!e._writableState.needDrain||h())}function u(){d("onend"),e.end()}o.endEmitted?i.nextTick(f):r.once("end",f),e.on("unpipe",c);var h=function(e){return function(){var t=e._readableState;d("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&s(e,"data")&&(t.flowing=!0,I(e))}}(r);e.on("drain",h);var l=!1;var p=!1;function y(t){d("ondata"),p=!1,!1!==e.write(t)||p||((1===o.pipesCount&&o.pipes===e||o.pipesCount>1&&-1!==j(o.pipes,e))&&!l&&(d("false write response, pause",o.awaitDrain),o.awaitDrain++,p=!0),r.pause())}function b(t){d("onerror",t),m(),e.removeListener("error",b),0===s(e,"error")&&e.emit("error",t)}function v(){e.removeListener("finish",g),m()}function g(){d("onfinish"),e.removeListener("close",v),m()}function m(){d("unpipe"),r.unpipe(e)}return r.on("data",y),function(e,t,r){if("function"==typeof e.prependListener)return e.prependListener(t,r);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(r):e._events[t]=[r,e._events[t]]:e.on(t,r)}(e,"error",b),e.once("close",v),e.once("finish",g),e.emit("pipe",r),o.flowing||(d("pipe resume"),r.resume()),e},m.prototype.unpipe=function(e){var t=this._readableState,r={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,r)),this;if(!e){var n=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var o=0;o>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e){var t=this.lastTotal-this.lastNeed,r=function(e,t,r){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==r?r:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function f(e,t){if((e.length-t)%2==0){var r=e.toString("utf16le",t);if(r){var n=r.charCodeAt(r.length-1);if(n>=55296&&n<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function c(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,r)}return t}function u(e,t){var r=(e.length-t)%3;return 0===r?e.toString("base64",t):(this.lastNeed=3-r,this.lastTotal=3,1===r?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-r))}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function l(e){return e.toString(this.encoding)}function d(e){return e&&e.length?this.write(e):""}t.StringDecoder=o,o.prototype.write=function(e){if(0===e.length)return"";var t,r;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r=0)return i>0&&(e.lastNeed=i-1),i;if(--n=0)return i>0&&(e.lastNeed=i-2),i;if(--n=0)return i>0&&(2===i?i=0:e.lastNeed=i-3),i;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=r;var n=e.length-(r-this.lastNeed);return e.copy(this.lastChar,0,n),e.toString("utf8",t,n)},o.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,r){"use strict";e.exports=a;var n=r(0),i=Object.create(r(4));function o(e,t){var r=this._transformState;r.transforming=!1;var n=r.writecb;if(!n)return this.emit("error",new Error("write callback called multiple times"));r.writechunk=null,r.writecb=null,null!=t&&this.push(t),n(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length=0;--i){var o=this.tryEntries[i],a=o.completion;if("root"===o.tryLoc)return n("end");if(o.tryLoc<=this.prev){var s=r.call(o,"catchLoc"),f=r.call(o,"finallyLoc");if(s&&f){if(this.prev=0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),O(r),d}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var i=n.arg;O(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:T(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),d}},e}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&a(e,t)}function a(e,t){return(a=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function s(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=u(e);if(t){var i=u(this).constructor;r=Reflect.construct(n,arguments,i)}else r=n.apply(this,arguments);return f(this,r)}}function f(e,t){if(t&&("object"===n(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return c(e)}function c(e){if(void 0===e)throw new ReferenceError("this hasn\'t been initialised - super() hasn\'t been called");return e}function u(e){return(u=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function h(e,t){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:"hex",n=t;if("string"==typeof t&&(n=e.from(t,r)),!(n instanceof e))throw new TypeError(\'"data" argument must be an Array of Buffers\');var i=n;return i=e.from(m(n),"hex"),i=e.from(m(i),"hex"),i=e.from(n.toString("hex")+i.slice(0,4).toString("hex"),"hex"),p.a.encode(i)},decode:function(t,r){var n=e.from(p.a.decode(t)),i=n.slice(0,-4),o=i;return o=e.from(m(o),"hex"),o=e.from(m(o),"hex"),n.slice(-4).forEach((function(e,t){if(e!==o[t])throw new Error("Invalid checksum")})),r&&(i=i.toString(r)),i}},_={chainIdToBase58:function(t){var r=e.alloc(4);r.writeInt32LE("0x".concat(t.toString("16")),0);var n=e.concat([r],3);return p.a.encode(n)},base58ToChainId:function(t){return e.concat([p.a.decode(t)],4).readInt32LE(0)}},E=function(t){var r,n="";return t instanceof e?n=t.toString("hex"):(r=t,n=Array.prototype.map.call(new Uint8Array(r),(function(e){return"0".concat(e.toString(16)).slice(-2)})).join("")),n},S=function(e,t,r){var n=t-e.length+1;return new Array(n<0?0:n).join(r||"0")+e},A=function(e,t,r){var n=t-e.length+1;return e+new Array(n<0?0:n).join(r||"0")},k=function(e){if(e.indexOf("_")>-1){var t=e.split("_")[1];return w.decode(t,"hex")}return w.decode(e,"hex")},O=function(t){var r=e.from(t.replace("0x",""),"hex");return w.encode(r,"hex")},x=function(e){return e instanceof u.a||e&&e.constructor&&"BigNumber"===e.constructor.name},T=function(e){return"string"==typeof e||e&&e.constructor&&"String"===e.constructor.name},I=function(e){return"function"==typeof e},M=function(e){return null!==e&&!Array.isArray(e)&&"object"===f()(e)},R=function(e){return"boolean"==typeof e},P=function(e){try{return!!JSON.parse(e)}catch(e){return!1}},j=function(e){var t=e||0;return x(t)?t:!T(t)||0!==t.indexOf("0x")&&0!==t.indexOf("-0x")?new u.a(t.toString(10),10):new u.a(t.replace("0x",""),16)},B=function(e){var t=y.c[e?e.toLowerCase():"ether"];if(void 0===t)throw new Error("This unit doesn\'t exists, please use the one of the following units ".concat(JSON.stringify(y.c,null,2)));return new u.a(t,10)},C=function(e,t){var r=j(e).dividedBy(B(t));return x(e)?r:r.toString(10)},N=function(e,t){var r=j(e).times(B(t));return x(e)?r:r.toString(10)},L=function(e){var t=j(e).round();return t.lessThan(0)?new u.a(y.d,16).plus(t).plus(1):t},D=function(e){var t="";return e.forEach((function(e){var r=e.toString(16);r.length<=1&&(r="0".concat(r)),t+=r})),t},U=function(){},F=function(e,t,r){var n=t.split(".");n.reduce((function(e,t,i){return i===n.length-1?(e[t]=r,e):(e[t]={},e[t])}),e)},H=function(t){var r=t.data,n=t.dataType,i=t.encoding,o=void 0===i?"hex":i,a=e.from(r,o||"hex"),s=n.decode(a);return n.toObject(s,{enums:String,longs:String,bytes:String,defaults:!0,arrays:!0,objects:!0,oneofs:!0})};function q(t,r){var n=H({data:t,dataType:b.Transaction}),o=n.from,s=n.to,f=n.params,c=n.refBlockPrefix,u=n.signature,h=a()(n,["from","to","params","refBlockPrefix","signature"]),l=H({data:f,encoding:"base64",dataType:r});return l=Object(v.transform)(r,l,v.OUTPUT_TRANSFORMERS),l=Object(v.transformArrayToMap)(r,l),function(e){for(var t=1;t\n * @license MIT\n */\nvar n=r(155),i=r(156),o=r(87);function a(){return f.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function p(e,t){if(f.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var r=e.length;if(0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return H(e).length;default:if(n)return F(e).length;t=(""+t).toLowerCase(),n=!0}}function y(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return I(this,t,r);case"utf8":case"utf-8":return O(this,t,r);case"ascii":return x(this,t,r);case"latin1":case"binary":return T(this,t,r);case"base64":return k(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return M(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function b(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function v(e,t,r,n,i){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof t&&(t=f.from(t,n)),f.isBuffer(t))return 0===t.length?-1:g(e,t,r,n,i);if("number"==typeof t)return t&=255,f.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):g(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function g(e,t,r,n,i){var o,a=1,s=e.length,f=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;a=2,s/=2,f/=2,r/=2}function c(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(i){var u=-1;for(o=r;os&&(r=s-f),o=r;o>=0;o--){for(var h=!0,l=0;li&&(n=i):n=i;var o=t.length;if(o%2!=0)throw new TypeError("Invalid hex string");n>o/2&&(n=o/2);for(var a=0;a>8,i=r%256,o.push(i),o.push(n);return o}(t,e.length-r),e,r,n)}function k(e,t,r){return 0===t&&r===e.length?n.fromByteArray(e):n.fromByteArray(e.slice(t,r))}function O(e,t,r){r=Math.min(e.length,r);for(var n=[],i=t;i239?4:c>223?3:c>191?2:1;if(i+h<=r)switch(h){case 1:c<128&&(u=c);break;case 2:128==(192&(o=e[i+1]))&&(f=(31&c)<<6|63&o)>127&&(u=f);break;case 3:o=e[i+1],a=e[i+2],128==(192&o)&&128==(192&a)&&(f=(15&c)<<12|(63&o)<<6|63&a)>2047&&(f<55296||f>57343)&&(u=f);break;case 4:o=e[i+1],a=e[i+2],s=e[i+3],128==(192&o)&&128==(192&a)&&128==(192&s)&&(f=(15&c)<<18|(63&o)<<12|(63&a)<<6|63&s)>65535&&f<1114112&&(u=f)}null===u?(u=65533,h=1):u>65535&&(u-=65536,n.push(u>>>10&1023|55296),u=56320|1023&u),n.push(u),i+=h}return function(e){var t=e.length;if(t<=4096)return String.fromCharCode.apply(String,e);for(var r="",n=0;nn)&&(r=n);for(var i="",o=t;or)throw new RangeError("Trying to access beyond buffer length")}function P(e,t,r,n,i,o){if(!f.isBuffer(e))throw new TypeError(\'"buffer" argument must be a Buffer instance\');if(t>i||te.length)throw new RangeError("Index out of range")}function j(e,t,r,n){t<0&&(t=65535+t+1);for(var i=0,o=Math.min(e.length-r,2);i>>8*(n?i:1-i)}function B(e,t,r,n){t<0&&(t=4294967295+t+1);for(var i=0,o=Math.min(e.length-r,4);i>>8*(n?i:3-i)&255}function C(e,t,r,n,i,o){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function N(e,t,r,n,o){return o||C(e,0,r,4),i.write(e,t,r,n,23,4),r+4}function L(e,t,r,n,o){return o||C(e,0,r,8),i.write(e,t,r,n,52,8),r+8}t.Buffer=f,t.SlowBuffer=function(e){return+e!=e&&(e=0),f.alloc(+e)},t.INSPECT_MAX_BYTES=50,f.TYPED_ARRAY_SUPPORT=void 0!==e.TYPED_ARRAY_SUPPORT?e.TYPED_ARRAY_SUPPORT:function(){try{var e=new Uint8Array(1);return e.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===e.foo()&&"function"==typeof e.subarray&&0===e.subarray(1,1).byteLength}catch(e){return!1}}(),t.kMaxLength=a(),f.poolSize=8192,f._augment=function(e){return e.__proto__=f.prototype,e},f.from=function(e,t,r){return c(null,e,t,r)},f.TYPED_ARRAY_SUPPORT&&(f.prototype.__proto__=Uint8Array.prototype,f.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&f[Symbol.species]===f&&Object.defineProperty(f,Symbol.species,{value:null,configurable:!0})),f.alloc=function(e,t,r){return function(e,t,r,n){return u(t),t<=0?s(e,t):void 0!==r?"string"==typeof n?s(e,t).fill(r,n):s(e,t).fill(r):s(e,t)}(null,e,t,r)},f.allocUnsafe=function(e){return h(null,e)},f.allocUnsafeSlow=function(e){return h(null,e)},f.isBuffer=function(e){return!(null==e||!e._isBuffer)},f.compare=function(e,t){if(!f.isBuffer(e)||!f.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var r=e.length,n=t.length,i=0,o=Math.min(r,n);i0&&(e=this.toString("hex",0,r).match(/.{2}/g).join(" "),this.length>r&&(e+=" ... ")),""},f.prototype.compare=function(e,t,r,n,i){if(!f.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return-1;if(t>=r)return 1;if(this===e)return 0;for(var o=(i>>>=0)-(n>>>=0),a=(r>>>=0)-(t>>>=0),s=Math.min(o,a),c=this.slice(n,i),u=e.slice(t,r),h=0;hi)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return m(this,e,t,r);case"utf8":case"utf-8":return w(this,e,t,r);case"ascii":return _(this,e,t,r);case"latin1":case"binary":return E(this,e,t,r);case"base64":return S(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return A(this,e,t,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},f.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},f.prototype.slice=function(e,t){var r,n=this.length;if((e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t0&&(i*=256);)n+=this[e+--t]*i;return n},f.prototype.readUInt8=function(e,t){return t||R(e,1,this.length),this[e]},f.prototype.readUInt16LE=function(e,t){return t||R(e,2,this.length),this[e]|this[e+1]<<8},f.prototype.readUInt16BE=function(e,t){return t||R(e,2,this.length),this[e]<<8|this[e+1]},f.prototype.readUInt32LE=function(e,t){return t||R(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},f.prototype.readUInt32BE=function(e,t){return t||R(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},f.prototype.readIntLE=function(e,t,r){e|=0,t|=0,r||R(e,t,this.length);for(var n=this[e],i=1,o=0;++o=(i*=128)&&(n-=Math.pow(2,8*t)),n},f.prototype.readIntBE=function(e,t,r){e|=0,t|=0,r||R(e,t,this.length);for(var n=t,i=1,o=this[e+--n];n>0&&(i*=256);)o+=this[e+--n]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*t)),o},f.prototype.readInt8=function(e,t){return t||R(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},f.prototype.readInt16LE=function(e,t){t||R(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},f.prototype.readInt16BE=function(e,t){t||R(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},f.prototype.readInt32LE=function(e,t){return t||R(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},f.prototype.readInt32BE=function(e,t){return t||R(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},f.prototype.readFloatLE=function(e,t){return t||R(e,4,this.length),i.read(this,e,!0,23,4)},f.prototype.readFloatBE=function(e,t){return t||R(e,4,this.length),i.read(this,e,!1,23,4)},f.prototype.readDoubleLE=function(e,t){return t||R(e,8,this.length),i.read(this,e,!0,52,8)},f.prototype.readDoubleBE=function(e,t){return t||R(e,8,this.length),i.read(this,e,!1,52,8)},f.prototype.writeUIntLE=function(e,t,r,n){e=+e,t|=0,r|=0,n||P(this,e,t,r,Math.pow(2,8*r)-1,0);var i=1,o=0;for(this[t]=255&e;++o=0&&(o*=256);)this[t+i]=e/o&255;return t+r},f.prototype.writeUInt8=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,1,255,0),f.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},f.prototype.writeUInt16LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,65535,0),f.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},f.prototype.writeUInt16BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,65535,0),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},f.prototype.writeUInt32LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,4294967295,0),f.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):B(this,e,t,!0),t+4},f.prototype.writeUInt32BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,4294967295,0),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):B(this,e,t,!1),t+4},f.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);P(this,e,t,r,i-1,-i)}var o=0,a=1,s=0;for(this[t]=255&e;++o>0)-s&255;return t+r},f.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t|=0,!n){var i=Math.pow(2,8*r-1);P(this,e,t,r,i-1,-i)}var o=r-1,a=1,s=0;for(this[t+o]=255&e;--o>=0&&(a*=256);)e<0&&0===s&&0!==this[t+o+1]&&(s=1),this[t+o]=(e/a>>0)-s&255;return t+r},f.prototype.writeInt8=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,1,127,-128),f.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},f.prototype.writeInt16LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,32767,-32768),f.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},f.prototype.writeInt16BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,2,32767,-32768),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},f.prototype.writeInt32LE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,2147483647,-2147483648),f.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):B(this,e,t,!0),t+4},f.prototype.writeInt32BE=function(e,t,r){return e=+e,t|=0,r||P(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),f.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):B(this,e,t,!1),t+4},f.prototype.writeFloatLE=function(e,t,r){return N(this,e,t,!0,r)},f.prototype.writeFloatBE=function(e,t,r){return N(this,e,t,!1,r)},f.prototype.writeDoubleLE=function(e,t,r){return L(this,e,t,!0,r)},f.prototype.writeDoubleBE=function(e,t,r){return L(this,e,t,!1,r)},f.prototype.copy=function(e,t,r,n){if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t=0;--i)e[i+t]=this[i+r];else if(o<1e3||!f.TYPED_ARRAY_SUPPORT)for(i=0;i>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(o=t;o55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(a+1===n){(t-=3)>-1&&o.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&o.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;o.push(r)}else if(r<2048){if((t-=2)<0)break;o.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;o.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return o}function H(e){return n.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\\s+|\\s+$/g,"")}(e).replace(D,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function q(e,t,r,n){for(var i=0;i=t.length||i>=e.length);++i)t[i+r]=e[i];return i}}).call(this,r(10))},function(e,t){e.exports=function(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}},function(e,t,r){"use strict";r.r(t),function(e){r.d(t,"transform",(function(){return l})),r.d(t,"transformMapToArray",(function(){return d})),r.d(t,"transformArrayToMap",(function(){return p})),r.d(t,"INPUT_TRANSFORMERS",(function(){return y})),r.d(t,"encodeAddress",(function(){return b})),r.d(t,"OUTPUT_TRANSFORMERS",(function(){return v}));var n=r(4),i=r.n(n),o=r(1),a=r(32);function s(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function f(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:[],n=(e.fieldsArray||[]).length,o=t;if(0===n)return t;var a=!0,s=!1,c=void 0;try{for(var u,h=r[Symbol.iterator]();!(a=(u=h.next()).done);a=!0){var d=u.value,p=d.filter,y=d.transformer;if(p(e)&&t)return y(t)}}catch(e){s=!0,c=e}finally{try{a||null==h.return||h.return()}finally{if(s)throw c}}return Object.keys(e.fields).forEach((function(n){var a=e.fields[n],s=a.rule,c=a.name,u=a.resolvedType;if(u)if(s&&"repeated"===s){var h=t[c];h&&Array.isArray(h)&&(h=h.filter((function(e){return null!=e})).map((function(e){return l(u,e,r)}))),o=f({},o,i()({},c,h))}else o=f({},o,i()({},c,null!==t[c]&&void 0!==t[c]?l(u,t[c]||{},r):t[c]))})),o}function d(e,t){var r=e.fieldsArray?e.fieldsArray.length:0,n=t;if(!t)return t;if(0===r||1===r&&!e.fieldsArray[0].resolvedType)return t;if(u(e)||h(e))return t;var o=e.fields,a=e.options,s=void 0===a?{}:a;return 2===r&&o.value&&o.key&&!0===s.map_entry?Object.keys(t||{}).map((function(e){return{key:e,value:t[e]}})):(Object.keys(e.fields).forEach((function(r){var o=e.fields[r],a=o.name,s=o.resolvedType;if(s)if(t[a]&&Array.isArray(t[a])){var c=t[a];c=c.map((function(e){return d(s,e)})),n=f({},n,i()({},a,c))}else n=f({},n,i()({},a,d(s,t[a])))})),n)}function p(e,t){var r=(e.fieldsArray||[]).length,n=t;if(0===r||1===r&&!e.fieldsArray[0].resolvedType)return t;if(u(e)||h(e))return t;var o=e.fields,a=e.options,s=void 0===a?{}:a;return 2===r&&o.value&&o.key&&!0===s.map_entry?t.reduce((function(e,t){return f({},e,i()({},t.key,t.value))}),{}):(Object.keys(o).forEach((function(e){var r=o[e],a=r.name,s=r.resolvedType;if(s&&null!=t)if(t[a]&&Array.isArray(t[a])){var c=s.fieldsArray,u=s.fields,h=s.options,l=void 0===h?{}:h;if(2===c.length&&u.value&&u.key&&!0===l.map_entry)n=f({},n,i()({},a,t[a].reduce((function(e,t){return f({},e,i()({},t.key,t.value))}),{})));else{var d=t[a];d=d.map((function(e){return p(s,e)})),n=f({},n,i()({},a,d))}}else n=f({},n,i()({},a,p(s,t[a])))})),n)}var y=[{filter:u,transformer:function(t){var r=t;return"string"==typeof t&&(r={value:e.from(Object(o.decodeAddressRep)(Object(a.a)(t)),"hex")}),Array.isArray(t)&&(r=t.map((function(t){return{value:e.from(Object(o.decodeAddressRep)(Object(a.a)(t)),"hex")}}))),r}},{filter:h,transformer:function(t){var r=t;return"string"==typeof t&&(r={value:e.from(t.replace("0x",""),"hex")}),Array.isArray(t)&&(r=t.map((function(t){return{value:e.from(t.replace("0x",""),"hex")}}))),r}}];function b(t){var r=e.from(t,"base64");return o.base58.encode(r)}var v=[{filter:u,transformer:function(e){var t=e;return Array.isArray(t)&&(t=t.map((function(e){return b(e.value)}))),"string"!=typeof t&&(t=b(t.value)),t}},{filter:h,transformer:function(t){var r=t;return Array.isArray(r)&&(r=r.map((function(t){return e.from(t.value,"base64").toString("hex")}))),"string"!=typeof r&&(r=e.from(r.value,"base64").toString("hex")),r}}]}.call(this,r(3).Buffer)},function(e,t,r){(function(e){!function(e,t){"use strict";function n(e,t){if(!e)throw new Error(t||"Assertion failed")}function i(e,t){e.super_=t;var r=function(){};r.prototype=t.prototype,e.prototype=new r,e.prototype.constructor=e}function o(e,t,r){if(o.isBN(e))return e;this.negative=0,this.words=null,this.length=0,this.red=null,null!==e&&("le"!==t&&"be"!==t||(r=t,t=10),this._init(e||0,t||10,r||"be"))}var a;"object"==typeof e?e.exports=o:t.BN=o,o.BN=o,o.wordSize=26;try{a=r(179).Buffer}catch(e){}function s(e,t,r){for(var n=0,i=Math.min(e.length,r),o=t;o=49&&a<=54?a-49+10:a>=17&&a<=22?a-17+10:15&a}return n}function f(e,t,r,n){for(var i=0,o=Math.min(e.length,r),a=t;a=49?s-49+10:s>=17?s-17+10:s}return i}o.isBN=function(e){return e instanceof o||null!==e&&"object"==typeof e&&e.constructor.wordSize===o.wordSize&&Array.isArray(e.words)},o.max=function(e,t){return e.cmp(t)>0?e:t},o.min=function(e,t){return e.cmp(t)<0?e:t},o.prototype._init=function(e,t,r){if("number"==typeof e)return this._initNumber(e,t,r);if("object"==typeof e)return this._initArray(e,t,r);"hex"===t&&(t=16),n(t===(0|t)&&t>=2&&t<=36);var i=0;"-"===(e=e.toString().replace(/\\s+/g,""))[0]&&i++,16===t?this._parseHex(e,i):this._parseBase(e,t,i),"-"===e[0]&&(this.negative=1),this.strip(),"le"===r&&this._initArray(this.toArray(),t,r)},o.prototype._initNumber=function(e,t,r){e<0&&(this.negative=1,e=-e),e<67108864?(this.words=[67108863&e],this.length=1):e<4503599627370496?(this.words=[67108863&e,e/67108864&67108863],this.length=2):(n(e<9007199254740992),this.words=[67108863&e,e/67108864&67108863,1],this.length=3),"le"===r&&this._initArray(this.toArray(),t,r)},o.prototype._initArray=function(e,t,r){if(n("number"==typeof e.length),e.length<=0)return this.words=[0],this.length=1,this;this.length=Math.ceil(e.length/3),this.words=new Array(this.length);for(var i=0;i=0;i-=3)a=e[i]|e[i-1]<<8|e[i-2]<<16,this.words[o]|=a<>>26-s&67108863,(s+=24)>=26&&(s-=26,o++);else if("le"===r)for(i=0,o=0;i>>26-s&67108863,(s+=24)>=26&&(s-=26,o++);return this.strip()},o.prototype._parseHex=function(e,t){this.length=Math.ceil((e.length-t)/6),this.words=new Array(this.length);for(var r=0;r=t;r-=6)i=s(e,r,r+6),this.words[n]|=i<>>26-o&4194303,(o+=24)>=26&&(o-=26,n++);r+6!==t&&(i=s(e,t,r+6),this.words[n]|=i<>>26-o&4194303),this.strip()},o.prototype._parseBase=function(e,t,r){this.words=[0],this.length=1;for(var n=0,i=1;i<=67108863;i*=t)n++;n--,i=i/t|0;for(var o=e.length-r,a=o%n,s=Math.min(o,o-a)+r,c=0,u=r;u1&&0===this.words[this.length-1];)this.length--;return this._normSign()},o.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},o.prototype.inspect=function(){return(this.red?""};var c=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],u=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],h=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function l(e,t,r){r.negative=t.negative^e.negative;var n=e.length+t.length|0;r.length=n,n=n-1|0;var i=0|e.words[0],o=0|t.words[0],a=i*o,s=67108863&a,f=a/67108864|0;r.words[0]=s;for(var c=1;c>>26,h=67108863&f,l=Math.min(c,t.length-1),d=Math.max(0,c-e.length+1);d<=l;d++){var p=c-d|0;u+=(a=(i=0|e.words[p])*(o=0|t.words[d])+h)/67108864|0,h=67108863&a}r.words[c]=0|h,f=0|u}return 0!==f?r.words[c]=0|f:r.length--,r.strip()}o.prototype.toString=function(e,t){var r;if(t=0|t||1,16===(e=e||10)||"hex"===e){r="";for(var i=0,o=0,a=0;a>>24-i&16777215)||a!==this.length-1?c[6-f.length]+f+r:f+r,(i+=2)>=26&&(i-=26,a--)}for(0!==o&&(r=o.toString(16)+r);r.length%t!=0;)r="0"+r;return 0!==this.negative&&(r="-"+r),r}if(e===(0|e)&&e>=2&&e<=36){var l=u[e],d=h[e];r="";var p=this.clone();for(p.negative=0;!p.isZero();){var y=p.modn(d).toString(e);r=(p=p.idivn(d)).isZero()?y+r:c[l-y.length]+y+r}for(this.isZero()&&(r="0"+r);r.length%t!=0;)r="0"+r;return 0!==this.negative&&(r="-"+r),r}n(!1,"Base should be between 2 and 36")},o.prototype.toNumber=function(){var e=this.words[0];return 2===this.length?e+=67108864*this.words[1]:3===this.length&&1===this.words[2]?e+=4503599627370496+67108864*this.words[1]:this.length>2&&n(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-e:e},o.prototype.toJSON=function(){return this.toString(16)},o.prototype.toBuffer=function(e,t){return n(void 0!==a),this.toArrayLike(a,e,t)},o.prototype.toArray=function(e,t){return this.toArrayLike(Array,e,t)},o.prototype.toArrayLike=function(e,t,r){var i=this.byteLength(),o=r||Math.max(1,i);n(i<=o,"byte array longer than desired length"),n(o>0,"Requested array length <= 0"),this.strip();var a,s,f="le"===t,c=new e(o),u=this.clone();if(f){for(s=0;!u.isZero();s++)a=u.andln(255),u.iushrn(8),c[s]=a;for(;s=4096&&(r+=13,t>>>=13),t>=64&&(r+=7,t>>>=7),t>=8&&(r+=4,t>>>=4),t>=2&&(r+=2,t>>>=2),r+t},o.prototype._zeroBits=function(e){if(0===e)return 26;var t=e,r=0;return 0==(8191&t)&&(r+=13,t>>>=13),0==(127&t)&&(r+=7,t>>>=7),0==(15&t)&&(r+=4,t>>>=4),0==(3&t)&&(r+=2,t>>>=2),0==(1&t)&&r++,r},o.prototype.bitLength=function(){var e=this.words[this.length-1],t=this._countBits(e);return 26*(this.length-1)+t},o.prototype.zeroBits=function(){if(this.isZero())return 0;for(var e=0,t=0;te.length?this.clone().ior(e):e.clone().ior(this)},o.prototype.uor=function(e){return this.length>e.length?this.clone().iuor(e):e.clone().iuor(this)},o.prototype.iuand=function(e){var t;t=this.length>e.length?e:this;for(var r=0;re.length?this.clone().iand(e):e.clone().iand(this)},o.prototype.uand=function(e){return this.length>e.length?this.clone().iuand(e):e.clone().iuand(this)},o.prototype.iuxor=function(e){var t,r;this.length>e.length?(t=this,r=e):(t=e,r=this);for(var n=0;ne.length?this.clone().ixor(e):e.clone().ixor(this)},o.prototype.uxor=function(e){return this.length>e.length?this.clone().iuxor(e):e.clone().iuxor(this)},o.prototype.inotn=function(e){n("number"==typeof e&&e>=0);var t=0|Math.ceil(e/26),r=e%26;this._expand(t),r>0&&t--;for(var i=0;i0&&(this.words[i]=~this.words[i]&67108863>>26-r),this.strip()},o.prototype.notn=function(e){return this.clone().inotn(e)},o.prototype.setn=function(e,t){n("number"==typeof e&&e>=0);var r=e/26|0,i=e%26;return this._expand(r+1),this.words[r]=t?this.words[r]|1<e.length?(r=this,n=e):(r=e,n=this);for(var i=0,o=0;o>>26;for(;0!==i&&o>>26;if(this.length=r.length,0!==i)this.words[this.length]=i,this.length++;else if(r!==this)for(;oe.length?this.clone().iadd(e):e.clone().iadd(this)},o.prototype.isub=function(e){if(0!==e.negative){e.negative=0;var t=this.iadd(e);return e.negative=1,t._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(e),this.negative=1,this._normSign();var r,n,i=this.cmp(e);if(0===i)return this.negative=0,this.length=1,this.words[0]=0,this;i>0?(r=this,n=e):(r=e,n=this);for(var o=0,a=0;a>26,this.words[a]=67108863&t;for(;0!==o&&a>26,this.words[a]=67108863&t;if(0===o&&a>>13,d=0|a[1],p=8191&d,y=d>>>13,b=0|a[2],v=8191&b,g=b>>>13,m=0|a[3],w=8191&m,_=m>>>13,E=0|a[4],S=8191&E,A=E>>>13,k=0|a[5],O=8191&k,x=k>>>13,T=0|a[6],I=8191&T,M=T>>>13,R=0|a[7],P=8191&R,j=R>>>13,B=0|a[8],C=8191&B,N=B>>>13,L=0|a[9],D=8191&L,U=L>>>13,F=0|s[0],H=8191&F,q=F>>>13,z=0|s[1],K=8191&z,V=z>>>13,Y=0|s[2],G=8191&Y,W=Y>>>13,X=0|s[3],J=8191&X,$=X>>>13,Z=0|s[4],Q=8191&Z,ee=Z>>>13,te=0|s[5],re=8191&te,ne=te>>>13,ie=0|s[6],oe=8191&ie,ae=ie>>>13,se=0|s[7],fe=8191&se,ce=se>>>13,ue=0|s[8],he=8191&ue,le=ue>>>13,de=0|s[9],pe=8191&de,ye=de>>>13;r.negative=e.negative^t.negative,r.length=19;var be=(c+(n=Math.imul(h,H))|0)+((8191&(i=(i=Math.imul(h,q))+Math.imul(l,H)|0))<<13)|0;c=((o=Math.imul(l,q))+(i>>>13)|0)+(be>>>26)|0,be&=67108863,n=Math.imul(p,H),i=(i=Math.imul(p,q))+Math.imul(y,H)|0,o=Math.imul(y,q);var ve=(c+(n=n+Math.imul(h,K)|0)|0)+((8191&(i=(i=i+Math.imul(h,V)|0)+Math.imul(l,K)|0))<<13)|0;c=((o=o+Math.imul(l,V)|0)+(i>>>13)|0)+(ve>>>26)|0,ve&=67108863,n=Math.imul(v,H),i=(i=Math.imul(v,q))+Math.imul(g,H)|0,o=Math.imul(g,q),n=n+Math.imul(p,K)|0,i=(i=i+Math.imul(p,V)|0)+Math.imul(y,K)|0,o=o+Math.imul(y,V)|0;var ge=(c+(n=n+Math.imul(h,G)|0)|0)+((8191&(i=(i=i+Math.imul(h,W)|0)+Math.imul(l,G)|0))<<13)|0;c=((o=o+Math.imul(l,W)|0)+(i>>>13)|0)+(ge>>>26)|0,ge&=67108863,n=Math.imul(w,H),i=(i=Math.imul(w,q))+Math.imul(_,H)|0,o=Math.imul(_,q),n=n+Math.imul(v,K)|0,i=(i=i+Math.imul(v,V)|0)+Math.imul(g,K)|0,o=o+Math.imul(g,V)|0,n=n+Math.imul(p,G)|0,i=(i=i+Math.imul(p,W)|0)+Math.imul(y,G)|0,o=o+Math.imul(y,W)|0;var me=(c+(n=n+Math.imul(h,J)|0)|0)+((8191&(i=(i=i+Math.imul(h,$)|0)+Math.imul(l,J)|0))<<13)|0;c=((o=o+Math.imul(l,$)|0)+(i>>>13)|0)+(me>>>26)|0,me&=67108863,n=Math.imul(S,H),i=(i=Math.imul(S,q))+Math.imul(A,H)|0,o=Math.imul(A,q),n=n+Math.imul(w,K)|0,i=(i=i+Math.imul(w,V)|0)+Math.imul(_,K)|0,o=o+Math.imul(_,V)|0,n=n+Math.imul(v,G)|0,i=(i=i+Math.imul(v,W)|0)+Math.imul(g,G)|0,o=o+Math.imul(g,W)|0,n=n+Math.imul(p,J)|0,i=(i=i+Math.imul(p,$)|0)+Math.imul(y,J)|0,o=o+Math.imul(y,$)|0;var we=(c+(n=n+Math.imul(h,Q)|0)|0)+((8191&(i=(i=i+Math.imul(h,ee)|0)+Math.imul(l,Q)|0))<<13)|0;c=((o=o+Math.imul(l,ee)|0)+(i>>>13)|0)+(we>>>26)|0,we&=67108863,n=Math.imul(O,H),i=(i=Math.imul(O,q))+Math.imul(x,H)|0,o=Math.imul(x,q),n=n+Math.imul(S,K)|0,i=(i=i+Math.imul(S,V)|0)+Math.imul(A,K)|0,o=o+Math.imul(A,V)|0,n=n+Math.imul(w,G)|0,i=(i=i+Math.imul(w,W)|0)+Math.imul(_,G)|0,o=o+Math.imul(_,W)|0,n=n+Math.imul(v,J)|0,i=(i=i+Math.imul(v,$)|0)+Math.imul(g,J)|0,o=o+Math.imul(g,$)|0,n=n+Math.imul(p,Q)|0,i=(i=i+Math.imul(p,ee)|0)+Math.imul(y,Q)|0,o=o+Math.imul(y,ee)|0;var _e=(c+(n=n+Math.imul(h,re)|0)|0)+((8191&(i=(i=i+Math.imul(h,ne)|0)+Math.imul(l,re)|0))<<13)|0;c=((o=o+Math.imul(l,ne)|0)+(i>>>13)|0)+(_e>>>26)|0,_e&=67108863,n=Math.imul(I,H),i=(i=Math.imul(I,q))+Math.imul(M,H)|0,o=Math.imul(M,q),n=n+Math.imul(O,K)|0,i=(i=i+Math.imul(O,V)|0)+Math.imul(x,K)|0,o=o+Math.imul(x,V)|0,n=n+Math.imul(S,G)|0,i=(i=i+Math.imul(S,W)|0)+Math.imul(A,G)|0,o=o+Math.imul(A,W)|0,n=n+Math.imul(w,J)|0,i=(i=i+Math.imul(w,$)|0)+Math.imul(_,J)|0,o=o+Math.imul(_,$)|0,n=n+Math.imul(v,Q)|0,i=(i=i+Math.imul(v,ee)|0)+Math.imul(g,Q)|0,o=o+Math.imul(g,ee)|0,n=n+Math.imul(p,re)|0,i=(i=i+Math.imul(p,ne)|0)+Math.imul(y,re)|0,o=o+Math.imul(y,ne)|0;var Ee=(c+(n=n+Math.imul(h,oe)|0)|0)+((8191&(i=(i=i+Math.imul(h,ae)|0)+Math.imul(l,oe)|0))<<13)|0;c=((o=o+Math.imul(l,ae)|0)+(i>>>13)|0)+(Ee>>>26)|0,Ee&=67108863,n=Math.imul(P,H),i=(i=Math.imul(P,q))+Math.imul(j,H)|0,o=Math.imul(j,q),n=n+Math.imul(I,K)|0,i=(i=i+Math.imul(I,V)|0)+Math.imul(M,K)|0,o=o+Math.imul(M,V)|0,n=n+Math.imul(O,G)|0,i=(i=i+Math.imul(O,W)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,W)|0,n=n+Math.imul(S,J)|0,i=(i=i+Math.imul(S,$)|0)+Math.imul(A,J)|0,o=o+Math.imul(A,$)|0,n=n+Math.imul(w,Q)|0,i=(i=i+Math.imul(w,ee)|0)+Math.imul(_,Q)|0,o=o+Math.imul(_,ee)|0,n=n+Math.imul(v,re)|0,i=(i=i+Math.imul(v,ne)|0)+Math.imul(g,re)|0,o=o+Math.imul(g,ne)|0,n=n+Math.imul(p,oe)|0,i=(i=i+Math.imul(p,ae)|0)+Math.imul(y,oe)|0,o=o+Math.imul(y,ae)|0;var Se=(c+(n=n+Math.imul(h,fe)|0)|0)+((8191&(i=(i=i+Math.imul(h,ce)|0)+Math.imul(l,fe)|0))<<13)|0;c=((o=o+Math.imul(l,ce)|0)+(i>>>13)|0)+(Se>>>26)|0,Se&=67108863,n=Math.imul(C,H),i=(i=Math.imul(C,q))+Math.imul(N,H)|0,o=Math.imul(N,q),n=n+Math.imul(P,K)|0,i=(i=i+Math.imul(P,V)|0)+Math.imul(j,K)|0,o=o+Math.imul(j,V)|0,n=n+Math.imul(I,G)|0,i=(i=i+Math.imul(I,W)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,W)|0,n=n+Math.imul(O,J)|0,i=(i=i+Math.imul(O,$)|0)+Math.imul(x,J)|0,o=o+Math.imul(x,$)|0,n=n+Math.imul(S,Q)|0,i=(i=i+Math.imul(S,ee)|0)+Math.imul(A,Q)|0,o=o+Math.imul(A,ee)|0,n=n+Math.imul(w,re)|0,i=(i=i+Math.imul(w,ne)|0)+Math.imul(_,re)|0,o=o+Math.imul(_,ne)|0,n=n+Math.imul(v,oe)|0,i=(i=i+Math.imul(v,ae)|0)+Math.imul(g,oe)|0,o=o+Math.imul(g,ae)|0,n=n+Math.imul(p,fe)|0,i=(i=i+Math.imul(p,ce)|0)+Math.imul(y,fe)|0,o=o+Math.imul(y,ce)|0;var Ae=(c+(n=n+Math.imul(h,he)|0)|0)+((8191&(i=(i=i+Math.imul(h,le)|0)+Math.imul(l,he)|0))<<13)|0;c=((o=o+Math.imul(l,le)|0)+(i>>>13)|0)+(Ae>>>26)|0,Ae&=67108863,n=Math.imul(D,H),i=(i=Math.imul(D,q))+Math.imul(U,H)|0,o=Math.imul(U,q),n=n+Math.imul(C,K)|0,i=(i=i+Math.imul(C,V)|0)+Math.imul(N,K)|0,o=o+Math.imul(N,V)|0,n=n+Math.imul(P,G)|0,i=(i=i+Math.imul(P,W)|0)+Math.imul(j,G)|0,o=o+Math.imul(j,W)|0,n=n+Math.imul(I,J)|0,i=(i=i+Math.imul(I,$)|0)+Math.imul(M,J)|0,o=o+Math.imul(M,$)|0,n=n+Math.imul(O,Q)|0,i=(i=i+Math.imul(O,ee)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,ee)|0,n=n+Math.imul(S,re)|0,i=(i=i+Math.imul(S,ne)|0)+Math.imul(A,re)|0,o=o+Math.imul(A,ne)|0,n=n+Math.imul(w,oe)|0,i=(i=i+Math.imul(w,ae)|0)+Math.imul(_,oe)|0,o=o+Math.imul(_,ae)|0,n=n+Math.imul(v,fe)|0,i=(i=i+Math.imul(v,ce)|0)+Math.imul(g,fe)|0,o=o+Math.imul(g,ce)|0,n=n+Math.imul(p,he)|0,i=(i=i+Math.imul(p,le)|0)+Math.imul(y,he)|0,o=o+Math.imul(y,le)|0;var ke=(c+(n=n+Math.imul(h,pe)|0)|0)+((8191&(i=(i=i+Math.imul(h,ye)|0)+Math.imul(l,pe)|0))<<13)|0;c=((o=o+Math.imul(l,ye)|0)+(i>>>13)|0)+(ke>>>26)|0,ke&=67108863,n=Math.imul(D,K),i=(i=Math.imul(D,V))+Math.imul(U,K)|0,o=Math.imul(U,V),n=n+Math.imul(C,G)|0,i=(i=i+Math.imul(C,W)|0)+Math.imul(N,G)|0,o=o+Math.imul(N,W)|0,n=n+Math.imul(P,J)|0,i=(i=i+Math.imul(P,$)|0)+Math.imul(j,J)|0,o=o+Math.imul(j,$)|0,n=n+Math.imul(I,Q)|0,i=(i=i+Math.imul(I,ee)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,ee)|0,n=n+Math.imul(O,re)|0,i=(i=i+Math.imul(O,ne)|0)+Math.imul(x,re)|0,o=o+Math.imul(x,ne)|0,n=n+Math.imul(S,oe)|0,i=(i=i+Math.imul(S,ae)|0)+Math.imul(A,oe)|0,o=o+Math.imul(A,ae)|0,n=n+Math.imul(w,fe)|0,i=(i=i+Math.imul(w,ce)|0)+Math.imul(_,fe)|0,o=o+Math.imul(_,ce)|0,n=n+Math.imul(v,he)|0,i=(i=i+Math.imul(v,le)|0)+Math.imul(g,he)|0,o=o+Math.imul(g,le)|0;var Oe=(c+(n=n+Math.imul(p,pe)|0)|0)+((8191&(i=(i=i+Math.imul(p,ye)|0)+Math.imul(y,pe)|0))<<13)|0;c=((o=o+Math.imul(y,ye)|0)+(i>>>13)|0)+(Oe>>>26)|0,Oe&=67108863,n=Math.imul(D,G),i=(i=Math.imul(D,W))+Math.imul(U,G)|0,o=Math.imul(U,W),n=n+Math.imul(C,J)|0,i=(i=i+Math.imul(C,$)|0)+Math.imul(N,J)|0,o=o+Math.imul(N,$)|0,n=n+Math.imul(P,Q)|0,i=(i=i+Math.imul(P,ee)|0)+Math.imul(j,Q)|0,o=o+Math.imul(j,ee)|0,n=n+Math.imul(I,re)|0,i=(i=i+Math.imul(I,ne)|0)+Math.imul(M,re)|0,o=o+Math.imul(M,ne)|0,n=n+Math.imul(O,oe)|0,i=(i=i+Math.imul(O,ae)|0)+Math.imul(x,oe)|0,o=o+Math.imul(x,ae)|0,n=n+Math.imul(S,fe)|0,i=(i=i+Math.imul(S,ce)|0)+Math.imul(A,fe)|0,o=o+Math.imul(A,ce)|0,n=n+Math.imul(w,he)|0,i=(i=i+Math.imul(w,le)|0)+Math.imul(_,he)|0,o=o+Math.imul(_,le)|0;var xe=(c+(n=n+Math.imul(v,pe)|0)|0)+((8191&(i=(i=i+Math.imul(v,ye)|0)+Math.imul(g,pe)|0))<<13)|0;c=((o=o+Math.imul(g,ye)|0)+(i>>>13)|0)+(xe>>>26)|0,xe&=67108863,n=Math.imul(D,J),i=(i=Math.imul(D,$))+Math.imul(U,J)|0,o=Math.imul(U,$),n=n+Math.imul(C,Q)|0,i=(i=i+Math.imul(C,ee)|0)+Math.imul(N,Q)|0,o=o+Math.imul(N,ee)|0,n=n+Math.imul(P,re)|0,i=(i=i+Math.imul(P,ne)|0)+Math.imul(j,re)|0,o=o+Math.imul(j,ne)|0,n=n+Math.imul(I,oe)|0,i=(i=i+Math.imul(I,ae)|0)+Math.imul(M,oe)|0,o=o+Math.imul(M,ae)|0,n=n+Math.imul(O,fe)|0,i=(i=i+Math.imul(O,ce)|0)+Math.imul(x,fe)|0,o=o+Math.imul(x,ce)|0,n=n+Math.imul(S,he)|0,i=(i=i+Math.imul(S,le)|0)+Math.imul(A,he)|0,o=o+Math.imul(A,le)|0;var Te=(c+(n=n+Math.imul(w,pe)|0)|0)+((8191&(i=(i=i+Math.imul(w,ye)|0)+Math.imul(_,pe)|0))<<13)|0;c=((o=o+Math.imul(_,ye)|0)+(i>>>13)|0)+(Te>>>26)|0,Te&=67108863,n=Math.imul(D,Q),i=(i=Math.imul(D,ee))+Math.imul(U,Q)|0,o=Math.imul(U,ee),n=n+Math.imul(C,re)|0,i=(i=i+Math.imul(C,ne)|0)+Math.imul(N,re)|0,o=o+Math.imul(N,ne)|0,n=n+Math.imul(P,oe)|0,i=(i=i+Math.imul(P,ae)|0)+Math.imul(j,oe)|0,o=o+Math.imul(j,ae)|0,n=n+Math.imul(I,fe)|0,i=(i=i+Math.imul(I,ce)|0)+Math.imul(M,fe)|0,o=o+Math.imul(M,ce)|0,n=n+Math.imul(O,he)|0,i=(i=i+Math.imul(O,le)|0)+Math.imul(x,he)|0,o=o+Math.imul(x,le)|0;var Ie=(c+(n=n+Math.imul(S,pe)|0)|0)+((8191&(i=(i=i+Math.imul(S,ye)|0)+Math.imul(A,pe)|0))<<13)|0;c=((o=o+Math.imul(A,ye)|0)+(i>>>13)|0)+(Ie>>>26)|0,Ie&=67108863,n=Math.imul(D,re),i=(i=Math.imul(D,ne))+Math.imul(U,re)|0,o=Math.imul(U,ne),n=n+Math.imul(C,oe)|0,i=(i=i+Math.imul(C,ae)|0)+Math.imul(N,oe)|0,o=o+Math.imul(N,ae)|0,n=n+Math.imul(P,fe)|0,i=(i=i+Math.imul(P,ce)|0)+Math.imul(j,fe)|0,o=o+Math.imul(j,ce)|0,n=n+Math.imul(I,he)|0,i=(i=i+Math.imul(I,le)|0)+Math.imul(M,he)|0,o=o+Math.imul(M,le)|0;var Me=(c+(n=n+Math.imul(O,pe)|0)|0)+((8191&(i=(i=i+Math.imul(O,ye)|0)+Math.imul(x,pe)|0))<<13)|0;c=((o=o+Math.imul(x,ye)|0)+(i>>>13)|0)+(Me>>>26)|0,Me&=67108863,n=Math.imul(D,oe),i=(i=Math.imul(D,ae))+Math.imul(U,oe)|0,o=Math.imul(U,ae),n=n+Math.imul(C,fe)|0,i=(i=i+Math.imul(C,ce)|0)+Math.imul(N,fe)|0,o=o+Math.imul(N,ce)|0,n=n+Math.imul(P,he)|0,i=(i=i+Math.imul(P,le)|0)+Math.imul(j,he)|0,o=o+Math.imul(j,le)|0;var Re=(c+(n=n+Math.imul(I,pe)|0)|0)+((8191&(i=(i=i+Math.imul(I,ye)|0)+Math.imul(M,pe)|0))<<13)|0;c=((o=o+Math.imul(M,ye)|0)+(i>>>13)|0)+(Re>>>26)|0,Re&=67108863,n=Math.imul(D,fe),i=(i=Math.imul(D,ce))+Math.imul(U,fe)|0,o=Math.imul(U,ce),n=n+Math.imul(C,he)|0,i=(i=i+Math.imul(C,le)|0)+Math.imul(N,he)|0,o=o+Math.imul(N,le)|0;var Pe=(c+(n=n+Math.imul(P,pe)|0)|0)+((8191&(i=(i=i+Math.imul(P,ye)|0)+Math.imul(j,pe)|0))<<13)|0;c=((o=o+Math.imul(j,ye)|0)+(i>>>13)|0)+(Pe>>>26)|0,Pe&=67108863,n=Math.imul(D,he),i=(i=Math.imul(D,le))+Math.imul(U,he)|0,o=Math.imul(U,le);var je=(c+(n=n+Math.imul(C,pe)|0)|0)+((8191&(i=(i=i+Math.imul(C,ye)|0)+Math.imul(N,pe)|0))<<13)|0;c=((o=o+Math.imul(N,ye)|0)+(i>>>13)|0)+(je>>>26)|0,je&=67108863;var Be=(c+(n=Math.imul(D,pe))|0)+((8191&(i=(i=Math.imul(D,ye))+Math.imul(U,pe)|0))<<13)|0;return c=((o=Math.imul(U,ye))+(i>>>13)|0)+(Be>>>26)|0,Be&=67108863,f[0]=be,f[1]=ve,f[2]=ge,f[3]=me,f[4]=we,f[5]=_e,f[6]=Ee,f[7]=Se,f[8]=Ae,f[9]=ke,f[10]=Oe,f[11]=xe,f[12]=Te,f[13]=Ie,f[14]=Me,f[15]=Re,f[16]=Pe,f[17]=je,f[18]=Be,0!==c&&(f[19]=c,r.length++),r};function p(e,t,r){return(new y).mulp(e,t,r)}function y(e,t){this.x=e,this.y=t}Math.imul||(d=l),o.prototype.mulTo=function(e,t){var r=this.length+e.length;return 10===this.length&&10===e.length?d(this,e,t):r<63?l(this,e,t):r<1024?function(e,t,r){r.negative=t.negative^e.negative,r.length=e.length+t.length;for(var n=0,i=0,o=0;o>>26)|0)>>>26,a&=67108863}r.words[o]=s,n=a,a=i}return 0!==n?r.words[o]=n:r.length--,r.strip()}(this,e,t):p(this,e,t)},y.prototype.makeRBT=function(e){for(var t=new Array(e),r=o.prototype._countBits(e)-1,n=0;n>=1;return n},y.prototype.permute=function(e,t,r,n,i,o){for(var a=0;a>>=1)i++;return 1<>>=13,r[2*a+1]=8191&o,o>>>=13;for(a=2*t;a>=26,t+=i/67108864|0,t+=o>>>26,this.words[r]=67108863&o}return 0!==t&&(this.words[r]=t,this.length++),this},o.prototype.muln=function(e){return this.clone().imuln(e)},o.prototype.sqr=function(){return this.mul(this)},o.prototype.isqr=function(){return this.imul(this.clone())},o.prototype.pow=function(e){var t=function(e){for(var t=new Array(e.bitLength()),r=0;r>>i}return t}(e);if(0===t.length)return new o(1);for(var r=this,n=0;n=0);var t,r=e%26,i=(e-r)/26,o=67108863>>>26-r<<26-r;if(0!==r){var a=0;for(t=0;t>>26-r}a&&(this.words[t]=a,this.length++)}if(0!==i){for(t=this.length-1;t>=0;t--)this.words[t+i]=this.words[t];for(t=0;t=0),i=t?(t-t%26)/26:0;var o=e%26,a=Math.min((e-o)/26,this.length),s=67108863^67108863>>>o<a)for(this.length-=a,c=0;c=0&&(0!==u||c>=i);c--){var h=0|this.words[c];this.words[c]=u<<26-o|h>>>o,u=h&s}return f&&0!==u&&(f.words[f.length++]=u),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},o.prototype.ishrn=function(e,t,r){return n(0===this.negative),this.iushrn(e,t,r)},o.prototype.shln=function(e){return this.clone().ishln(e)},o.prototype.ushln=function(e){return this.clone().iushln(e)},o.prototype.shrn=function(e){return this.clone().ishrn(e)},o.prototype.ushrn=function(e){return this.clone().iushrn(e)},o.prototype.testn=function(e){n("number"==typeof e&&e>=0);var t=e%26,r=(e-t)/26,i=1<=0);var t=e%26,r=(e-t)/26;if(n(0===this.negative,"imaskn works only with positive numbers"),this.length<=r)return this;if(0!==t&&r++,this.length=Math.min(r,this.length),0!==t){var i=67108863^67108863>>>t<=67108864;t++)this.words[t]-=67108864,t===this.length-1?this.words[t+1]=1:this.words[t+1]++;return this.length=Math.max(this.length,t+1),this},o.prototype.isubn=function(e){if(n("number"==typeof e),n(e<67108864),e<0)return this.iaddn(-e);if(0!==this.negative)return this.negative=0,this.iaddn(e),this.negative=1,this;if(this.words[0]-=e,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var t=0;t>26)-(f/67108864|0),this.words[i+r]=67108863&o}for(;i>26,this.words[i+r]=67108863&o;if(0===s)return this.strip();for(n(-1===s),s=0,i=0;i>26,this.words[i]=67108863&o;return this.negative=1,this.strip()},o.prototype._wordDiv=function(e,t){var r=(this.length,e.length),n=this.clone(),i=e,a=0|i.words[i.length-1];0!=(r=26-this._countBits(a))&&(i=i.ushln(r),n.iushln(r),a=0|i.words[i.length-1]);var s,f=n.length-i.length;if("mod"!==t){(s=new o(null)).length=f+1,s.words=new Array(s.length);for(var c=0;c=0;h--){var l=67108864*(0|n.words[i.length+h])+(0|n.words[i.length+h-1]);for(l=Math.min(l/a|0,67108863),n._ishlnsubmul(i,l,h);0!==n.negative;)l--,n.negative=0,n._ishlnsubmul(i,1,h),n.isZero()||(n.negative^=1);s&&(s.words[h]=l)}return s&&s.strip(),n.strip(),"div"!==t&&0!==r&&n.iushrn(r),{div:s||null,mod:n}},o.prototype.divmod=function(e,t,r){return n(!e.isZero()),this.isZero()?{div:new o(0),mod:new o(0)}:0!==this.negative&&0===e.negative?(s=this.neg().divmod(e,t),"mod"!==t&&(i=s.div.neg()),"div"!==t&&(a=s.mod.neg(),r&&0!==a.negative&&a.iadd(e)),{div:i,mod:a}):0===this.negative&&0!==e.negative?(s=this.divmod(e.neg(),t),"mod"!==t&&(i=s.div.neg()),{div:i,mod:s.mod}):0!=(this.negative&e.negative)?(s=this.neg().divmod(e.neg(),t),"div"!==t&&(a=s.mod.neg(),r&&0!==a.negative&&a.isub(e)),{div:s.div,mod:a}):e.length>this.length||this.cmp(e)<0?{div:new o(0),mod:this}:1===e.length?"div"===t?{div:this.divn(e.words[0]),mod:null}:"mod"===t?{div:null,mod:new o(this.modn(e.words[0]))}:{div:this.divn(e.words[0]),mod:new o(this.modn(e.words[0]))}:this._wordDiv(e,t);var i,a,s},o.prototype.div=function(e){return this.divmod(e,"div",!1).div},o.prototype.mod=function(e){return this.divmod(e,"mod",!1).mod},o.prototype.umod=function(e){return this.divmod(e,"mod",!0).mod},o.prototype.divRound=function(e){var t=this.divmod(e);if(t.mod.isZero())return t.div;var r=0!==t.div.negative?t.mod.isub(e):t.mod,n=e.ushrn(1),i=e.andln(1),o=r.cmp(n);return o<0||1===i&&0===o?t.div:0!==t.div.negative?t.div.isubn(1):t.div.iaddn(1)},o.prototype.modn=function(e){n(e<=67108863);for(var t=(1<<26)%e,r=0,i=this.length-1;i>=0;i--)r=(t*r+(0|this.words[i]))%e;return r},o.prototype.idivn=function(e){n(e<=67108863);for(var t=0,r=this.length-1;r>=0;r--){var i=(0|this.words[r])+67108864*t;this.words[r]=i/e|0,t=i%e}return this.strip()},o.prototype.divn=function(e){return this.clone().idivn(e)},o.prototype.egcd=function(e){n(0===e.negative),n(!e.isZero());var t=this,r=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i=new o(1),a=new o(0),s=new o(0),f=new o(1),c=0;t.isEven()&&r.isEven();)t.iushrn(1),r.iushrn(1),++c;for(var u=r.clone(),h=t.clone();!t.isZero();){for(var l=0,d=1;0==(t.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(t.iushrn(l);l-- >0;)(i.isOdd()||a.isOdd())&&(i.iadd(u),a.isub(h)),i.iushrn(1),a.iushrn(1);for(var p=0,y=1;0==(r.words[0]&y)&&p<26;++p,y<<=1);if(p>0)for(r.iushrn(p);p-- >0;)(s.isOdd()||f.isOdd())&&(s.iadd(u),f.isub(h)),s.iushrn(1),f.iushrn(1);t.cmp(r)>=0?(t.isub(r),i.isub(s),a.isub(f)):(r.isub(t),s.isub(i),f.isub(a))}return{a:s,b:f,gcd:r.iushln(c)}},o.prototype._invmp=function(e){n(0===e.negative),n(!e.isZero());var t=this,r=e.clone();t=0!==t.negative?t.umod(e):t.clone();for(var i,a=new o(1),s=new o(0),f=r.clone();t.cmpn(1)>0&&r.cmpn(1)>0;){for(var c=0,u=1;0==(t.words[0]&u)&&c<26;++c,u<<=1);if(c>0)for(t.iushrn(c);c-- >0;)a.isOdd()&&a.iadd(f),a.iushrn(1);for(var h=0,l=1;0==(r.words[0]&l)&&h<26;++h,l<<=1);if(h>0)for(r.iushrn(h);h-- >0;)s.isOdd()&&s.iadd(f),s.iushrn(1);t.cmp(r)>=0?(t.isub(r),a.isub(s)):(r.isub(t),s.isub(a))}return(i=0===t.cmpn(1)?a:s).cmpn(0)<0&&i.iadd(e),i},o.prototype.gcd=function(e){if(this.isZero())return e.abs();if(e.isZero())return this.abs();var t=this.clone(),r=e.clone();t.negative=0,r.negative=0;for(var n=0;t.isEven()&&r.isEven();n++)t.iushrn(1),r.iushrn(1);for(;;){for(;t.isEven();)t.iushrn(1);for(;r.isEven();)r.iushrn(1);var i=t.cmp(r);if(i<0){var o=t;t=r,r=o}else if(0===i||0===r.cmpn(1))break;t.isub(r)}return r.iushln(n)},o.prototype.invm=function(e){return this.egcd(e).a.umod(e)},o.prototype.isEven=function(){return 0==(1&this.words[0])},o.prototype.isOdd=function(){return 1==(1&this.words[0])},o.prototype.andln=function(e){return this.words[0]&e},o.prototype.bincn=function(e){n("number"==typeof e);var t=e%26,r=(e-t)/26,i=1<>>26,s&=67108863,this.words[a]=s}return 0!==o&&(this.words[a]=o,this.length++),this},o.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},o.prototype.cmpn=function(e){var t,r=e<0;if(0!==this.negative&&!r)return-1;if(0===this.negative&&r)return 1;if(this.strip(),this.length>1)t=1;else{r&&(e=-e),n(e<=67108863,"Number is too big");var i=0|this.words[0];t=i===e?0:ie.length)return 1;if(this.length=0;r--){var n=0|this.words[r],i=0|e.words[r];if(n!==i){ni&&(t=1);break}}return t},o.prototype.gtn=function(e){return 1===this.cmpn(e)},o.prototype.gt=function(e){return 1===this.cmp(e)},o.prototype.gten=function(e){return this.cmpn(e)>=0},o.prototype.gte=function(e){return this.cmp(e)>=0},o.prototype.ltn=function(e){return-1===this.cmpn(e)},o.prototype.lt=function(e){return-1===this.cmp(e)},o.prototype.lten=function(e){return this.cmpn(e)<=0},o.prototype.lte=function(e){return this.cmp(e)<=0},o.prototype.eqn=function(e){return 0===this.cmpn(e)},o.prototype.eq=function(e){return 0===this.cmp(e)},o.red=function(e){return new E(e)},o.prototype.toRed=function(e){return n(!this.red,"Already a number in reduction context"),n(0===this.negative,"red works only with positives"),e.convertTo(this)._forceRed(e)},o.prototype.fromRed=function(){return n(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},o.prototype._forceRed=function(e){return this.red=e,this},o.prototype.forceRed=function(e){return n(!this.red,"Already a number in reduction context"),this._forceRed(e)},o.prototype.redAdd=function(e){return n(this.red,"redAdd works only with red numbers"),this.red.add(this,e)},o.prototype.redIAdd=function(e){return n(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,e)},o.prototype.redSub=function(e){return n(this.red,"redSub works only with red numbers"),this.red.sub(this,e)},o.prototype.redISub=function(e){return n(this.red,"redISub works only with red numbers"),this.red.isub(this,e)},o.prototype.redShl=function(e){return n(this.red,"redShl works only with red numbers"),this.red.shl(this,e)},o.prototype.redMul=function(e){return n(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.mul(this,e)},o.prototype.redIMul=function(e){return n(this.red,"redMul works only with red numbers"),this.red._verify2(this,e),this.red.imul(this,e)},o.prototype.redSqr=function(){return n(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},o.prototype.redISqr=function(){return n(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},o.prototype.redSqrt=function(){return n(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},o.prototype.redInvm=function(){return n(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},o.prototype.redNeg=function(){return n(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},o.prototype.redPow=function(e){return n(this.red&&!e.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,e)};var b={k256:null,p224:null,p192:null,p25519:null};function v(e,t){this.name=e,this.p=new o(t,16),this.n=this.p.bitLength(),this.k=new o(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function g(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function m(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function _(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function E(e){if("string"==typeof e){var t=o._prime(e);this.m=t.p,this.prime=t}else n(e.gtn(1),"modulus must be greater than 1"),this.m=e,this.prime=null}function S(e){E.call(this,e),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new o(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var e=new o(null);return e.words=new Array(Math.ceil(this.n/13)),e},v.prototype.ireduce=function(e){var t,r=e;do{this.split(r,this.tmp),t=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(t>this.n);var n=t0?r.isub(this.p):r.strip(),r},v.prototype.split=function(e,t){e.iushrn(this.n,0,t)},v.prototype.imulK=function(e){return e.imul(this.k)},i(g,v),g.prototype.split=function(e,t){for(var r=Math.min(e.length,9),n=0;n>>22,i=o}i>>>=22,e.words[n-10]=i,0===i&&e.length>10?e.length-=10:e.length-=9},g.prototype.imulK=function(e){e.words[e.length]=0,e.words[e.length+1]=0,e.length+=2;for(var t=0,r=0;r>>=26,e.words[r]=i,t=n}return 0!==t&&(e.words[e.length++]=t),e},o._prime=function(e){if(b[e])return b[e];var t;if("k256"===e)t=new g;else if("p224"===e)t=new m;else if("p192"===e)t=new w;else{if("p25519"!==e)throw new Error("Unknown prime "+e);t=new _}return b[e]=t,t},E.prototype._verify1=function(e){n(0===e.negative,"red works only with positives"),n(e.red,"red works only with red numbers")},E.prototype._verify2=function(e,t){n(0==(e.negative|t.negative),"red works only with positives"),n(e.red&&e.red===t.red,"red works only with red numbers")},E.prototype.imod=function(e){return this.prime?this.prime.ireduce(e)._forceRed(this):e.umod(this.m)._forceRed(this)},E.prototype.neg=function(e){return e.isZero()?e.clone():this.m.sub(e)._forceRed(this)},E.prototype.add=function(e,t){this._verify2(e,t);var r=e.add(t);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},E.prototype.iadd=function(e,t){this._verify2(e,t);var r=e.iadd(t);return r.cmp(this.m)>=0&&r.isub(this.m),r},E.prototype.sub=function(e,t){this._verify2(e,t);var r=e.sub(t);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},E.prototype.isub=function(e,t){this._verify2(e,t);var r=e.isub(t);return r.cmpn(0)<0&&r.iadd(this.m),r},E.prototype.shl=function(e,t){return this._verify1(e),this.imod(e.ushln(t))},E.prototype.imul=function(e,t){return this._verify2(e,t),this.imod(e.imul(t))},E.prototype.mul=function(e,t){return this._verify2(e,t),this.imod(e.mul(t))},E.prototype.isqr=function(e){return this.imul(e,e.clone())},E.prototype.sqr=function(e){return this.mul(e,e)},E.prototype.sqrt=function(e){if(e.isZero())return e.clone();var t=this.m.andln(3);if(n(t%2==1),3===t){var r=this.m.add(new o(1)).iushrn(2);return this.pow(e,r)}for(var i=this.m.subn(1),a=0;!i.isZero()&&0===i.andln(1);)a++,i.iushrn(1);n(!i.isZero());var s=new o(1).toRed(this),f=s.redNeg(),c=this.m.subn(1).iushrn(1),u=this.m.bitLength();for(u=new o(2*u*u).toRed(this);0!==this.pow(u,c).cmp(f);)u.redIAdd(f);for(var h=this.pow(u,i),l=this.pow(e,i.addn(1).iushrn(1)),d=this.pow(e,i),p=a;0!==d.cmp(s);){for(var y=d,b=0;0!==y.cmp(s);b++)y=y.redSqr();n(b=0;n--){for(var c=t.words[n],u=f-1;u>=0;u--){var h=c>>u&1;i!==r[0]&&(i=this.sqr(i)),0!==h||0!==a?(a<<=1,a|=h,(4==++s||0===n&&0===u)&&(i=this.mul(i,r[a]),s=0,a=0)):s=0}f=26}return i},E.prototype.convertTo=function(e){var t=e.umod(this.m);return t===e?t.clone():t},E.prototype.convertFrom=function(e){var t=e.clone();return t.red=null,t},o.mont=function(e){return new S(e)},i(S,E),S.prototype.convertTo=function(e){return this.imod(e.ushln(this.shift))},S.prototype.convertFrom=function(e){var t=this.imod(e.mul(this.rinv));return t.red=null,t},S.prototype.imul=function(e,t){if(e.isZero()||t.isZero())return e.words[0]=0,e.length=1,e;var r=e.imul(t),n=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=r.isub(n).iushrn(this.shift),o=i;return i.cmp(this.m)>=0?o=i.isub(this.m):i.cmpn(0)<0&&(o=i.iadd(this.m)),o._forceRed(this)},S.prototype.mul=function(e,t){if(e.isZero()||t.isZero())return new o(0)._forceRed(this);var r=e.mul(t),n=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),i=r.isub(n).iushrn(this.shift),a=i;return i.cmp(this.m)>=0?a=i.isub(this.m):i.cmpn(0)<0&&(a=i.iadd(this.m)),a._forceRed(this)},S.prototype.invm=function(e){return this.imod(e._invmp(this.m).mul(this.r2))._forceRed(this)}}(e,this)}).call(this,r(178)(e))},function(e,t,r){"use strict";var n,i,o=e.exports=r(19),a=r(89);o.codegen=r(160),o.fetch=r(161),o.path=r(162),o.fs=o.inquire("fs"),o.toArray=function(e){if(e){for(var t=Object.keys(e),r=new Array(t.length),n=0;n(i>>1)-1?(i>>1)-f:f,o.isubn(s)):s=0,n[a]=s,o.iushrn(1)}return n},n.getJSF=function(e,t){var r=[[],[]];e=e.clone(),t=t.clone();for(var n=0,i=0;e.cmpn(-n)>0||t.cmpn(-i)>0;){var o,a,s,f=e.andln(3)+n&3,c=t.andln(3)+i&3;3===f&&(f=-1),3===c&&(c=-1),o=0==(1&f)?0:3!=(s=e.andln(7)+n&7)&&5!==s||2!==c?f:-f,r[0].push(o),a=0==(1&c)?0:3!=(s=t.andln(7)+i&7)&&5!==s||2!==f?c:-c,r[1].push(a),2*n===o+1&&(n=1-n),2*i===a+1&&(i=1-i),e.iushrn(1),t.iushrn(1)}return r},n.cachedProperty=function(e,t,r){var n="_"+t;e.prototype[t]=function(){return void 0!==this[n]?this[n]:this[n]=r.call(this)}},n.parseBytes=function(e){return"string"==typeof e?n.toArray(e,"hex"):e},n.intFromLE=function(e){return new i(e,"hex","le")}},function(e,t){function r(e,t){if(!e)throw new Error(t||"Assertion failed")}e.exports=r,r.equal=function(e,t,r){if(e!=t)throw new Error(r||"Assertion failed: "+e+" != "+t)}},function(module,exports,__webpack_require__){(function(process,global){var __WEBPACK_AMD_DEFINE_RESULT__;\n/**\n * [js-sha256]{@link https://github.com/emn178/js-sha256}\n *\n * @version 0.9.0\n * @author Chen, Yi-Cyuan [emn178@gmail.com]\n * @copyright Chen, Yi-Cyuan 2014-2017\n * @license MIT\n */!function(){"use strict";var ERROR="input is invalid type",WINDOW="object"==typeof window,root=WINDOW?window:{};root.JS_SHA256_NO_WINDOW&&(WINDOW=!1);var WEB_WORKER=!WINDOW&&"object"==typeof self,NODE_JS=!root.JS_SHA256_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;NODE_JS?root=global:WEB_WORKER&&(root=self);var COMMON_JS=!root.JS_SHA256_NO_COMMON_JS&&"object"==typeof module&&module.exports,AMD=__webpack_require__(163),ARRAY_BUFFER=!root.JS_SHA256_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,HEX_CHARS="0123456789abcdef".split(""),EXTRA=[-2147483648,8388608,32768,128],SHIFT=[24,16,8,0],K=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],OUTPUT_TYPES=["hex","array","digest","arrayBuffer"],blocks=[];!root.JS_SHA256_NO_NODE_JS&&Array.isArray||(Array.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)}),!ARRAY_BUFFER||!root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView=function(e){return"object"==typeof e&&e.buffer&&e.buffer.constructor===ArrayBuffer});var createOutputMethod=function(e,t){return function(r){return new Sha256(t,!0).update(r)[e]()}},createMethod=function(e){var t=createOutputMethod("hex",e);NODE_JS&&(t=nodeWrap(t,e)),t.create=function(){return new Sha256(e)},t.update=function(e){return t.create().update(e)};for(var r=0;r>6,a[f++]=128|63&o):o<55296||o>=57344?(a[f++]=224|o>>12,a[f++]=128|o>>6&63,a[f++]=128|63&o):(o=65536+((1023&o)<<10|1023&e.charCodeAt(++n)),a[f++]=240|o>>18,a[f++]=128|o>>12&63,a[f++]=128|o>>6&63,a[f++]=128|63&o);e=a}else{if("object"!==i)throw new Error(ERROR);if(null===e)throw new Error(ERROR);if(ARRAY_BUFFER&&e.constructor===ArrayBuffer)e=new Uint8Array(e);else if(!(Array.isArray(e)||ARRAY_BUFFER&&ArrayBuffer.isView(e)))throw new Error(ERROR)}e.length>64&&(e=new Sha256(t,!0).update(e).array());var c=[],u=[];for(n=0;n<64;++n){var h=e[n]||0;c[n]=92^h,u[n]=54^h}Sha256.call(this,t,r),this.update(u),this.oKeyPad=c,this.inner=!0,this.sharedMemory=r}Sha256.prototype.update=function(e){if(!this.finalized){var t,r=typeof e;if("string"!==r){if("object"!==r)throw new Error(ERROR);if(null===e)throw new Error(ERROR);if(ARRAY_BUFFER&&e.constructor===ArrayBuffer)e=new Uint8Array(e);else if(!(Array.isArray(e)||ARRAY_BUFFER&&ArrayBuffer.isView(e)))throw new Error(ERROR);t=!0}for(var n,i,o=0,a=e.length,s=this.blocks;o>2]|=e[o]<>2]|=n<>2]|=(192|n>>6)<>2]|=(128|63&n)<=57344?(s[i>>2]|=(224|n>>12)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<>2]|=(240|n>>18)<>2]|=(128|n>>12&63)<>2]|=(128|n>>6&63)<>2]|=(128|63&n)<=64?(this.block=s[16],this.start=i-64,this.hash(),this.hashed=!0):this.start=i}return this.bytes>4294967295&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes=this.bytes%4294967296),this}},Sha256.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var e=this.blocks,t=this.lastByteIndex;e[16]=this.block,e[t>>2]|=EXTRA[3&t],this.block=e[16],t>=56&&(this.hashed||this.hash(),e[0]=this.block,e[16]=e[1]=e[2]=e[3]=e[4]=e[5]=e[6]=e[7]=e[8]=e[9]=e[10]=e[11]=e[12]=e[13]=e[14]=e[15]=0),e[14]=this.hBytes<<3|this.bytes>>>29,e[15]=this.bytes<<3,this.hash()}},Sha256.prototype.hash=function(){var e,t,r,n,i,o,a,s,f,c=this.h0,u=this.h1,h=this.h2,l=this.h3,d=this.h4,p=this.h5,y=this.h6,b=this.h7,v=this.blocks;for(e=16;e<64;++e)t=((i=v[e-15])>>>7|i<<25)^(i>>>18|i<<14)^i>>>3,r=((i=v[e-2])>>>17|i<<15)^(i>>>19|i<<13)^i>>>10,v[e]=v[e-16]+t+v[e-7]+r<<0;for(f=u&h,e=0;e<64;e+=4)this.first?(this.is224?(o=300032,b=(i=v[0]-1413257819)-150054599<<0,l=i+24177077<<0):(o=704751109,b=(i=v[0]-210244248)-1521486534<<0,l=i+143694565<<0),this.first=!1):(t=(c>>>2|c<<30)^(c>>>13|c<<19)^(c>>>22|c<<10),n=(o=c&u)^c&h^f,b=l+(i=b+(r=(d>>>6|d<<26)^(d>>>11|d<<21)^(d>>>25|d<<7))+(d&p^~d&y)+K[e]+v[e])<<0,l=i+(t+n)<<0),t=(l>>>2|l<<30)^(l>>>13|l<<19)^(l>>>22|l<<10),n=(a=l&c)^l&u^o,y=h+(i=y+(r=(b>>>6|b<<26)^(b>>>11|b<<21)^(b>>>25|b<<7))+(b&d^~b&p)+K[e+1]+v[e+1])<<0,t=((h=i+(t+n)<<0)>>>2|h<<30)^(h>>>13|h<<19)^(h>>>22|h<<10),n=(s=h&l)^h&c^a,p=u+(i=p+(r=(y>>>6|y<<26)^(y>>>11|y<<21)^(y>>>25|y<<7))+(y&b^~y&d)+K[e+2]+v[e+2])<<0,t=((u=i+(t+n)<<0)>>>2|u<<30)^(u>>>13|u<<19)^(u>>>22|u<<10),n=(f=u&h)^u&l^s,d=c+(i=d+(r=(p>>>6|p<<26)^(p>>>11|p<<21)^(p>>>25|p<<7))+(p&y^~p&b)+K[e+3]+v[e+3])<<0,c=i+(t+n)<<0;this.h0=this.h0+c<<0,this.h1=this.h1+u<<0,this.h2=this.h2+h<<0,this.h3=this.h3+l<<0,this.h4=this.h4+d<<0,this.h5=this.h5+p<<0,this.h6=this.h6+y<<0,this.h7=this.h7+b<<0},Sha256.prototype.hex=function(){this.finalize();var e=this.h0,t=this.h1,r=this.h2,n=this.h3,i=this.h4,o=this.h5,a=this.h6,s=this.h7,f=HEX_CHARS[e>>28&15]+HEX_CHARS[e>>24&15]+HEX_CHARS[e>>20&15]+HEX_CHARS[e>>16&15]+HEX_CHARS[e>>12&15]+HEX_CHARS[e>>8&15]+HEX_CHARS[e>>4&15]+HEX_CHARS[15&e]+HEX_CHARS[t>>28&15]+HEX_CHARS[t>>24&15]+HEX_CHARS[t>>20&15]+HEX_CHARS[t>>16&15]+HEX_CHARS[t>>12&15]+HEX_CHARS[t>>8&15]+HEX_CHARS[t>>4&15]+HEX_CHARS[15&t]+HEX_CHARS[r>>28&15]+HEX_CHARS[r>>24&15]+HEX_CHARS[r>>20&15]+HEX_CHARS[r>>16&15]+HEX_CHARS[r>>12&15]+HEX_CHARS[r>>8&15]+HEX_CHARS[r>>4&15]+HEX_CHARS[15&r]+HEX_CHARS[n>>28&15]+HEX_CHARS[n>>24&15]+HEX_CHARS[n>>20&15]+HEX_CHARS[n>>16&15]+HEX_CHARS[n>>12&15]+HEX_CHARS[n>>8&15]+HEX_CHARS[n>>4&15]+HEX_CHARS[15&n]+HEX_CHARS[i>>28&15]+HEX_CHARS[i>>24&15]+HEX_CHARS[i>>20&15]+HEX_CHARS[i>>16&15]+HEX_CHARS[i>>12&15]+HEX_CHARS[i>>8&15]+HEX_CHARS[i>>4&15]+HEX_CHARS[15&i]+HEX_CHARS[o>>28&15]+HEX_CHARS[o>>24&15]+HEX_CHARS[o>>20&15]+HEX_CHARS[o>>16&15]+HEX_CHARS[o>>12&15]+HEX_CHARS[o>>8&15]+HEX_CHARS[o>>4&15]+HEX_CHARS[15&o]+HEX_CHARS[a>>28&15]+HEX_CHARS[a>>24&15]+HEX_CHARS[a>>20&15]+HEX_CHARS[a>>16&15]+HEX_CHARS[a>>12&15]+HEX_CHARS[a>>8&15]+HEX_CHARS[a>>4&15]+HEX_CHARS[15&a];return this.is224||(f+=HEX_CHARS[s>>28&15]+HEX_CHARS[s>>24&15]+HEX_CHARS[s>>20&15]+HEX_CHARS[s>>16&15]+HEX_CHARS[s>>12&15]+HEX_CHARS[s>>8&15]+HEX_CHARS[s>>4&15]+HEX_CHARS[15&s]),f},Sha256.prototype.toString=Sha256.prototype.hex,Sha256.prototype.digest=function(){this.finalize();var e=this.h0,t=this.h1,r=this.h2,n=this.h3,i=this.h4,o=this.h5,a=this.h6,s=this.h7,f=[e>>24&255,e>>16&255,e>>8&255,255&e,t>>24&255,t>>16&255,t>>8&255,255&t,r>>24&255,r>>16&255,r>>8&255,255&r,n>>24&255,n>>16&255,n>>8&255,255&n,i>>24&255,i>>16&255,i>>8&255,255&i,o>>24&255,o>>16&255,o>>8&255,255&o,a>>24&255,a>>16&255,a>>8&255,255&a];return this.is224||f.push(s>>24&255,s>>16&255,s>>8&255,255&s),f},Sha256.prototype.array=Sha256.prototype.digest,Sha256.prototype.arrayBuffer=function(){this.finalize();var e=new ArrayBuffer(this.is224?28:32),t=new DataView(e);return t.setUint32(0,this.h0),t.setUint32(4,this.h1),t.setUint32(8,this.h2),t.setUint32(12,this.h3),t.setUint32(16,this.h4),t.setUint32(20,this.h5),t.setUint32(24,this.h6),this.is224||t.setUint32(28,this.h7),e},HmacSha256.prototype=new Sha256,HmacSha256.prototype.finalize=function(){if(Sha256.prototype.finalize.call(this),this.inner){this.inner=!1;var e=this.array();Sha256.call(this,this.is224,this.sharedMemory),this.update(this.oKeyPad),this.update(e),Sha256.prototype.finalize.call(this)}};var exports=createMethod();exports.sha256=exports,exports.sha224=createMethod(!0),exports.sha256.hmac=createHmacMethod(),exports.sha224.hmac=createHmacMethod(!0),COMMON_JS?module.exports=exports:(root.sha256=exports.sha256,root.sha224=exports.sha224,AMD&&(__WEBPACK_AMD_DEFINE_RESULT__=function(){return exports}.call(exports,__webpack_require__,exports,module),void 0===__WEBPACK_AMD_DEFINE_RESULT__||(module.exports=__WEBPACK_AMD_DEFINE_RESULT__)))}()}).call(this,__webpack_require__(16),__webpack_require__(10))},function(e,t,r){"use strict";r.r(t),function(e){r.d(t,"coreRootProto",(function(){return c})),r.d(t,"Transaction",(function(){return u})),r.d(t,"Hash",(function(){return h})),r.d(t,"Address",(function(){return l})),r.d(t,"TransactionFeeCharged",(function(){return d})),r.d(t,"ResourceTokenCharged",(function(){return p})),r.d(t,"getFee",(function(){return y})),r.d(t,"getSerializedDataFromLog",(function(){return b})),r.d(t,"getResourceFee",(function(){return v})),r.d(t,"getTransactionFee",(function(){return g})),r.d(t,"arrayBufferToHex",(function(){return m})),r.d(t,"getRepForAddress",(function(){return w})),r.d(t,"getAddressFromRep",(function(){return _})),r.d(t,"getAddressObjectFromRep",(function(){return E})),r.d(t,"getRepForHash",(function(){return S})),r.d(t,"getHashFromHex",(function(){return A})),r.d(t,"getHashObjectFromHex",(function(){return k})),r.d(t,"encodeTransaction",(function(){return O})),r.d(t,"getTransaction",(function(){return x}));var n=r(51),i=r.n(n),o=r(30),a=r(138),s=r(1),f=r(5),c=o.Root.fromJSON(a),u=c.Transaction,h=c.Hash,l=c.Address,d=c.TransactionFeeCharged,p=c.ResourceTokenCharged,y=function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"TransactionFeeCharged";if(-1===["ResourceTokenCharged","TransactionFeeCharged"].indexOf(r))throw new Error("type needs to be one of ResourceTokenCharged and TransactionFeeCharged");var n=c[r],i=n.decode(e.from(t,"base64"));i=n.toObject(i,{enums:String,longs:String,bytes:String,defaults:!0,arrays:!0,objects:!0,oneofs:!0});var o=Object(f.transform)(n,i,f.OUTPUT_TRANSFORMERS);return Object(f.transformArrayToMap)(n,o)},b=function(e){var t=e.NonIndexed,r=e.Indexed,n=void 0===r?[]:r,o=i()(n||[]);return t&&o.push(t),o.join("")},v=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return Array.isArray(e)&&0!==e.length?e.filter((function(e){return"ResourceTokenCharged"===e.Name})).map((function(e){return y(b(e),"ResourceTokenCharged")})):[]},g=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return Array.isArray(e)&&0!==e.length?e.filter((function(e){return"TransactionFeeCharged"===e.Name})).map((function(e){return y(b(e),"TransactionFeeCharged")})):[]},m=function(e){return Array.prototype.map.call(new Uint8Array(e),(function(e){return"0".concat(e.toString(16)).slice(-2)})).join("")},w=function(t){var r,n=l.fromObject(t);return r=n.value instanceof e?n.value.toString("hex"):m(n.value),s.encodeAddressRep(r)},_=function(t){var r=s.decodeAddressRep(t);return l.create({value:e.from(r.replace("0x",""),"hex")})},E=function(e){return l.toObject(_(e))},S=function(t){var r=l.fromObject(t);return r.value instanceof e?r.value.toString("hex"):m(r.value)},A=function(t){return h.create({value:e.from(t.replace("0x",""),"hex")})},k=function(e){return h.toObject(A(e))},O=function(e){return u.encode(e).finish()},x=function(e,t,r,n){var i={from:_(e),to:_(t),methodName:r,params:n};return u.create(i)}}.call(this,r(3).Buffer)},function(e,t,r){"use strict";e.exports=a;var n=r(26);((a.prototype=Object.create(n.prototype)).constructor=a).className="Enum";var i=r(36),o=r(7);function a(e,t,r,i,o){if(n.call(this,e,r),t&&"object"!=typeof t)throw TypeError("values must be an object");if(this.valuesById={},this.values=Object.create(this.valuesById),this.comment=i,this.comments=o||{},this.reserved=void 0,t)for(var a=Object.keys(t),s=0;s1)for(var r=1;r=e.length)&&56320==(64512&e.charCodeAt(t+1))}function a(e){return(e>>>24|e>>>8&65280|e<<8&16711680|(255&e)<<24)>>>0}function s(e){return 1===e.length?"0"+e:e}function f(e){return 7===e.length?"0"+e:6===e.length?"00"+e:5===e.length?"000"+e:4===e.length?"0000"+e:3===e.length?"00000"+e:2===e.length?"000000"+e:1===e.length?"0000000"+e:e}t.inherits=i,t.toArray=function(e,t){if(Array.isArray(e))return e.slice();if(!e)return[];var r=[];if("string"==typeof e)if(t){if("hex"===t)for((e=e.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(e="0"+e),i=0;i>6|192,r[n++]=63&a|128):o(e,i)?(a=65536+((1023&a)<<10)+(1023&e.charCodeAt(++i)),r[n++]=a>>18|240,r[n++]=a>>12&63|128,r[n++]=a>>6&63|128,r[n++]=63&a|128):(r[n++]=a>>12|224,r[n++]=a>>6&63|128,r[n++]=63&a|128)}else for(i=0;i>>0}return a},t.split32=function(e,t){for(var r=new Array(4*e.length),n=0,i=0;n>>24,r[i+1]=o>>>16&255,r[i+2]=o>>>8&255,r[i+3]=255&o):(r[i+3]=o>>>24,r[i+2]=o>>>16&255,r[i+1]=o>>>8&255,r[i]=255&o)}return r},t.rotr32=function(e,t){return e>>>t|e<<32-t},t.rotl32=function(e,t){return e<>>32-t},t.sum32=function(e,t){return e+t>>>0},t.sum32_3=function(e,t,r){return e+t+r>>>0},t.sum32_4=function(e,t,r,n){return e+t+r+n>>>0},t.sum32_5=function(e,t,r,n,i){return e+t+r+n+i>>>0},t.sum64=function(e,t,r,n){var i=e[t],o=n+e[t+1]>>>0,a=(o>>0,e[t+1]=o},t.sum64_hi=function(e,t,r,n){return(t+n>>>0>>0},t.sum64_lo=function(e,t,r,n){return t+n>>>0},t.sum64_4_hi=function(e,t,r,n,i,o,a,s){var f=0,c=t;return f+=(c=c+n>>>0)>>0)>>0)>>0},t.sum64_4_lo=function(e,t,r,n,i,o,a,s){return t+n+o+s>>>0},t.sum64_5_hi=function(e,t,r,n,i,o,a,s,f,c){var u=0,h=t;return u+=(h=h+n>>>0)>>0)>>0)>>0)>>0},t.sum64_5_lo=function(e,t,r,n,i,o,a,s,f,c){return t+n+o+s+c>>>0},t.rotr64_hi=function(e,t,r){return(t<<32-r|e>>>r)>>>0},t.rotr64_lo=function(e,t,r){return(e<<32-r|t>>>r)>>>0},t.shr64_hi=function(e,t,r){return e>>>r},t.shr64_lo=function(e,t,r){return(e<<32-r|t>>>r)>>>0}},function(e,t,r){"use strict";(function(t,n){var i=r(0).Buffer,o=t.crypto||t.msCrypto;o&&o.getRandomValues?e.exports=function(e,t){if(e>4294967295)throw new RangeError("requested too many random bytes");var r=i.allocUnsafe(e);if(e>0)if(e>65536)for(var a=0;a0)},n.BufferTemp=r(3).Buffer,n.Buffer=function(){try{var e=n.inquire("buffer").Buffer;return e.prototype.utf8Write?e:null}catch(e){return null}}(),n._Buffer_from=null,n._Buffer_allocUnsafe=null,n.newBuffer=function(e){return"number"==typeof e?n.Buffer?n._Buffer_allocUnsafe(e):new n.Array(e):n.Buffer?n._Buffer_from(e):"undefined"==typeof Uint8Array?e:new Uint8Array(e)},n.Array="undefined"!=typeof Uint8Array?Uint8Array:Array,n.Long=n.global.dcodeIO&&n.global.dcodeIO.Long||n.global.Long||n.inquire("long"),n.key2Re=/^true|false|0|1$/,n.key32Re=/^-?(?:0|[1-9][0-9]*)$/,n.key64Re=/^(?:[\\\\x00-\\\\xff]{8}|-?(?:0|[1-9][0-9]*))$/,n.longToHash=function(e){return e?n.LongBits.from(e).toHash():n.LongBits.zeroHash},n.longFromHash=function(e,t){var r=n.LongBits.fromHash(e);return n.Long?n.Long.fromBits(r.lo,r.hi,t):r.toNumber(Boolean(t))},n.merge=i,n.lcFirst=function(e){return e.charAt(0).toLowerCase()+e.substring(1)},n.newError=o,n.ProtocolError=o("ProtocolError"),n.oneOfGetter=function(e){for(var t={},r=0;r-1;--r)if(1===t[e[r]]&&void 0!==this[e[r]]&&null!==this[e[r]])return e[r]}},n.oneOfSetter=function(e){return function(t){for(var r=0;r>>2]>>>24-o%4*8&255;t[n+o>>>2]|=a<<24-(n+o)%4*8}else for(o=0;o>>2]=r[o>>>2];return this.sigBytes+=i,this},clamp:function(){var t=this.words,r=this.sigBytes;t[r>>>2]&=4294967295<<32-r%4*8,t.length=e.ceil(r/4)},clone:function(){var e=o.clone.call(this);return e.words=this.words.slice(0),e},random:function(t){for(var r,n=[],i=function(t){t=t;var r=987654321,n=4294967295;return function(){var i=((r=36969*(65535&r)+(r>>16)&n)<<16)+(t=18e3*(65535&t)+(t>>16)&n)&n;return i/=4294967296,(i+=.5)*(e.random()>.5?1:-1)}},o=0;o>>2]>>>24-i%4*8&255;n.push((o>>>4).toString(16)),n.push((15&o).toString(16))}return n.join("")},parse:function(e){for(var t=e.length,r=[],n=0;n>>3]|=parseInt(e.substr(n,2),16)<<24-n%8*4;return new a.init(r,t/2)}},c=s.Latin1={stringify:function(e){for(var t=e.words,r=e.sigBytes,n=[],i=0;i>>2]>>>24-i%4*8&255;n.push(String.fromCharCode(o))}return n.join("")},parse:function(e){for(var t=e.length,r=[],n=0;n>>2]|=(255&e.charCodeAt(n))<<24-n%4*8;return new a.init(r,t)}},u=s.Utf8={stringify:function(e){try{return decodeURIComponent(escape(c.stringify(e)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(e){return c.parse(unescape(encodeURIComponent(e)))}},h=i.BufferedBlockAlgorithm=o.extend({reset:function(){this._data=new a.init,this._nDataBytes=0},_append:function(e){"string"==typeof e&&(e=u.parse(e)),this._data.concat(e),this._nDataBytes+=e.sigBytes},_process:function(t){var r=this._data,n=r.words,i=r.sigBytes,o=this.blockSize,s=i/(4*o),f=(s=t?e.ceil(s):e.max((0|s)-this._minBufferSize,0))*o,c=e.min(4*f,i);if(f){for(var u=0;u=this._finalSize&&(this._update(this._block),this._block.fill(0));var r=8*this._len;if(r<=4294967295)this._block.writeUInt32BE(r,this._blockSize-4);else{var n=(4294967295&r)>>>0,i=(r-n)/4294967296;this._block.writeUInt32BE(i,this._blockSize-8),this._block.writeUInt32BE(n,this._blockSize-4)}this._update(this._block);var o=this._hash();return e?o.toString(e):o},i.prototype._update=function(){throw new Error("_update must be implemented by subclass")},e.exports=i},function(e,t,r){"use strict";var n=t;n.version=r(177).version,n.utils=r(11),n.rand=r(62),n.curve=r(97),n.curves=r(63),n.ec=r(191),n.eddsa=r(195)},function(e,t,r){"use strict";e.exports=r(84)},function(e,t,r){var n=r(168);e.exports=n("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")},function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return f})),r.d(t,"b",(function(){return c}));var n=r(140),i=r.n(n),o=r(141),a=r.n(o),s=r(1),f=function(e){var t=e;if(e&&e.value&&(t=e.value),t.indexOf("_")>0){var r=t.split("_").filter((function(e){try{return s.base58.decode(e,"hex"),!0}catch(e){return!1}}));if(0===r.length)throw new Error("Invalid address");t=i()(r,1)[0]}try{s.base58.decode(t,"hex")}catch(e){throw new Error("Invalid address")}return t},c=function(t){var r=e.from(t,"base64");return a.a.FileDescriptorSet.decode(r)}}).call(this,r(3).Buffer)},function(e,t,r){var n=r(219),i=r(75),o=r(76),a=r(232),s=r(49);function f(e,t,r){if(e=e.toLowerCase(),o[e])return i.createCipheriv(e,t,r);if(a[e])return new n({key:t,iv:r,mode:e});throw new TypeError("invalid suite type")}function c(e,t,r){if(e=e.toLowerCase(),o[e])return i.createDecipheriv(e,t,r);if(a[e])return new n({key:t,iv:r,mode:e,decrypt:!0});throw new TypeError("invalid suite type")}t.createCipher=t.Cipher=function(e,t){var r,n;if(e=e.toLowerCase(),o[e])r=o[e].key,n=o[e].iv;else{if(!a[e])throw new TypeError("invalid suite type");r=8*a[e].key,n=a[e].iv}var i=s(t,!1,r,n);return f(e,i.key,i.iv)},t.createCipheriv=t.Cipheriv=f,t.createDecipher=t.Decipher=function(e,t){var r,n;if(e=e.toLowerCase(),o[e])r=o[e].key,n=o[e].iv;else{if(!a[e])throw new TypeError("invalid suite type");r=8*a[e].key,n=a[e].iv}var i=s(t,!1,r,n);return c(e,i.key,i.iv)},t.createDecipheriv=t.Decipheriv=c,t.listCiphers=t.getCiphers=function(){return Object.keys(a).concat(i.getCiphers())}},function(e,t,r){var n;!function(i){"use strict";var o,a=/^-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?$/i,s=Math.ceil,f=Math.floor,c="[BigNumber Error] ",u=c+"Number primitive has more than 15 significant digits: ",h=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13];function l(e){var t=0|e;return e>0||e===t?t:t-1}function d(e){for(var t,r,n=1,i=e.length,o=e[0]+"";nc^r?1:-1;for(s=(f=i.length)<(c=o.length)?f:c,a=0;ao[a]^r?1:-1;return f==c?0:f>c^r?1:-1}function y(e,t,r,n){if(er||e!==f(e))throw Error(c+(n||"Argument")+("number"==typeof e?er?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function b(e){var t=e.c.length-1;return l(e.e/14)==t&&e.c[t]%2!=0}function v(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function g(e,t,r){var n,i;if(t<0){for(i=r+".";++t;i+=r);e=i+e}else if(++t>(n=e.length)){for(i=r,t-=n;--t;i+=r);e+=i}else tR?b.c=b.e=null:e.e=10;h/=10,c++);return void(c>R?b.c=b.e=null:(b.e=c,b.c=[e]))}p=String(e)}else{if(!a.test(p=String(e)))return i(b,p,l);b.s=45==p.charCodeAt(0)?(p=p.slice(1),-1):1}(c=p.indexOf("."))>-1&&(p=p.replace(".","")),(h=p.search(/e/i))>0?(c<0&&(c=h),c+=+p.slice(h+1),p=p.substring(0,h)):c<0&&(c=p.length)}else{if(y(t,2,N.length,"Base"),10==t)return H(b=new L(e),O+b.e+1,x);if(p=String(e),l="number"==typeof e){if(0*e!=0)return i(b,p,l,t);if(b.s=1/e<0?(p=p.slice(1),-1):1,L.DEBUG&&p.replace(/^0\\.0*|\\./,"").length>15)throw Error(u+e)}else b.s=45===p.charCodeAt(0)?(p=p.slice(1),-1):1;for(r=N.slice(0,t),c=h=0,d=p.length;hc){c=d;continue}}else if(!s&&(p==p.toUpperCase()&&(p=p.toLowerCase())||p==p.toLowerCase()&&(p=p.toUpperCase()))){s=!0,h=-1,c=0;continue}return i(b,String(e),l,t)}l=!1,(c=(p=n(p,t,10,b.s)).indexOf("."))>-1?p=p.replace(".",""):c=p.length}for(h=0;48===p.charCodeAt(h);h++);for(d=p.length;48===p.charCodeAt(--d););if(p=p.slice(h,++d)){if(d-=h,l&&L.DEBUG&&d>15&&(e>9007199254740991||e!==f(e)))throw Error(u+b.s*e);if((c=c-h-1)>R)b.c=b.e=null;else if(c=I)?v(f,a):g(f,a,"0");else if(o=(e=H(new L(e),t,r)).e,s=(f=d(e.c)).length,1==n||2==n&&(t<=o||o<=T)){for(;ss){if(--t>0)for(f+=".";t--;f+="0");}else if((t+=o-s)>0)for(o+1==s&&(f+=".");t--;f+="0");return e.s<0&&i?"-"+f:f}function U(e,t){for(var r,n=1,i=new L(e[0]);n=10;i/=10,n++);return(r=n+14*r-1)>R?e.c=e.e=null:r=10;c/=10,i++);if((o=t-i)<0)o+=14,a=t,d=(u=p[l=0])/y[i-a-1]%10|0;else if((l=s((o+1)/14))>=p.length){if(!n)break e;for(;p.length<=l;p.push(0));u=d=0,i=1,a=(o%=14)-14+1}else{for(u=c=p[l],i=1;c>=10;c/=10,i++);d=(a=(o%=14)-14+i)<0?0:u/y[i-a-1]%10|0}if(n=n||t<0||null!=p[l+1]||(a<0?u:u%y[i-a-1]),n=r<4?(d||n)&&(0==r||r==(e.s<0?3:2)):d>5||5==d&&(4==r||n||6==r&&(o>0?a>0?u/y[i-a]:0:p[l-1])%10&1||r==(e.s<0?8:7)),t<1||!p[0])return p.length=0,n?(t-=e.e+1,p[0]=y[(14-t%14)%14],e.e=-t||0):p[0]=e.e=0,e;if(0==o?(p.length=l,c=1,l--):(p.length=l+1,c=y[14-o],p[l]=a>0?f(u/y[i-a]%y[a])*c:0),n)for(;;){if(0==l){for(o=1,a=p[0];a>=10;a/=10,o++);for(a=p[0]+=c,c=1;a>=10;a/=10,c++);o!=c&&(e.e++,1e14==p[0]&&(p[0]=1));break}if(p[l]+=c,1e14!=p[l])break;p[l--]=0,c=1}for(o=p.length;0===p[--o];p.pop());}e.e>R?e.c=e.e=null:e.e=I?v(t,r):g(t,r,"0"),e.s<0?"-"+t:t)}return L.clone=e,L.ROUND_UP=0,L.ROUND_DOWN=1,L.ROUND_CEIL=2,L.ROUND_FLOOR=3,L.ROUND_HALF_UP=4,L.ROUND_HALF_DOWN=5,L.ROUND_HALF_EVEN=6,L.ROUND_HALF_CEIL=7,L.ROUND_HALF_FLOOR=8,L.EUCLID=9,L.config=L.set=function(e){var t,r;if(null!=e){if("object"!=typeof e)throw Error(c+"Object expected: "+e);if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(y(r=e[t],0,1e9,t),O=r),e.hasOwnProperty(t="ROUNDING_MODE")&&(y(r=e[t],0,8,t),x=r),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((r=e[t])&&r.pop?(y(r[0],-1e9,0,t),y(r[1],0,1e9,t),T=r[0],I=r[1]):(y(r,-1e9,1e9,t),T=-(I=r<0?-r:r))),e.hasOwnProperty(t="RANGE"))if((r=e[t])&&r.pop)y(r[0],-1e9,-1,t),y(r[1],1,1e9,t),M=r[0],R=r[1];else{if(y(r,-1e9,1e9,t),!r)throw Error(c+t+" cannot be zero: "+r);M=-(R=r<0?-r:r)}if(e.hasOwnProperty(t="CRYPTO")){if((r=e[t])!==!!r)throw Error(c+t+" not true or false: "+r);if(r){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw P=!r,Error(c+"crypto unavailable");P=r}else P=r}if(e.hasOwnProperty(t="MODULO_MODE")&&(y(r=e[t],0,9,t),j=r),e.hasOwnProperty(t="POW_PRECISION")&&(y(r=e[t],0,1e9,t),B=r),e.hasOwnProperty(t="FORMAT")){if("object"!=typeof(r=e[t]))throw Error(c+t+" not an object: "+r);C=r}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(r=e[t])||/^.$|[+-.\\s]|(.).*\\1/.test(r))throw Error(c+t+" invalid: "+r);N=r}}return{DECIMAL_PLACES:O,ROUNDING_MODE:x,EXPONENTIAL_AT:[T,I],RANGE:[M,R],CRYPTO:P,MODULO_MODE:j,POW_PRECISION:B,FORMAT:C,ALPHABET:N}},L.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!L.DEBUG)return!0;var t,r,n=e.c,i=e.e,o=e.s;e:if("[object Array]"=={}.toString.call(n)){if((1===o||-1===o)&&i>=-1e9&&i<=1e9&&i===f(i)){if(0===n[0]){if(0===i&&1===n.length)return!0;break e}if((t=(i+1)%14)<1&&(t+=14),String(n[0]).length==t){for(t=0;t=1e14||r!==f(r))break e;if(0!==r)return!0}}}else if(null===n&&null===i&&(null===o||1===o||-1===o))return!0;throw Error(c+"Invalid BigNumber: "+e)},L.maximum=L.max=function(){return U(arguments,A.lt)},L.minimum=L.min=function(){return U(arguments,A.gt)},L.random=(o=9007199254740992*Math.random()&2097151?function(){return f(9007199254740992*Math.random())}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(e){var t,r,n,i,a,u=0,l=[],d=new L(k);if(null==e?e=O:y(e,0,1e9),i=s(e/14),P)if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(i*=2));u>>11))>=9e15?(r=crypto.getRandomValues(new Uint32Array(2)),t[u]=r[0],t[u+1]=r[1]):(l.push(a%1e14),u+=2);u=i/2}else{if(!crypto.randomBytes)throw P=!1,Error(c+"crypto unavailable");for(t=crypto.randomBytes(i*=7);u=9e15?crypto.randomBytes(7).copy(t,u):(l.push(a%1e14),u+=7);u=i/7}if(!P)for(;u=10;a/=10,u++);u<14&&(n-=14-u)}return d.e=n,d.c=l,d}),L.sum=function(){for(var e=1,t=arguments,r=new L(t[0]);er-1&&(null==a[i+1]&&(a[i+1]=0),a[i+1]+=a[i]/r|0,a[i]%=r)}return a.reverse()}return function(t,n,i,o,a){var s,f,c,u,h,l,p,y,b=t.indexOf("."),v=O,m=x;for(b>=0&&(u=B,B=0,t=t.replace(".",""),l=(y=new L(n)).pow(t.length-b),B=u,y.c=e(g(d(l.c),l.e,"0"),10,i,"0123456789"),y.e=y.c.length),c=u=(p=e(t,n,i,a?(s=N,"0123456789"):(s="0123456789",N))).length;0==p[--u];p.pop());if(!p[0])return s.charAt(0);if(b<0?--c:(l.c=p,l.e=c,l.s=o,p=(l=r(l,y,v,m,i)).c,h=l.r,c=l.e),b=p[f=c+v+1],u=i/2,h=h||f<0||null!=p[f+1],h=m<4?(null!=b||h)&&(0==m||m==(l.s<0?3:2)):b>u||b==u&&(4==m||h||6==m&&1&p[f-1]||m==(l.s<0?8:7)),f<1||!p[0])t=h?g(s.charAt(1),-v,s.charAt(0)):s.charAt(0);else{if(p.length=f,h)for(--i;++p[--f]>i;)p[f]=0,f||(++c,p=[1].concat(p));for(u=p.length;!p[--u];);for(b=0,t="";b<=u;t+=s.charAt(p[b++]));t=g(t,c,s.charAt(0))}return t}}(),r=function(){function e(e,t,r){var n,i,o,a,s=0,f=e.length,c=t%1e7,u=t/1e7|0;for(e=e.slice();f--;)s=((i=c*(o=e[f]%1e7)+(n=u*o+(a=e[f]/1e7|0)*c)%1e7*1e7+s)/r|0)+(n/1e7|0)+u*a,e[f]=i%r;return s&&(e=[s].concat(e)),e}function t(e,t,r,n){var i,o;if(r!=n)o=r>n?1:-1;else for(i=o=0;it[i]?1:-1;break}return o}function r(e,t,r,n){for(var i=0;r--;)e[r]-=i,i=e[r]1;e.splice(0,1));}return function(n,i,o,a,s){var c,u,h,d,p,y,b,v,g,m,w,_,E,S,A,k,O,x=n.s==i.s?1:-1,T=n.c,I=i.c;if(!(T&&T[0]&&I&&I[0]))return new L(n.s&&i.s&&(T?!I||T[0]!=I[0]:I)?T&&0==T[0]||!I?0*x:x/0:NaN);for(g=(v=new L(x)).c=[],x=o+(u=n.e-i.e)+1,s||(s=1e14,u=l(n.e/14)-l(i.e/14),x=x/14|0),h=0;I[h]==(T[h]||0);h++);if(I[h]>(T[h]||0)&&u--,x<0)g.push(1),d=!0;else{for(S=T.length,k=I.length,h=0,x+=2,(p=f(s/(I[0]+1)))>1&&(I=e(I,p,s),T=e(T,p,s),k=I.length,S=T.length),E=k,w=(m=T.slice(0,k)).length;w=s/2&&A++;do{if(p=0,(c=t(I,m,k,w))<0){if(_=m[0],k!=w&&(_=_*s+(m[1]||0)),(p=f(_/A))>1)for(p>=s&&(p=s-1),b=(y=e(I,p,s)).length,w=m.length;1==t(y,m,b,w);)p--,r(y,k=10;x/=10,h++);H(v,o+(v.e=h+14*u-1)+1,a,d)}else v.e=u,v.r=+d;return v}}(),m=/^(-?)0([xbo])(?=\\w[\\w.]*$)/i,w=/^([^.]+)\\.$/,_=/^\\.([^.]+)$/,E=/^-?(Infinity|NaN)$/,S=/^\\s*\\+(?=[\\w.])|^\\s+|\\s+$/g,i=function(e,t,r,n){var i,o=r?t:t.replace(S,"");if(E.test(o))e.s=isNaN(o)?null:o<0?-1:1;else{if(!r&&(o=o.replace(m,(function(e,t,r){return i="x"==(r=r.toLowerCase())?16:"b"==r?2:8,n&&n!=i?e:t})),n&&(i=n,o=o.replace(w,"$1").replace(_,"0.$1")),t!=o))return new L(o,i);if(L.DEBUG)throw Error(c+"Not a"+(n?" base "+n:"")+" number: "+t);e.s=null}e.c=e.e=null},A.absoluteValue=A.abs=function(){var e=new L(this);return e.s<0&&(e.s=1),e},A.comparedTo=function(e,t){return p(this,new L(e,t))},A.decimalPlaces=A.dp=function(e,t){var r,n,i,o=this;if(null!=e)return y(e,0,1e9),null==t?t=x:y(t,0,8),H(new L(o),e+o.e+1,t);if(!(r=o.c))return null;if(n=14*((i=r.length-1)-l(this.e/14)),i=r[i])for(;i%10==0;i/=10,n--);return n<0&&(n=0),n},A.dividedBy=A.div=function(e,t){return r(this,new L(e,t),O,x)},A.dividedToIntegerBy=A.idiv=function(e,t){return r(this,new L(e,t),0,1)},A.exponentiatedBy=A.pow=function(e,t){var r,n,i,o,a,u,h,l,d=this;if((e=new L(e)).c&&!e.isInteger())throw Error(c+"Exponent not an integer: "+q(e));if(null!=t&&(t=new L(t)),a=e.e>14,!d.c||!d.c[0]||1==d.c[0]&&!d.e&&1==d.c.length||!e.c||!e.c[0])return l=new L(Math.pow(+q(d),a?2-b(e):+q(e))),t?l.mod(t):l;if(u=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new L(NaN);(n=!u&&d.isInteger()&&t.isInteger())&&(d=d.mod(t))}else{if(e.e>9&&(d.e>0||d.e<-1||(0==d.e?d.c[0]>1||a&&d.c[1]>=24e7:d.c[0]<8e13||a&&d.c[0]<=9999975e7)))return o=d.s<0&&b(e)?-0:0,d.e>-1&&(o=1/o),new L(u?1/o:o);B&&(o=s(B/14+2))}for(a?(r=new L(.5),u&&(e.s=1),h=b(e)):h=(i=Math.abs(+q(e)))%2,l=new L(k);;){if(h){if(!(l=l.times(d)).c)break;o?l.c.length>o&&(l.c.length=o):n&&(l=l.mod(t))}if(i){if(0===(i=f(i/2)))break;h=i%2}else if(H(e=e.times(r),e.e+1,1),e.e>14)h=b(e);else{if(0==(i=+q(e)))break;h=i%2}d=d.times(d),o?d.c&&d.c.length>o&&(d.c.length=o):n&&(d=d.mod(t))}return n?l:(u&&(l=k.div(l)),t?l.mod(t):o?H(l,B,x,void 0):l)},A.integerValue=function(e){var t=new L(this);return null==e?e=x:y(e,0,8),H(t,t.e+1,e)},A.isEqualTo=A.eq=function(e,t){return 0===p(this,new L(e,t))},A.isFinite=function(){return!!this.c},A.isGreaterThan=A.gt=function(e,t){return p(this,new L(e,t))>0},A.isGreaterThanOrEqualTo=A.gte=function(e,t){return 1===(t=p(this,new L(e,t)))||0===t},A.isInteger=function(){return!!this.c&&l(this.e/14)>this.c.length-2},A.isLessThan=A.lt=function(e,t){return p(this,new L(e,t))<0},A.isLessThanOrEqualTo=A.lte=function(e,t){return-1===(t=p(this,new L(e,t)))||0===t},A.isNaN=function(){return!this.s},A.isNegative=function(){return this.s<0},A.isPositive=function(){return this.s>0},A.isZero=function(){return!!this.c&&0==this.c[0]},A.minus=function(e,t){var r,n,i,o,a=this,s=a.s;if(t=(e=new L(e,t)).s,!s||!t)return new L(NaN);if(s!=t)return e.s=-t,a.plus(e);var f=a.e/14,c=e.e/14,u=a.c,h=e.c;if(!f||!c){if(!u||!h)return u?(e.s=-t,e):new L(h?a:NaN);if(!u[0]||!h[0])return h[0]?(e.s=-t,e):new L(u[0]?a:3==x?-0:0)}if(f=l(f),c=l(c),u=u.slice(),s=f-c){for((o=s<0)?(s=-s,i=u):(c=f,i=h),i.reverse(),t=s;t--;i.push(0));i.reverse()}else for(n=(o=(s=u.length)<(t=h.length))?s:t,s=t=0;t0)for(;t--;u[r++]=0);for(t=99999999999999;n>s;){if(u[--n]=0;){for(r=0,d=g[i]%1e7,p=g[i]/1e7|0,o=i+(a=f);o>i;)r=((c=d*(c=v[--a]%1e7)+(s=p*c+(u=v[a]/1e7|0)*d)%1e7*1e7+y[o]+r)/1e14|0)+(s/1e7|0)+p*u,y[o--]=c%1e14;y[o]=r}return r?++n:y.splice(0,1),F(e,y,n)},A.negated=function(){var e=new L(this);return e.s=-e.s||null,e},A.plus=function(e,t){var r,n=this,i=n.s;if(t=(e=new L(e,t)).s,!i||!t)return new L(NaN);if(i!=t)return e.s=-t,n.minus(e);var o=n.e/14,a=e.e/14,s=n.c,f=e.c;if(!o||!a){if(!s||!f)return new L(i/0);if(!s[0]||!f[0])return f[0]?e:new L(s[0]?n:0*i)}if(o=l(o),a=l(a),s=s.slice(),i=o-a){for(i>0?(a=o,r=f):(i=-i,r=s),r.reverse();i--;r.push(0));r.reverse()}for((i=s.length)-(t=f.length)<0&&(r=f,f=s,s=r,t=i),i=0;t;)i=(s[--t]=s[t]+f[t]+i)/1e14|0,s[t]=1e14===s[t]?0:s[t]%1e14;return i&&(s=[i].concat(s),++a),F(e,s,a)},A.precision=A.sd=function(e,t){var r,n,i,o=this;if(null!=e&&e!==!!e)return y(e,1,1e9),null==t?t=x:y(t,0,8),H(new L(o),e,t);if(!(r=o.c))return null;if(n=14*(i=r.length-1)+1,i=r[i]){for(;i%10==0;i/=10,n--);for(i=r[0];i>=10;i/=10,n++);}return e&&o.e+1>n&&(n=o.e+1),n},A.shiftedBy=function(e){return y(e,-9007199254740991,9007199254740991),this.times("1e"+e)},A.squareRoot=A.sqrt=function(){var e,t,n,i,o,a=this,s=a.c,f=a.s,c=a.e,u=O+4,h=new L("0.5");if(1!==f||!s||!s[0])return new L(!f||f<0&&(!s||s[0])?NaN:s?a:1/0);if(0==(f=Math.sqrt(+q(a)))||f==1/0?(((t=d(s)).length+c)%2==0&&(t+="0"),f=Math.sqrt(+t),c=l((c+1)/2)-(c<0||c%2),n=new L(t=f==1/0?"1e"+c:(t=f.toExponential()).slice(0,t.indexOf("e")+1)+c)):n=new L(f+""),n.c[0])for((f=(c=n.e)+u)<3&&(f=0);;)if(o=n,n=h.times(o.plus(r(a,o,u,1))),d(o.c).slice(0,f)===(t=d(n.c)).slice(0,f)){if(n.e0&&y>0){for(o=y%s||s,h=p.substr(0,o);o0&&(h+=u+p.slice(o)),d&&(h="-"+h)}n=l?h+(r.decimalSeparator||"")+((f=+r.fractionGroupSize)?l.replace(new RegExp("\\\\d{"+f+"}\\\\B","g"),"$&"+(r.fractionGroupSeparator||"")):l):h}return(r.prefix||"")+n+(r.suffix||"")},A.toFraction=function(e){var t,n,i,o,a,s,f,u,l,p,y,b,v=this,g=v.c;if(null!=e&&(!(f=new L(e)).isInteger()&&(f.c||1!==f.s)||f.lt(k)))throw Error(c+"Argument "+(f.isInteger()?"out of range: ":"not an integer: ")+q(f));if(!g)return new L(v);for(t=new L(k),l=n=new L(k),i=u=new L(k),b=d(g),a=t.e=b.length-v.e-1,t.c[0]=h[(s=a%14)<0?14+s:s],e=!e||f.comparedTo(t)>0?a>0?t:l:f,s=R,R=1/0,f=new L(b),u.c[0]=0;p=r(f,t,0,1),1!=(o=n.plus(p.times(i))).comparedTo(e);)n=i,i=o,l=u.plus(p.times(o=l)),u=o,t=f.minus(p.times(o=t)),f=o;return o=r(e.minus(n),i,0,1),u=u.plus(o.times(l)),n=n.plus(o.times(i)),u.s=l.s=v.s,y=r(l,i,a*=2,x).minus(v).abs().comparedTo(r(u,n,a,x).minus(v).abs())<1?[l,i]:[u,n],R=s,y},A.toNumber=function(){return+q(this)},A.toPrecision=function(e,t){return null!=e&&y(e,1,1e9),D(this,e,t,2)},A.toString=function(e){var t,r=this,i=r.s,o=r.e;return null===o?i?(t="Infinity",i<0&&(t="-"+t)):t="NaN":(null==e?t=o<=T||o>=I?v(d(r.c),o):g(d(r.c),o,"0"):10===e?t=g(d((r=H(new L(r),O+o+1,x)).c),r.e,"0"):(y(e,2,N.length,"Base"),t=n(g(d(r.c),o,"0"),10,e,i,!0)),i<0&&r.c[0]&&(t="-"+t)),t},A.valueOf=A.toJSON=function(){return q(this)},A._isBigNumber=!0,null!=t&&L.set(t),L}()).default=o.BigNumber=o,void 0===(n=function(){return o}.call(t,r,t,e))||(e.exports=n)}()},function(e,t,r){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});const n=r(21),i=r(73),o=r(18),a=r(113);let s=a._default;const f="A wordlist is required but a default could not be found.\\nPlease explicitly pass a 2048 word array explicitly.";function c(e,t,r){for(;e.lengthc(e.toString(2),"0",8)).join("")}function l(e){const t=8*e.length/32;return h([...n("sha256").update(e).digest()]).slice(0,t)}function d(e){return"mnemonic"+(e||"")}function p(t,r){if(!(r=r||s))throw new Error(f);const n=(t||"").normalize("NFKD").split(" ");if(n.length%3!=0)throw new Error("Invalid mnemonic");const i=n.map(e=>{const t=r.indexOf(e);if(-1===t)throw new Error("Invalid mnemonic");return c(t.toString(2),"0",11)}).join(""),o=32*Math.floor(i.length/33),a=i.slice(0,o),h=i.slice(o),d=a.match(/(.{1,8})/g).map(u);if(d.length<16)throw new Error("Invalid entropy");if(d.length>32)throw new Error("Invalid entropy");if(d.length%4!=0)throw new Error("Invalid entropy");const p=e.from(d);if(l(p)!==h)throw new Error("Invalid mnemonic checksum");return p.toString("hex")}function y(t,r){if(e.isBuffer(t)||(t=e.from(t,"hex")),!(r=r||s))throw new Error(f);if(t.length<16)throw new TypeError("Invalid entropy");if(t.length>32)throw new TypeError("Invalid entropy");if(t.length%4!=0)throw new TypeError("Invalid entropy");const n=(h([...t])+l(t)).match(/(.{1,11})/g).map(e=>{const t=u(e);return r[t]});return"BDSOW�"===r[0]?n.join("\0"):n.join(" ")}t.mnemonicToSeedSync=function(t,r){const n=e.from((t||"").normalize("NFKD"),"utf8"),o=e.from(d((r||"").normalize("NFKD")),"utf8");return i.pbkdf2Sync(n,o,2048,64,"sha512")},t.mnemonicToSeed=function(t,r){return new Promise((n,o)=>{try{const a=e.from((t||"").normalize("NFKD"),"utf8"),s=e.from(d((r||"").normalize("NFKD")),"utf8");i.pbkdf2(a,s,2048,64,"sha512",(e,t)=>e?o(e):n(t))}catch(e){return o(e)}})},t.mnemonicToEntropy=p,t.entropyToMnemonic=y,t.generateMnemonic=function(e,t,r){if((e=e||128)%32!=0)throw new TypeError("Invalid entropy");return y((t=t||o)(e/8),r)},t.validateMnemonic=function(e,t){try{p(e,t)}catch(e){return!1}return!0},t.setDefaultWordlist=function(e){const t=a.wordlists[e];if(!t)throw new Error(\'Could not find wordlist for language "\'+e+\'"\');s=t},t.getDefaultWordlist=function(){if(!s)throw new Error("No Default Wordlist set");return Object.keys(a.wordlists).filter(e=>"JA"!==e&&"EN"!==e&&a.wordlists[e].every((e,t)=>e===s[t]))[0]};var b=r(113);t.wordlists=b.wordlists}).call(this,r(3).Buffer)},function(e,t,r){"use strict";e.exports=u;var n=r(26);((u.prototype=Object.create(n.prototype)).constructor=u).className="Namespace";var i,o,a,s=r(24),f=r(7);function c(e,t){if(e&&e.length){for(var r={},n=0;n=t)return!0;return!1},u.isReservedName=function(e,t){if(e)for(var r=0;r0;){var n=e.shift();if(r.nested&&r.nested[n]){if(!((r=r.nested[n])instanceof u))throw Error("path conflicts with non-namespace objects")}else r.add(r=new u(n))}return t&&r.addJSON(t),r},u.prototype.resolveAll=function(){for(var e=this.nestedArray,t=0;t-1)return n}else if(n instanceof u&&(n=n.lookup(e.slice(1),t,!0)))return n}else for(var i=0;i=this._delta8){var r=(e=this.pending).length%this._delta8;this.pending=e.slice(e.length-r,e.length),0===this.pending.length&&(this.pending=null),e=n.join32(e,0,e.length-r,this.endian);for(var i=0;i>>24&255,n[i++]=e>>>16&255,n[i++]=e>>>8&255,n[i++]=255&e}else for(n[i++]=255&e,n[i++]=e>>>8&255,n[i++]=e>>>16&255,n[i++]=e>>>24&255,n[i++]=0,n[i++]=0,n[i++]=0,n[i++]=0,o=8;o0&&t.reverse();var r=e.concat(t);return e.from(i(r),"hex")},a=function(e,t){return function(e,t,r){var n=e;if(0===r.length||n>=t)return null;for(var i=0,o=t,a=[];n-1&&this.oneof.splice(t,1),e.partOf=null,this},a.prototype.onAdd=function(e){n.prototype.onAdd.call(this,e);for(var t=0;t0?this.redN=null:(this._maxwellTrick=!0,this.redN=this.n.toRed(this.red))}function c(e,t){this.curve=e,this.type=t,this.precomputed=null}e.exports=f,f.prototype.point=function(){throw new Error("Not implemented")},f.prototype.validate=function(){throw new Error("Not implemented")},f.prototype._fixedNafMul=function(e,t){s(e.precomputed);var r=e._getDoubles(),n=o(t,1,this._bitLength),i=(1<=f;t--)c=(c<<1)+n[t];a.push(c)}for(var u=this.jpoint(null,null,null),h=this.jpoint(null,null,null),l=i;l>0;l--){for(f=0;f=0;c--){for(t=0;c>=0&&0===a[c];c--)t++;if(c>=0&&t++,f=f.dblp(t),c<0)break;var u=a[c];s(0!==u),f="affine"===e.type?u>0?f.mixedAdd(i[u-1>>1]):f.mixedAdd(i[-u-1>>1].neg()):u>0?f.add(i[u-1>>1]):f.add(i[-u-1>>1].neg())}return"affine"===e.type?f.toP():f},f.prototype._wnafMulAdd=function(e,t,r,n,i){for(var s=this._wnafT1,f=this._wnafT2,c=this._wnafT3,u=0,h=0;h=1;h-=2){var d=h-1,p=h;if(1===s[d]&&1===s[p]){var y=[t[d],null,null,t[p]];0===t[d].y.cmp(t[p].y)?(y[1]=t[d].add(t[p]),y[2]=t[d].toJ().mixedAdd(t[p].neg())):0===t[d].y.cmp(t[p].y.redNeg())?(y[1]=t[d].toJ().mixedAdd(t[p]),y[2]=t[d].add(t[p].neg())):(y[1]=t[d].toJ().mixedAdd(t[p]),y[2]=t[d].toJ().mixedAdd(t[p].neg()));var b=[-3,-1,-5,-7,0,7,5,1,3],v=a(r[d],r[p]);u=Math.max(v[0].length,u),c[d]=new Array(u),c[p]=new Array(u);for(var g=0;g=0;h--){for(var S=0;h>=0;){var A=!0;for(g=0;g=0&&S++,_=_.dblp(S),h<0)break;for(g=0;g0?k=f[g][O-1>>1]:O<0&&(k=f[g][-O-1>>1].neg()),_="affine"===k.type?_.mixedAdd(k):_.add(k))}}for(h=0;h=Math.ceil((e.bitLength()+1)/t.step)},c.prototype._getDoubles=function(e,t){if(this.precomputed&&this.precomputed.doubles)return this.precomputed.doubles;for(var r=[this],n=this,i=0;i>>24]^u[p>>>16&255]^h[y>>>8&255]^l[255&b]^t[v++],a=c[p>>>24]^u[y>>>16&255]^h[b>>>8&255]^l[255&d]^t[v++],s=c[y>>>24]^u[b>>>16&255]^h[d>>>8&255]^l[255&p]^t[v++],f=c[b>>>24]^u[d>>>16&255]^h[p>>>8&255]^l[255&y]^t[v++],d=o,p=a,y=s,b=f;return o=(n[d>>>24]<<24|n[p>>>16&255]<<16|n[y>>>8&255]<<8|n[255&b])^t[v++],a=(n[p>>>24]<<24|n[y>>>16&255]<<16|n[b>>>8&255]<<8|n[255&d])^t[v++],s=(n[y>>>24]<<24|n[b>>>16&255]<<16|n[d>>>8&255]<<8|n[255&p])^t[v++],f=(n[b>>>24]<<24|n[d>>>16&255]<<16|n[p>>>8&255]<<8|n[255&y])^t[v++],[o>>>=0,a>>>=0,s>>>=0,f>>>=0]}var s=[0,1,2,4,8,16,32,64,128,27,54],f=function(){for(var e=new Array(256),t=0;t<256;t++)e[t]=t<128?t<<1:t<<1^283;for(var r=[],n=[],i=[[],[],[],[]],o=[[],[],[],[]],a=0,s=0,f=0;f<256;++f){var c=s^s<<1^s<<2^s<<3^s<<4;c=c>>>8^255&c^99,r[a]=c,n[c]=a;var u=e[a],h=e[u],l=e[h],d=257*e[c]^16843008*c;i[0][a]=d<<24|d>>>8,i[1][a]=d<<16|d>>>16,i[2][a]=d<<8|d>>>24,i[3][a]=d,d=16843009*l^65537*h^257*u^16843008*a,o[0][c]=d<<24|d>>>8,o[1][c]=d<<16|d>>>16,o[2][c]=d<<8|d>>>24,o[3][c]=d,0===a?a=s=1:(a=u^e[e[e[l^u]]],s^=e[e[s]])}return{SBOX:r,INV_SBOX:n,SUB_MIX:i,INV_SUB_MIX:o}}();function c(e){this._key=i(e),this._reset()}c.blockSize=16,c.keySize=32,c.prototype.blockSize=c.blockSize,c.prototype.keySize=c.keySize,c.prototype._reset=function(){for(var e=this._key,t=e.length,r=t+6,n=4*(r+1),i=[],o=0;o>>24,a=f.SBOX[a>>>24]<<24|f.SBOX[a>>>16&255]<<16|f.SBOX[a>>>8&255]<<8|f.SBOX[255&a],a^=s[o/t|0]<<24):t>6&&o%t==4&&(a=f.SBOX[a>>>24]<<24|f.SBOX[a>>>16&255]<<16|f.SBOX[a>>>8&255]<<8|f.SBOX[255&a]),i[o]=i[o-t]^a}for(var c=[],u=0;u>>24]]^f.INV_SUB_MIX[1][f.SBOX[l>>>16&255]]^f.INV_SUB_MIX[2][f.SBOX[l>>>8&255]]^f.INV_SUB_MIX[3][f.SBOX[255&l]]}this._nRounds=r,this._keySchedule=i,this._invKeySchedule=c},c.prototype.encryptBlockRaw=function(e){return a(e=i(e),this._keySchedule,f.SUB_MIX,f.SBOX,this._nRounds)},c.prototype.encryptBlock=function(e){var t=this.encryptBlockRaw(e),r=n.allocUnsafe(16);return r.writeUInt32BE(t[0],0),r.writeUInt32BE(t[1],4),r.writeUInt32BE(t[2],8),r.writeUInt32BE(t[3],12),r},c.prototype.decryptBlock=function(e){var t=(e=i(e))[1];e[1]=e[3],e[3]=t;var r=a(e,this._invKeySchedule,f.INV_SUB_MIX,f.INV_SBOX,this._nRounds),o=n.allocUnsafe(16);return o.writeUInt32BE(r[0],0),o.writeUInt32BE(r[3],4),o.writeUInt32BE(r[2],8),o.writeUInt32BE(r[1],12),o},c.prototype.scrub=function(){o(this._keySchedule),o(this._invKeySchedule),o(this._key)},e.exports.AES=c},function(e,t,r){var n=r(0).Buffer,i=r(65);e.exports=function(e,t,r,o){if(n.isBuffer(e)||(e=n.from(e,"binary")),t&&(n.isBuffer(t)||(t=n.from(t,"binary")),8!==t.length))throw new RangeError("salt should be Buffer with 8 byte length");for(var a=r/8,s=n.alloc(a),f=n.alloc(o||0),c=n.alloc(0);a>0||o>0;){var u=new i;u.update(c),u.update(e),t&&u.update(t),c=u.digest();var h=0;if(a>0){var l=s.length-a;h=Math.min(a,c.length),c.copy(s,l,0,h),a-=h}if(h0){var d=f.length-o,p=Math.min(o,c.length-h);c.copy(f,d,h,h+p),o-=p}}return c.fill(0),{key:s,iv:f}}},function(e,t,r){var n=r(238),i=r(249),o=r(250),a=r(75),s=r(73),f=r(0).Buffer;function c(e){var t;"object"!=typeof e||f.isBuffer(e)||(t=e.passphrase,e=e.key),"string"==typeof e&&(e=f.from(e));var r,c,u=o(e,t),h=u.tag,l=u.data;switch(h){case"CERTIFICATE":c=n.certificate.decode(l,"der").tbsCertificate.subjectPublicKeyInfo;case"PUBLIC KEY":switch(c||(c=n.PublicKey.decode(l,"der")),r=c.algorithm.algorithm.join(".")){case"1.2.840.113549.1.1.1":return n.RSAPublicKey.decode(c.subjectPublicKey.data,"der");case"1.2.840.10045.2.1":return c.subjectPrivateKey=c.subjectPublicKey,{type:"ec",data:c};case"1.2.840.10040.4.1":return c.algorithm.params.pub_key=n.DSAparam.decode(c.subjectPublicKey.data,"der"),{type:"dsa",data:c.algorithm.params};default:throw new Error("unknown key id "+r)}throw new Error("unknown key type "+h);case"ENCRYPTED PRIVATE KEY":l=function(e,t){var r=e.algorithm.decrypt.kde.kdeparams.salt,n=parseInt(e.algorithm.decrypt.kde.kdeparams.iters.toString(),10),o=i[e.algorithm.decrypt.cipher.algo.join(".")],c=e.algorithm.decrypt.cipher.iv,u=e.subjectPrivateKey,h=parseInt(o.split("-")[1],10)/8,l=s.pbkdf2Sync(t,r,n,h,"sha1"),d=a.createDecipheriv(o,l,c),p=[];return p.push(d.update(u)),p.push(d.final()),f.concat(p)}(l=n.EncryptedPrivateKey.decode(l,"der"),t);case"PRIVATE KEY":switch(r=(c=n.PrivateKey.decode(l,"der")).algorithm.algorithm.join(".")){case"1.2.840.113549.1.1.1":return n.RSAPrivateKey.decode(c.subjectPrivateKey,"der");case"1.2.840.10045.2.1":return{curve:c.algorithm.curve,privateKey:n.ECPrivateKey.decode(c.subjectPrivateKey,"der").privateKey};case"1.2.840.10040.4.1":return c.algorithm.params.priv_key=n.DSAparam.decode(c.subjectPrivateKey,"der"),{type:"dsa",params:c.algorithm.params};default:throw new Error("unknown key id "+r)}throw new Error("unknown key type "+h);case"RSA PUBLIC KEY":return n.RSAPublicKey.decode(l,"der");case"RSA PRIVATE KEY":return n.RSAPrivateKey.decode(l,"der");case"DSA PRIVATE KEY":return{type:"dsa",params:n.DSAPrivateKey.decode(l,"der")};case"EC PRIVATE KEY":return{curve:(l=n.ECPrivateKey.decode(l,"der")).parameters.value,privateKey:l.privateKey};default:throw new Error("unknown key type "+h)}}e.exports=c,c.signature=n.signature},function(e,t,r){var n=r(164),i=r(165),o=r(166);e.exports=function(e){return n(e)||i(e)||o()}},function(e,t,r){"use strict";(function(e){var n=r(4),i=r.n(n),o=r(29),a=r.n(o),s=r(35),f=r(53),c=r.n(f),u=r(13),h=r.n(u),l=r(79),d=r.n(l),p=r(145),y=r.n(p),b=r(146),v=r(1),g=r(14);function m(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}var w=h.a.sha256,_=new a.a.ec("secp256k1"),E=function(t){var r=t.encode(),n=e.from(w(r),"hex"),i=w(n).slice(0,64);return Object(v.encodeAddressRep)(i)},S=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"m/44\'/1616\'/0\'/0/0",n="",i="",o="",a="";switch(e){case"createNewWallet":n=s.generateMnemonic(),i=s.mnemonicToSeedSync(n).toString("hex"),o=c.a.fromMasterSeed(i).derive(r),a=_.keyFromPrivate(o.privateKey);break;case"getWalletByMnemonic":n=t,i=s.mnemonicToSeedSync(n).toString("hex"),o=c.a.fromMasterSeed(i).derive(r),a=_.keyFromPrivate(o.privateKey);break;case"getWalletByPrivateKey":a="string"==typeof t?_.keyFromPrivate(Object(v.padLeft)(t,64,"0")):_.keyFromPrivate(t);break;default:throw new Error("not a valid method")}var f=a.getPrivate().toString(16,64),u=a.getPublic(),h=E(u);return{mnemonic:n,BIP44Path:r,childWallet:o,keyPair:a,privateKey:f,address:h}},A=function(t,r){var n=r.getPrivate("hex"),i=w(t),o=_.sign(e.from(i,"hex"),n,"hex",{canonical:!0}),a=[o.r.toString("hex",32),o.s.toString("hex",32),"0".concat(o.recoveryParam.toString())].join("");return e.from(a,"hex")};t.a={hdkey:c.a,bip39:s,sign:function(t,r){var n=e.from(t.replace("0x",""),"hex");return A(n,r)},signTransaction:function(e,t){var r=e.params;0===r.length&&(r=null);var n=g.Transaction.encode(e).finish();return function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:"m/44\'/1616\'/0\'/0/0";return S("createNewWallet","",e)},getWalletByMnemonic:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"m/44\'/1616\'/0\'/0/0";return!!s.validateMnemonic(e)&&S("getWalletByMnemonic",e,t)},getWalletByPrivateKey:function(e){return S("getWalletByPrivateKey",e)},getAddressFromPubKey:E,ellipticEc:_,AESEncrypt:function(e,t){return d.a.encrypt(e,t).toString()},AESDecrypt:function(e,t){return d.a.decrypt(e,t).toString(y.a)},keyStore:b}}).call(this,r(3).Buffer)},function(e,t,r){var n=r(214),i=r(0).Buffer,o=r(47),a=r(257),s=r(258),f=i.from("Bitcoin seed","utf8"),c={private:76066276,public:76067358};function u(e){this.versions=e||c,this.depth=0,this.index=0,this._privateKey=null,this._publicKey=null,this.chainCode=null,this._fingerprint=0,this.parentFingerprint=0}function h(e,t,r){var n=i.allocUnsafe(78);n.writeUInt32BE(t,0),n.writeUInt8(e.depth,4);var o=e.depth?e.parentFingerprint:0;return n.writeUInt32BE(o,5),n.writeUInt32BE(e.index,9),e.chainCode.copy(n,13),r.copy(n,45),n}function l(e){var t=o.createHash("sha256").update(e).digest();return o.createHash("ripemd160").update(t).digest()}Object.defineProperty(u.prototype,"fingerprint",{get:function(){return this._fingerprint}}),Object.defineProperty(u.prototype,"identifier",{get:function(){return this._identifier}}),Object.defineProperty(u.prototype,"pubKeyHash",{get:function(){return this.identifier}}),Object.defineProperty(u.prototype,"privateKey",{get:function(){return this._privateKey},set:function(e){n.equal(e.length,32,"Private key must be 32 bytes."),n(!0===s.privateKeyVerify(e),"Invalid private key"),this._privateKey=e,this._publicKey=s.publicKeyCreate(e,!0),this._identifier=l(this.publicKey),this._fingerprint=this._identifier.slice(0,4).readUInt32BE(0)}}),Object.defineProperty(u.prototype,"publicKey",{get:function(){return this._publicKey},set:function(e){n(33===e.length||65===e.length,"Public key must be 33 or 65 bytes."),n(!0===s.publicKeyVerify(e),"Invalid public key"),this._publicKey=s.publicKeyConvert(e,!0),this._identifier=l(this.publicKey),this._fingerprint=this._identifier.slice(0,4).readUInt32BE(0),this._privateKey=null}}),Object.defineProperty(u.prototype,"privateExtendedKey",{get:function(){return this._privateKey?a.encode(h(this,this.versions.private,i.concat([i.alloc(1,0),this.privateKey]))):null}}),Object.defineProperty(u.prototype,"publicExtendedKey",{get:function(){return a.encode(h(this,this.versions.public,this.publicKey))}}),u.prototype.derive=function(e){if("m"===e||"M"===e||"m\'"===e||"M\'"===e)return this;var t=e.split("/"),r=this;return t.forEach((function(e,t){if(0!==t){var i=e.length>1&&"\'"===e[e.length-1],o=parseInt(e,10);n(o<2147483648,"Invalid index"),i&&(o+=2147483648),r=r.deriveChild(o)}else n(/^[mM]{1}/.test(e),\'Path must start with "m" or "M"\')})),r},u.prototype.deriveChild=function(e){var t,r=e>=2147483648,a=i.allocUnsafe(4);if(a.writeUInt32BE(e,0),r){n(this.privateKey,"Could not derive hardened child key");var f=this.privateKey,c=i.alloc(1,0);f=i.concat([c,f]),t=i.concat([f,a])}else t=i.concat([this.publicKey,a]);var h=o.createHmac("sha512",this.chainCode).update(t).digest(),l=h.slice(0,32),d=h.slice(32),p=new u(this.versions);if(this.privateKey)try{p.privateKey=s.privateKeyTweakAdd(this.privateKey,l)}catch(t){return this.derive(e+1)}else try{p.publicKey=s.publicKeyTweakAdd(this.publicKey,l,!0)}catch(t){return this.derive(e+1,r)}return p.chainCode=d,p.depth=this.depth+1,p.parentFingerprint=this.fingerprint,p.index=e,p},u.prototype.sign=function(e){return s.sign(e,this.privateKey).signature},u.prototype.verify=function(e,t){return s.verify(e,t,this.publicKey)},u.prototype.wipePrivateData=function(){return this._privateKey&&o.randomBytes(this._privateKey.length).copy(this._privateKey),this._privateKey=null,this},u.prototype.toJSON=function(){return{xpriv:this.privateExtendedKey,xpub:this.publicExtendedKey}},u.fromMasterSeed=function(e,t){var r=o.createHmac("sha512",f).update(e).digest(),n=r.slice(0,32),i=r.slice(32),a=new u(t);return a.chainCode=i,a.privateKey=n,a},u.fromExtendedKey=function(e,t){var r=new u(t=t||c),i=a.decode(e),o=i.readUInt32BE(0);n(o===t.private||o===t.public,"Version mismatch: does not match private or public"),r.depth=i.readUInt8(4),r.parentFingerprint=i.readUInt32BE(5),r.index=i.readUInt32BE(9),r.chainCode=i.slice(13,45);var s=i.slice(45);return 0===s.readUInt8(0)?(n(o===t.private,"Version mismatch: version does not match private"),r.privateKey=s.slice(1)):(n(o===t.public,"Version mismatch: version does not match public"),r.publicKey=s),r},u.fromJSON=function(e){return u.fromExtendedKey(e.xpriv)},u.HARDENED_OFFSET=2147483648,e.exports=u},function(e,t,r){"use strict";e.exports=h;var n,i=r(19),o=i.LongBits,a=i.base64,s=i.utf8;function f(e,t,r){this.fn=e,this.len=t,this.next=void 0,this.val=r}function c(){}function u(e){this.head=e.head,this.tail=e.tail,this.len=e.len,this.next=e.states}function h(){this.len=0,this.head=new f(c,0,0),this.tail=this.head,this.states=null}function l(e,t,r){t[r]=255&e}function d(e,t){this.len=e,this.next=void 0,this.val=t}function p(e,t,r){for(;e.hi;)t[r++]=127&e.lo|128,e.lo=(e.lo>>>7|e.hi<<25)>>>0,e.hi>>>=7;for(;e.lo>127;)t[r++]=127&e.lo|128,e.lo=e.lo>>>7;t[r++]=e.lo}function y(e,t,r){t[r]=255&e,t[r+1]=e>>>8&255,t[r+2]=e>>>16&255,t[r+3]=e>>>24}h.create=i.Buffer?function(){return(h.create=function(){return new n})()}:function(){return new h},h.alloc=function(e){return new i.Array(e)},i.Array!==Array&&(h.alloc=i.pool(h.alloc,i.Array.prototype.subarray)),h.prototype._push=function(e,t,r){return this.tail=this.tail.next=new f(e,t,r),this.len+=t,this},d.prototype=Object.create(f.prototype),d.prototype.fn=function(e,t,r){for(;e>127;)t[r++]=127&e|128,e>>>=7;t[r]=e},h.prototype.uint32=function(e){return this.len+=(this.tail=this.tail.next=new d((e>>>=0)<128?1:e<16384?2:e<2097152?3:e<268435456?4:5,e)).len,this},h.prototype.int32=function(e){return e<0?this._push(p,10,o.fromNumber(e)):this.uint32(e)},h.prototype.sint32=function(e){return this.uint32((e<<1^e>>31)>>>0)},h.prototype.uint64=function(e){var t=o.from(e);return this._push(p,t.length(),t)},h.prototype.int64=h.prototype.uint64,h.prototype.sint64=function(e){var t=o.from(e).zzEncode();return this._push(p,t.length(),t)},h.prototype.bool=function(e){return this._push(l,1,e?1:0)},h.prototype.fixed32=function(e){return this._push(y,4,e>>>0)},h.prototype.sfixed32=h.prototype.fixed32,h.prototype.fixed64=function(e){var t=o.from(e);return this._push(y,4,t.lo)._push(y,4,t.hi)},h.prototype.sfixed64=h.prototype.fixed64,h.prototype.float=function(e){return this._push(i.float.writeFloatLE,4,e)},h.prototype.double=function(e){return this._push(i.float.writeDoubleLE,8,e)};var b=i.Array.prototype.set?function(e,t,r){t.set(e,r)}:function(e,t,r){for(var n=0;n>>0;if(!t)return this._push(l,1,0);if(i.isString(e)){var r=h.alloc(t=a.length(e));a.decode(e,r,0),e=r}return this.uint32(t)._push(b,t,e)},h.prototype.string=function(e){var t=s.length(e);return t?this.uint32(t)._push(s.write,t,e):this._push(l,1,0)},h.prototype.fork=function(){return this.states=new u(this),this.head=this.tail=new f(c,0,0),this.len=0,this},h.prototype.reset=function(){return this.states?(this.head=this.states.head,this.tail=this.states.tail,this.len=this.states.len,this.states=this.states.next):(this.head=this.tail=new f(c,0,0),this.len=0),this},h.prototype.ldelim=function(){var e=this.head,t=this.tail,r=this.len;return this.reset().uint32(r),r&&(this.tail.next=e.next,this.tail=t,this.len+=r),this},h.prototype.finish=function(){for(var e=this.head.next,t=this.constructor.alloc(this.len),r=0;e;)e.fn(e.val,t,r),r+=e.len,e=e.next;return t},h._configure=function(e){n=e}},function(e,t,r){"use strict";e.exports=f;var n,i=r(19),o=i.LongBits,a=i.utf8;function s(e,t){return RangeError("index out of range: "+e.pos+" + "+(t||1)+" > "+e.len)}function f(e){this.buf=e,this.pos=0,this.len=e.length}var c,u="undefined"!=typeof Uint8Array?function(e){if(e instanceof Uint8Array||Array.isArray(e))return new f(e);throw Error("illegal buffer")}:function(e){if(Array.isArray(e))return new f(e);throw Error("illegal buffer")};function h(){var e=new o(0,0),t=0;if(!(this.len-this.pos>4)){for(;t<3;++t){if(this.pos>=this.len)throw s(this);if(e.lo=(e.lo|(127&this.buf[this.pos])<<7*t)>>>0,this.buf[this.pos++]<128)return e}return e.lo=(e.lo|(127&this.buf[this.pos++])<<7*t)>>>0,e}for(;t<4;++t)if(e.lo=(e.lo|(127&this.buf[this.pos])<<7*t)>>>0,this.buf[this.pos++]<128)return e;if(e.lo=(e.lo|(127&this.buf[this.pos])<<28)>>>0,e.hi=(e.hi|(127&this.buf[this.pos])>>4)>>>0,this.buf[this.pos++]<128)return e;if(t=0,this.len-this.pos>4){for(;t<5;++t)if(e.hi=(e.hi|(127&this.buf[this.pos])<<7*t+3)>>>0,this.buf[this.pos++]<128)return e}else for(;t<5;++t){if(this.pos>=this.len)throw s(this);if(e.hi=(e.hi|(127&this.buf[this.pos])<<7*t+3)>>>0,this.buf[this.pos++]<128)return e}throw Error("invalid varint encoding")}function l(e,t){return(e[t-4]|e[t-3]<<8|e[t-2]<<16|e[t-1]<<24)>>>0}function d(){if(this.pos+8>this.len)throw s(this,8);return new o(l(this.buf,this.pos+=4),l(this.buf,this.pos+=4))}f.create=i.Buffer?function(e){return(f.create=function(e){return i.Buffer.isBuffer(e)?new n(e):u(e)})(e)}:u,f.prototype._slice=i.Array.prototype.subarray||i.Array.prototype.slice,f.prototype.uint32=(c=4294967295,function(){if(c=(127&this.buf[this.pos])>>>0,this.buf[this.pos++]<128)return c;if(c=(c|(127&this.buf[this.pos])<<7)>>>0,this.buf[this.pos++]<128)return c;if(c=(c|(127&this.buf[this.pos])<<14)>>>0,this.buf[this.pos++]<128)return c;if(c=(c|(127&this.buf[this.pos])<<21)>>>0,this.buf[this.pos++]<128)return c;if(c=(c|(15&this.buf[this.pos])<<28)>>>0,this.buf[this.pos++]<128)return c;if((this.pos+=5)>this.len)throw this.pos=this.len,s(this,10);return c}),f.prototype.int32=function(){return 0|this.uint32()},f.prototype.sint32=function(){var e=this.uint32();return e>>>1^-(1&e)|0},f.prototype.bool=function(){return 0!==this.uint32()},f.prototype.fixed32=function(){if(this.pos+4>this.len)throw s(this,4);return l(this.buf,this.pos+=4)},f.prototype.sfixed32=function(){if(this.pos+4>this.len)throw s(this,4);return 0|l(this.buf,this.pos+=4)},f.prototype.float=function(){if(this.pos+4>this.len)throw s(this,4);var e=i.float.readFloatLE(this.buf,this.pos);return this.pos+=4,e},f.prototype.double=function(){if(this.pos+8>this.len)throw s(this,4);var e=i.float.readDoubleLE(this.buf,this.pos);return this.pos+=8,e},f.prototype.bytes=function(){var e=this.uint32(),t=this.pos,r=this.pos+e;if(r>this.len)throw s(this,e);return this.pos+=e,Array.isArray(this.buf)?this.buf.slice(t,r):t===r?new this.buf.constructor(0):this._slice.call(this.buf,t,r)},f.prototype.string=function(){var e=this.bytes();return a.read(e,0,e.length)},f.prototype.skip=function(e){if("number"==typeof e){if(this.pos+e>this.len)throw s(this,e);this.pos+=e}else do{if(this.pos>=this.len)throw s(this)}while(128&this.buf[this.pos++]);return this},f.prototype.skipType=function(e){switch(e){case 0:this.skip();break;case 1:this.skip(8);break;case 2:this.skip(this.uint32());break;case 3:for(;4!=(e=7&this.uint32());)this.skipType(e);break;case 5:this.skip(4);break;default:throw Error("invalid wire type "+e+" at offset "+this.pos)}return this},f._configure=function(e){n=e;var t=i.Long?"toLong":"toNumber";i.merge(f.prototype,{int64:function(){return h.call(this)[t](!1)},uint64:function(){return h.call(this)[t](!0)},sint64:function(){return h.call(this).zzDecode()[t](!1)},fixed64:function(){return d.call(this)[t](!0)},sfixed64:function(){return d.call(this)[t](!1)}})}},function(e,t,r){"use strict";e.exports=g;var n=r(36);((g.prototype=Object.create(n.prototype)).constructor=g).className="Type";var i=r(15),o=r(44),a=r(24),s=r(57),f=r(58),c=r(60),u=r(55),h=r(54),l=r(7),d=r(90),p=r(91),y=r(92),b=r(93),v=r(94);function g(e,t){n.call(this,e,t),this.fields={},this.oneofs=void 0,this.extensions=void 0,this.reserved=void 0,this.group=void 0,this._fieldsById=null,this._fieldsArray=null,this._oneofsArray=null,this._ctor=null}function m(e){return e._fieldsById=e._fieldsArray=e._oneofsArray=null,delete e.encode,delete e.decode,delete e.verify,e}Object.defineProperties(g.prototype,{fieldsById:{get:function(){if(this._fieldsById)return this._fieldsById;this._fieldsById={};for(var e=Object.keys(this.fields),t=0;t-1){var o=e.substring(r);o in a&&(e=o)}if(!(i.files.indexOf(e)>-1))if(i.files.push(e),e in a)s?c(e,a[e]):(++d,setTimeout((function(){--d,c(e,a[e])})));else if(s){var h;try{h=u.fs.readFileSync(e).toString("utf8")}catch(e){return void(t||f(e))}c(e,h)}else++d,u.fetch(e,(function(r,o){--d,n&&(r?t?d||f(null,i):f(r):c(e,o))}))}var d=0;u.isString(t)&&(t=[t]);for(var p,y=0;y-1&&this.deferred.splice(t,1)}}else if(e instanceof f)d.test(e.name)&&delete e.parent[e.name];else if(e instanceof n){for(var r=0;r>>32-t}function c(e,t,r,n,i,o,a){return f(e+(t&r|~t&n)+i+o|0,a)+t|0}function u(e,t,r,n,i,o,a){return f(e+(t&n|r&~n)+i+o|0,a)+t|0}function h(e,t,r,n,i,o,a){return f(e+(t^r^n)+i+o|0,a)+t|0}function l(e,t,r,n,i,o,a){return f(e+(r^(t|~n))+i+o|0,a)+t|0}n(s,i),s.prototype._update=function(){for(var e=a,t=0;t<16;++t)e[t]=this._block.readInt32LE(4*t);var r=this._a,n=this._b,i=this._c,o=this._d;r=c(r,n,i,o,e[0],3614090360,7),o=c(o,r,n,i,e[1],3905402710,12),i=c(i,o,r,n,e[2],606105819,17),n=c(n,i,o,r,e[3],3250441966,22),r=c(r,n,i,o,e[4],4118548399,7),o=c(o,r,n,i,e[5],1200080426,12),i=c(i,o,r,n,e[6],2821735955,17),n=c(n,i,o,r,e[7],4249261313,22),r=c(r,n,i,o,e[8],1770035416,7),o=c(o,r,n,i,e[9],2336552879,12),i=c(i,o,r,n,e[10],4294925233,17),n=c(n,i,o,r,e[11],2304563134,22),r=c(r,n,i,o,e[12],1804603682,7),o=c(o,r,n,i,e[13],4254626195,12),i=c(i,o,r,n,e[14],2792965006,17),r=u(r,n=c(n,i,o,r,e[15],1236535329,22),i,o,e[1],4129170786,5),o=u(o,r,n,i,e[6],3225465664,9),i=u(i,o,r,n,e[11],643717713,14),n=u(n,i,o,r,e[0],3921069994,20),r=u(r,n,i,o,e[5],3593408605,5),o=u(o,r,n,i,e[10],38016083,9),i=u(i,o,r,n,e[15],3634488961,14),n=u(n,i,o,r,e[4],3889429448,20),r=u(r,n,i,o,e[9],568446438,5),o=u(o,r,n,i,e[14],3275163606,9),i=u(i,o,r,n,e[3],4107603335,14),n=u(n,i,o,r,e[8],1163531501,20),r=u(r,n,i,o,e[13],2850285829,5),o=u(o,r,n,i,e[2],4243563512,9),i=u(i,o,r,n,e[7],1735328473,14),r=h(r,n=u(n,i,o,r,e[12],2368359562,20),i,o,e[5],4294588738,4),o=h(o,r,n,i,e[8],2272392833,11),i=h(i,o,r,n,e[11],1839030562,16),n=h(n,i,o,r,e[14],4259657740,23),r=h(r,n,i,o,e[1],2763975236,4),o=h(o,r,n,i,e[4],1272893353,11),i=h(i,o,r,n,e[7],4139469664,16),n=h(n,i,o,r,e[10],3200236656,23),r=h(r,n,i,o,e[13],681279174,4),o=h(o,r,n,i,e[0],3936430074,11),i=h(i,o,r,n,e[3],3572445317,16),n=h(n,i,o,r,e[6],76029189,23),r=h(r,n,i,o,e[9],3654602809,4),o=h(o,r,n,i,e[12],3873151461,11),i=h(i,o,r,n,e[15],530742520,16),r=l(r,n=h(n,i,o,r,e[2],3299628645,23),i,o,e[0],4096336452,6),o=l(o,r,n,i,e[7],1126891415,10),i=l(i,o,r,n,e[14],2878612391,15),n=l(n,i,o,r,e[5],4237533241,21),r=l(r,n,i,o,e[12],1700485571,6),o=l(o,r,n,i,e[3],2399980690,10),i=l(i,o,r,n,e[10],4293915773,15),n=l(n,i,o,r,e[1],2240044497,21),r=l(r,n,i,o,e[8],1873313359,6),o=l(o,r,n,i,e[15],4264355552,10),i=l(i,o,r,n,e[6],2734768916,15),n=l(n,i,o,r,e[13],1309151649,21),r=l(r,n,i,o,e[4],4149444226,6),o=l(o,r,n,i,e[11],3174756917,10),i=l(i,o,r,n,e[2],718787259,15),n=l(n,i,o,r,e[9],3951481745,21),this._a=this._a+r|0,this._b=this._b+n|0,this._c=this._c+i|0,this._d=this._d+o|0},s.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var e=o.allocUnsafe(16);return e.writeInt32LE(this._a,0),e.writeInt32LE(this._b,4),e.writeInt32LE(this._c,8),e.writeInt32LE(this._d,12),e},e.exports=s},function(e,t,r){e.exports=i;var n=r(67).EventEmitter;function i(){n.call(this)}r(2)(i,n),i.Readable=r(68),i.Writable=r(204),i.Duplex=r(205),i.Transform=r(206),i.PassThrough=r(207),i.Stream=i,i.prototype.pipe=function(e,t){var r=this;function i(t){e.writable&&!1===e.write(t)&&r.pause&&r.pause()}function o(){r.readable&&r.resume&&r.resume()}r.on("data",i),e.on("drain",o),e._isStdio||t&&!1===t.end||(r.on("end",s),r.on("close",f));var a=!1;function s(){a||(a=!0,e.end())}function f(){a||(a=!0,"function"==typeof e.destroy&&e.destroy())}function c(e){if(u(),0===n.listenerCount(this,"error"))throw e}function u(){r.removeListener("data",i),e.removeListener("drain",o),r.removeListener("end",s),r.removeListener("close",f),r.removeListener("error",c),e.removeListener("error",c),r.removeListener("end",u),r.removeListener("close",u),e.removeListener("close",u)}return r.on("error",c),e.on("error",c),r.on("end",u),r.on("close",u),e.on("close",u),e.emit("pipe",r),e}},function(e,t,r){"use strict";var n,i="object"==typeof Reflect?Reflect:null,o=i&&"function"==typeof i.apply?i.apply:function(e,t,r){return Function.prototype.apply.call(e,t,r)};n=i&&"function"==typeof i.ownKeys?i.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var a=Number.isNaN||function(e){return e!=e};function s(){s.init.call(this)}e.exports=s,s.EventEmitter=s,s.prototype._events=void 0,s.prototype._eventsCount=0,s.prototype._maxListeners=void 0;var f=10;function c(e){if("function"!=typeof e)throw new TypeError(\'The "listener" argument must be of type Function. Received type \'+typeof e)}function u(e){return void 0===e._maxListeners?s.defaultMaxListeners:e._maxListeners}function h(e,t,r,n){var i,o,a,s;if(c(r),void 0===(o=e._events)?(o=e._events=Object.create(null),e._eventsCount=0):(void 0!==o.newListener&&(e.emit("newListener",t,r.listener?r.listener:r),o=e._events),a=o[t]),void 0===a)a=o[t]=r,++e._eventsCount;else if("function"==typeof a?a=o[t]=n?[r,a]:[a,r]:n?a.unshift(r):a.push(r),(i=u(e))>0&&a.length>i&&!a.warned){a.warned=!0;var f=new Error("Possible EventEmitter memory leak detected. "+a.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");f.name="MaxListenersExceededWarning",f.emitter=e,f.type=t,f.count=a.length,s=f,console&&console.warn&&console.warn(s)}return e}function l(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function d(e,t,r){var n={fired:!1,wrapFn:void 0,target:e,type:t,listener:r},i=l.bind(n);return i.listener=r,n.wrapFn=i,i}function p(e,t,r){var n=e._events;if(void 0===n)return[];var i=n[t];return void 0===i?[]:"function"==typeof i?r?[i.listener||i]:[i]:r?function(e){for(var t=new Array(e.length),r=0;r0&&(a=t[0]),a instanceof Error)throw a;var s=new Error("Unhandled error."+(a?" ("+a.message+")":""));throw s.context=a,s}var f=i[e];if(void 0===f)return!1;if("function"==typeof f)o(f,this,t);else{var c=f.length,u=b(f,c);for(r=0;r=0;o--)if(r[o]===t||r[o].listener===t){a=r[o].listener,i=o;break}if(i<0)return this;0===i?r.shift():function(e,t){for(;t+1=0;n--)this.removeListener(e,t[n]);return this},s.prototype.listeners=function(e){return p(this,e,!0)},s.prototype.rawListeners=function(e){return p(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):y.call(e,t)},s.prototype.listenerCount=y,s.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]}},function(e,t,r){(t=e.exports=r(102)).Stream=t,t.Readable=t,t.Writable=r(69),t.Duplex=r(25),t.Transform=r(106),t.PassThrough=r(203)},function(e,t,r){"use strict";(function(t,n,i){var o=r(46);function a(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,r){var n=e.entry;for(e.entry=null;n;){var i=n.callback;t.pendingcb--,i(void 0),n=n.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=g;var s,f=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?n:o.nextTick;g.WritableState=v;var c=Object.create(r(38));c.inherits=r(2);var u,h={deprecate:r(202)},l=r(103),d=r(0).Buffer,p=i.Uint8Array||function(){},y=r(104);function b(){}function v(e,t){s=s||r(25),e=e||{};var n=t instanceof s;this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var i=e.highWaterMark,c=e.writableHighWaterMark,u=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(c||0===c)?c:u,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var h=!1===e.decodeStrings;this.decodeStrings=!h,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var r=e._writableState,n=r.sync,i=r.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(r),t)!function(e,t,r,n,i){--t.pendingcb,r?(o.nextTick(i,n),o.nextTick(A,e,t),e._writableState.errorEmitted=!0,e.emit("error",n)):(i(n),e._writableState.errorEmitted=!0,e.emit("error",n),A(e,t))}(e,r,n,t,i);else{var a=E(r);a||r.corked||r.bufferProcessing||!r.bufferedRequest||_(e,r),n?f(w,e,r,a,i):w(e,r,a,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new a(this)}function g(e){if(s=s||r(25),!(u.call(g,this)||this instanceof s))return new g(e);this._writableState=new v(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),l.call(this)}function m(e,t,r,n,i,o,a){t.writelen=n,t.writecb=a,t.writing=!0,t.sync=!0,r?e._writev(i,t.onwrite):e._write(i,o,t.onwrite),t.sync=!1}function w(e,t,r,n){r||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,n(),A(e,t)}function _(e,t){t.bufferProcessing=!0;var r=t.bufferedRequest;if(e._writev&&r&&r.next){var n=t.bufferedRequestCount,i=new Array(n),o=t.corkedRequestsFree;o.entry=r;for(var s=0,f=!0;r;)i[s]=r,r.isBuf||(f=!1),r=r.next,s+=1;i.allBuffers=f,m(e,t,!0,t.length,i,"",o.finish),t.pendingcb++,t.lastBufferedRequest=null,o.next?(t.corkedRequestsFree=o.next,o.next=null):t.corkedRequestsFree=new a(t),t.bufferedRequestCount=0}else{for(;r;){var c=r.chunk,u=r.encoding,h=r.callback;if(m(e,t,!1,t.objectMode?1:c.length,c,u,h),r=r.next,t.bufferedRequestCount--,t.writing)break}null===r&&(t.lastBufferedRequest=null)}t.bufferedRequest=r,t.bufferProcessing=!1}function E(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function S(e,t){e._final((function(r){t.pendingcb--,r&&e.emit("error",r),t.prefinished=!0,e.emit("prefinish"),A(e,t)}))}function A(e,t){var r=E(t);return r&&(function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,o.nextTick(S,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),r}c.inherits(g,l),v.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(v.prototype,"buffer",{get:h.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(u=Function.prototype[Symbol.hasInstance],Object.defineProperty(g,Symbol.hasInstance,{value:function(e){return!!u.call(this,e)||this===g&&e&&e._writableState instanceof v}})):u=function(e){return e instanceof this},g.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},g.prototype.write=function(e,t,r){var n,i=this._writableState,a=!1,s=!i.objectMode&&(n=e,d.isBuffer(n)||n instanceof p);return s&&!d.isBuffer(e)&&(e=function(e){return d.from(e)}(e)),"function"==typeof t&&(r=t,t=null),s?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof r&&(r=b),i.ended?function(e,t){var r=new Error("write after end");e.emit("error",r),o.nextTick(t,r)}(this,r):(s||function(e,t,r,n){var i=!0,a=!1;return null===r?a=new TypeError("May not write null values to stream"):"string"==typeof r||void 0===r||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),o.nextTick(n,a),i=!1),i}(this,i,e,r))&&(i.pendingcb++,a=function(e,t,r,n,i,o){if(!r){var a=function(e,t,r){return e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=d.from(t,r)),t}(t,n,i);n!==a&&(r=!0,i="buffer",n=a)}var s=t.objectMode?1:n.length;t.length+=s;var f=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(g.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),g.prototype._write=function(e,t,r){r(new Error("_write() is not implemented"))},g.prototype._writev=null,g.prototype.end=function(e,t,r){var n=this._writableState;"function"==typeof e?(r=e,e=null,t=null):"function"==typeof t&&(r=t,t=null),null!=e&&this.write(e,t),n.corked&&(n.corked=1,this.uncork()),n.ending||n.finished||function(e,t,r){t.ending=!0,A(e,t),r&&(t.finished?o.nextTick(r):e.once("finish",r)),t.ended=!0,e.writable=!1}(this,n,r)},Object.defineProperty(g.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),g.prototype.destroy=y.destroy,g.prototype._undestroy=y.undestroy,g.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,r(16),r(105).setImmediate,r(10))},function(e,t,r){"use strict";var n=r(0).Buffer,i=n.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function o(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(n.isEncoding===i||!i(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=f,this.end=c,t=4;break;case"utf8":this.fillLast=s,t=4;break;case"base64":this.text=u,this.end=h,t=3;break;default:return this.write=l,void(this.end=d)}this.lastNeed=0,this.lastTotal=0,this.lastChar=n.allocUnsafe(t)}function a(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e){var t=this.lastTotal-this.lastNeed,r=function(e,t,r){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==r?r:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function f(e,t){if((e.length-t)%2==0){var r=e.toString("utf16le",t);if(r){var n=r.charCodeAt(r.length-1);if(n>=55296&&n<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function c(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,r)}return t}function u(e,t){var r=(e.length-t)%3;return 0===r?e.toString("base64",t):(this.lastNeed=3-r,this.lastTotal=3,1===r?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-r))}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function l(e){return e.toString(this.encoding)}function d(e){return e&&e.length?this.write(e):""}t.StringDecoder=o,o.prototype.write=function(e){if(0===e.length)return"";var t,r;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r=0?(i>0&&(e.lastNeed=i-1),i):--n=0?(i>0&&(e.lastNeed=i-2),i):--n=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=r;var n=e.length-(r-this.lastNeed);return e.copy(this.lastChar,0,n),e.toString("utf8",t,n)},o.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,r){"use strict";var n=r(3).Buffer,i=r(2),o=r(101),a=new Array(16),s=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],f=[5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11],c=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],u=[8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11],h=[0,1518500249,1859775393,2400959708,2840853838],l=[1352829926,1548603684,1836072691,2053994217,0];function d(){o.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520}function p(e,t){return e<>>32-t}function y(e,t,r,n,i,o,a,s){return p(e+(t^r^n)+o+a|0,s)+i|0}function b(e,t,r,n,i,o,a,s){return p(e+(t&r|~t&n)+o+a|0,s)+i|0}function v(e,t,r,n,i,o,a,s){return p(e+((t|~r)^n)+o+a|0,s)+i|0}function g(e,t,r,n,i,o,a,s){return p(e+(t&n|r&~n)+o+a|0,s)+i|0}function m(e,t,r,n,i,o,a,s){return p(e+(t^(r|~n))+o+a|0,s)+i|0}i(d,o),d.prototype._update=function(){for(var e=a,t=0;t<16;++t)e[t]=this._block.readInt32LE(4*t);for(var r=0|this._a,n=0|this._b,i=0|this._c,o=0|this._d,d=0|this._e,w=0|this._a,_=0|this._b,E=0|this._c,S=0|this._d,A=0|this._e,k=0;k<80;k+=1){var O,x;k<16?(O=y(r,n,i,o,d,e[s[k]],h[0],c[k]),x=m(w,_,E,S,A,e[f[k]],l[0],u[k])):k<32?(O=b(r,n,i,o,d,e[s[k]],h[1],c[k]),x=g(w,_,E,S,A,e[f[k]],l[1],u[k])):k<48?(O=v(r,n,i,o,d,e[s[k]],h[2],c[k]),x=v(w,_,E,S,A,e[f[k]],l[2],u[k])):k<64?(O=g(r,n,i,o,d,e[s[k]],h[3],c[k]),x=b(w,_,E,S,A,e[f[k]],l[3],u[k])):(O=m(r,n,i,o,d,e[s[k]],h[4],c[k]),x=y(w,_,E,S,A,e[f[k]],l[4],u[k])),r=d,d=o,o=p(i,10),i=n,n=O,w=A,A=S,S=p(E,10),E=_,_=x}var T=this._b+i+S|0;this._b=this._c+o+A|0,this._c=this._d+d+w|0,this._d=this._e+r+_|0,this._e=this._a+n+E|0,this._a=T},d.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var e=n.alloc?n.alloc(20):new n(20);return e.writeInt32LE(this._a,0),e.writeInt32LE(this._b,4),e.writeInt32LE(this._c,8),e.writeInt32LE(this._d,12),e.writeInt32LE(this._e,16),e},e.exports=d},function(e,t,r){(t=e.exports=function(e){e=e.toLowerCase();var r=t[e];if(!r)throw new Error(e+" is not supported (we accept pull requests)");return new r}).sha=r(208),t.sha1=r(209),t.sha224=r(210),t.sha256=r(107),t.sha384=r(211),t.sha512=r(108)},function(e,t,r){t.pbkdf2=r(212),t.pbkdf2Sync=r(111)},function(e,t,r){"use strict";var n=r(12);function i(e){this.options=e,this.type=this.options.type,this.blockSize=8,this._init(),this.buffer=new Array(this.blockSize),this.bufferOff=0}e.exports=i,i.prototype._init=function(){},i.prototype.update=function(e){return 0===e.length?[]:"decrypt"===this.type?this._updateDecrypt(e):this._updateEncrypt(e)},i.prototype._buffer=function(e,t){for(var r=Math.min(this.buffer.length-this.bufferOff,e.length-t),n=0;n0;n--)t+=this._buffer(e,t),r+=this._flushBuffer(i,r);return t+=this._buffer(e,t),i},i.prototype.final=function(e){var t,r;return e&&(t=this.update(e)),r="encrypt"===this.type?this._finalEncrypt():this._finalDecrypt(),t?t.concat(r):r},i.prototype._pad=function(e,t){if(0===t)return!1;for(;t=0||!r.umod(e.prime1)||!r.umod(e.prime2);)r=new n(i(t));return r}e.exports=o,o.getr=a}).call(this,r(3).Buffer)},function(e,t,r){"use strict";(function(e){r.d(t,"a",(function(){return g}));var n=r(4),i=r.n(n),o=r(51),a=r.n(o),s=r(8),f=r.n(s),c=r(9),u=r.n(c),h=r(30),l=r(5),d=r(144),p=r(1);function y(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function b(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,i=(r||[]).filter((function(e){return e.Address===t.address&&n===e.Name}));return 0===i.length?[]:i.map((function(r){var n,i=r.Name,o=r.NonIndexed,s=r.Indexed,f=!0,c=!1,u=void 0;try{for(var h,d=t.services[Symbol.iterator]();!(f=(h=d.next()).done);f=!0){var p=h.value;try{n=p.lookupType(i);break}catch(e){}}}catch(e){c=!0,u=e}finally{try{f||null==d.return||d.return()}finally{if(c)throw u}}var y=a()(s||[]);o&&y.push(o);var v=y.reduce((function(t,r){var i=n.decode(e.from(r,"base64"));return b({},t,{},i=n.toObject(i,{enums:String,longs:String,bytes:String,defaults:!1,arrays:!0,objects:!0,oneofs:!0}))}),{});return v=Object(l.transform)(n,v,l.OUTPUT_TRANSFORMERS),Object(l.transformArrayToMap)(n,v)}))}}]),t}(),g=function(){function e(t,r,n){var i,o;f()(this,e),this.chain=t,this.services=(i=r,o=h.Root.fromDescriptor(i,"proto3").resolveAll(),i.file.filter((function(e){return e.service.length>0})).map((function(e){var t=e.service[0].name,r=e.package?"".concat(e.package,".").concat(t):t;return o.lookupService(r)}))),this.wallet=n}return u()(e,[{key:"at",value:function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p.noop,n=new v(this.chain,this.services,t);return e.bindMethodsToContract(n,this.wallet),r(null,n),n}}],[{key:"bindMethodsToContract",value:function(e,t){e.services.forEach((function(r){Object.keys(r.methods).forEach((function(n){var i=r.methods[n].resolve();new d.a(e._chain,i,e.address,t).bindMethodToContract(e)}))}))}}]),e}()}).call(this,r(3).Buffer)},function(e,t,r){var n;e.exports=(n=r(23),r(264),r(265),r(135),r(268),function(){var e=n,t=e.lib.BlockCipher,r=e.algo,i=[],o=[],a=[],s=[],f=[],c=[],u=[],h=[],l=[],d=[];!function(){for(var e=[],t=0;t<256;t++)e[t]=t<128?t<<1:t<<1^283;var r=0,n=0;for(t=0;t<256;t++){var p=n^n<<1^n<<2^n<<3^n<<4;p=p>>>8^255&p^99,i[r]=p,o[p]=r;var y=e[r],b=e[y],v=e[b],g=257*e[p]^16843008*p;a[r]=g<<24|g>>>8,s[r]=g<<16|g>>>16,f[r]=g<<8|g>>>24,c[r]=g,g=16843009*v^65537*b^257*y^16843008*r,u[p]=g<<24|g>>>8,h[p]=g<<16|g>>>16,l[p]=g<<8|g>>>24,d[p]=g,r?(r=y^e[e[e[v^y]]],n^=e[e[n]]):r=n=1}}();var p=[0,1,2,4,8,16,32,64,128,27,54],y=r.AES=t.extend({_doReset:function(){if(!this._nRounds||this._keyPriorReset!==this._key){for(var e=this._keyPriorReset=this._key,t=e.words,r=e.sigBytes/4,n=4*((this._nRounds=r+6)+1),o=this._keySchedule=[],a=0;a6&&a%r==4&&(s=i[s>>>24]<<24|i[s>>>16&255]<<16|i[s>>>8&255]<<8|i[255&s]):(s=i[(s=s<<8|s>>>24)>>>24]<<24|i[s>>>16&255]<<16|i[s>>>8&255]<<8|i[255&s],s^=p[a/r|0]<<24),o[a]=o[a-r]^s}for(var f=this._invKeySchedule=[],c=0;c>>24]]^h[i[s>>>16&255]]^l[i[s>>>8&255]]^d[i[255&s]]}},encryptBlock:function(e,t){this._doCryptBlock(e,t,this._keySchedule,a,s,f,c,i)},decryptBlock:function(e,t){var r=e[t+1];e[t+1]=e[t+3],e[t+3]=r,this._doCryptBlock(e,t,this._invKeySchedule,u,h,l,d,o),r=e[t+1],e[t+1]=e[t+3],e[t+3]=r},_doCryptBlock:function(e,t,r,n,i,o,a,s){for(var f=this._nRounds,c=e[t]^r[0],u=e[t+1]^r[1],h=e[t+2]^r[2],l=e[t+3]^r[3],d=4,p=1;p>>24]^i[u>>>16&255]^o[h>>>8&255]^a[255&l]^r[d++],b=n[u>>>24]^i[h>>>16&255]^o[l>>>8&255]^a[255&c]^r[d++],v=n[h>>>24]^i[l>>>16&255]^o[c>>>8&255]^a[255&u]^r[d++],g=n[l>>>24]^i[c>>>16&255]^o[u>>>8&255]^a[255&h]^r[d++];c=y,u=b,h=v,l=g}y=(s[c>>>24]<<24|s[u>>>16&255]<<16|s[h>>>8&255]<<8|s[255&l])^r[d++],b=(s[u>>>24]<<24|s[h>>>16&255]<<16|s[l>>>8&255]<<8|s[255&c])^r[d++],v=(s[h>>>24]<<24|s[l>>>16&255]<<16|s[c>>>8&255]<<8|s[255&u])^r[d++],g=(s[l>>>24]<<24|s[c>>>16&255]<<16|s[u>>>8&255]<<8|s[255&h])^r[d++],e[t]=y,e[t+1]=b,e[t+2]=v,e[t+3]=g},keySize:8});e.AES=t._createHelper(y)}(),n.AES)},function(e,t,r){const n=r(269);n.async=r(270),e.exports=n},function(e,t,r){"use strict";r.d(t,"a",(function(){return c}));var n="0123456789abcdef".split(""),i=[1,256,65536,16777216],o=[0,8,16,24],a=[1,0,32898,0,32906,2147483648,2147516416,2147483648,32907,0,2147483649,0,2147516545,2147483648,32777,2147483648,138,0,136,0,2147516425,0,2147483658,0,2147516555,0,139,2147483648,32905,2147483648,32771,2147483648,32770,2147483648,128,2147483648,32778,0,2147483658,2147483648,2147516545,2147483648,32896,2147483648,2147483649,0,2147516424,2147483648],s=function(e){var t,r,n,i,o,s,f,c,u,h,l,d,p,y,b,v,g,m,w,_,E,S,A,k,O,x,T,I,M,R,P,j,B,C,N,L,D,U,F,H,q,z,K,V,Y,G,W,X,J,$,Z,Q,ee,te,re,ne,ie,oe,ae,se,fe,ce,ue;for(n=0;n<48;n+=2)i=e[0]^e[10]^e[20]^e[30]^e[40],o=e[1]^e[11]^e[21]^e[31]^e[41],s=e[2]^e[12]^e[22]^e[32]^e[42],f=e[3]^e[13]^e[23]^e[33]^e[43],c=e[4]^e[14]^e[24]^e[34]^e[44],u=e[5]^e[15]^e[25]^e[35]^e[45],h=e[6]^e[16]^e[26]^e[36]^e[46],l=e[7]^e[17]^e[27]^e[37]^e[47],t=(d=e[8]^e[18]^e[28]^e[38]^e[48])^(s<<1|f>>>31),r=(p=e[9]^e[19]^e[29]^e[39]^e[49])^(f<<1|s>>>31),e[0]^=t,e[1]^=r,e[10]^=t,e[11]^=r,e[20]^=t,e[21]^=r,e[30]^=t,e[31]^=r,e[40]^=t,e[41]^=r,t=i^(c<<1|u>>>31),r=o^(u<<1|c>>>31),e[2]^=t,e[3]^=r,e[12]^=t,e[13]^=r,e[22]^=t,e[23]^=r,e[32]^=t,e[33]^=r,e[42]^=t,e[43]^=r,t=s^(h<<1|l>>>31),r=f^(l<<1|h>>>31),e[4]^=t,e[5]^=r,e[14]^=t,e[15]^=r,e[24]^=t,e[25]^=r,e[34]^=t,e[35]^=r,e[44]^=t,e[45]^=r,t=c^(d<<1|p>>>31),r=u^(p<<1|d>>>31),e[6]^=t,e[7]^=r,e[16]^=t,e[17]^=r,e[26]^=t,e[27]^=r,e[36]^=t,e[37]^=r,e[46]^=t,e[47]^=r,t=h^(i<<1|o>>>31),r=l^(o<<1|i>>>31),e[8]^=t,e[9]^=r,e[18]^=t,e[19]^=r,e[28]^=t,e[29]^=r,e[38]^=t,e[39]^=r,e[48]^=t,e[49]^=r,y=e[0],b=e[1],G=e[11]<<4|e[10]>>>28,W=e[10]<<4|e[11]>>>28,I=e[20]<<3|e[21]>>>29,M=e[21]<<3|e[20]>>>29,se=e[31]<<9|e[30]>>>23,fe=e[30]<<9|e[31]>>>23,z=e[40]<<18|e[41]>>>14,K=e[41]<<18|e[40]>>>14,C=e[2]<<1|e[3]>>>31,N=e[3]<<1|e[2]>>>31,v=e[13]<<12|e[12]>>>20,g=e[12]<<12|e[13]>>>20,X=e[22]<<10|e[23]>>>22,J=e[23]<<10|e[22]>>>22,R=e[33]<<13|e[32]>>>19,P=e[32]<<13|e[33]>>>19,ce=e[42]<<2|e[43]>>>30,ue=e[43]<<2|e[42]>>>30,te=e[5]<<30|e[4]>>>2,re=e[4]<<30|e[5]>>>2,L=e[14]<<6|e[15]>>>26,D=e[15]<<6|e[14]>>>26,m=e[25]<<11|e[24]>>>21,w=e[24]<<11|e[25]>>>21,$=e[34]<<15|e[35]>>>17,Z=e[35]<<15|e[34]>>>17,j=e[45]<<29|e[44]>>>3,B=e[44]<<29|e[45]>>>3,k=e[6]<<28|e[7]>>>4,O=e[7]<<28|e[6]>>>4,ne=e[17]<<23|e[16]>>>9,ie=e[16]<<23|e[17]>>>9,U=e[26]<<25|e[27]>>>7,F=e[27]<<25|e[26]>>>7,_=e[36]<<21|e[37]>>>11,E=e[37]<<21|e[36]>>>11,Q=e[47]<<24|e[46]>>>8,ee=e[46]<<24|e[47]>>>8,V=e[8]<<27|e[9]>>>5,Y=e[9]<<27|e[8]>>>5,x=e[18]<<20|e[19]>>>12,T=e[19]<<20|e[18]>>>12,oe=e[29]<<7|e[28]>>>25,ae=e[28]<<7|e[29]>>>25,H=e[38]<<8|e[39]>>>24,q=e[39]<<8|e[38]>>>24,S=e[48]<<14|e[49]>>>18,A=e[49]<<14|e[48]>>>18,e[0]=y^~v&m,e[1]=b^~g&w,e[10]=k^~x&I,e[11]=O^~T&M,e[20]=C^~L&U,e[21]=N^~D&F,e[30]=V^~G&X,e[31]=Y^~W&J,e[40]=te^~ne&oe,e[41]=re^~ie&ae,e[2]=v^~m&_,e[3]=g^~w&E,e[12]=x^~I&R,e[13]=T^~M&P,e[22]=L^~U&H,e[23]=D^~F&q,e[32]=G^~X&$,e[33]=W^~J&Z,e[42]=ne^~oe&se,e[43]=ie^~ae&fe,e[4]=m^~_&S,e[5]=w^~E&A,e[14]=I^~R&j,e[15]=M^~P&B,e[24]=U^~H&z,e[25]=F^~q&K,e[34]=X^~$&Q,e[35]=J^~Z&ee,e[44]=oe^~se&ce,e[45]=ae^~fe&ue,e[6]=_^~S&y,e[7]=E^~A&b,e[16]=R^~j&k,e[17]=P^~B&O,e[26]=H^~z&C,e[27]=q^~K&N,e[36]=$^~Q&V,e[37]=Z^~ee&Y,e[46]=se^~ce&te,e[47]=fe^~ue&re,e[8]=S^~y&v,e[9]=A^~b&g,e[18]=j^~k&x,e[19]=B^~O&T,e[28]=z^~C&L,e[29]=K^~N&D,e[38]=Q^~V&G,e[39]=ee^~Y&W,e[48]=ce^~te&ne,e[49]=ue^~re&ie,e[0]^=a[n],e[1]^=a[n+1]},f=function(e){return function(t){var r;if("0x"===t.slice(0,2)){r=[];for(var a=2,f=t.length;a>2]|=t[d]<>2]|=r<>2]|=(192|r>>6)<>2]|=(128|63&r)<=57344?(f[b>>2]|=(224|r>>12)<>2]|=(128|r>>6&63)<>2]|=(128|63&r)<>2]|=(240|r>>18)<>2]|=(128|r>>12&63)<>2]|=(128|r>>6&63)<>2]|=(128|63&r)<=c){for(e.start=b-c,e.block=f[u],b=0;b>2]|=i[3&b],e.lastByteIndex===c)for(f[0]=f[u],b=1;b>4&15]+n[15&p]+n[p>>12&15]+n[p>>8&15]+n[p>>20&15]+n[p>>16&15]+n[p>>28&15]+n[p>>24&15];v%u==0&&(s(l),b=0)}return"0x"+y}(function(e){return{blocks:[],reset:!0,block:0,start:0,blockCount:1600-(e<<1)>>5,outputBlocks:e>>5,s:(t=[0,0,0,0,0,0,0,0,0,0],[].concat(t,t,t,t,t))};var t}(e),r)}},c=f(256);f(512),f(256),f(512)},function(e,t,r){e.exports=r(271)},function(e,t,r){"use strict";var n=r(273),i=r(114),o=r(274);function a(e,t){return t.encode?t.strict?n(e):encodeURIComponent(e):e}function s(e){var t=e.indexOf("?");return-1===t?"":e.slice(t+1)}function f(e,t){var r=function(e){var t;switch(e.arrayFormat){case"index":return function(e,r,n){t=/\\[(\\d*)\\]$/.exec(e),e=e.replace(/\\[\\d*\\]$/,""),t?(void 0===n[e]&&(n[e]={}),n[e][t[1]]=r):n[e]=r};case"bracket":return function(e,r,n){t=/(\\[\\])$/.exec(e),e=e.replace(/\\[\\]$/,""),t?void 0!==n[e]?n[e]=[].concat(n[e],r):n[e]=[r]:n[e]=r};default:return function(e,t,r){void 0!==r[e]?r[e]=[].concat(r[e],t):r[e]=t}}}(t=i({arrayFormat:"none"},t)),n=Object.create(null);return"string"!=typeof e?n:(e=e.trim().replace(/^[?#&]/,""))?(e.split("&").forEach((function(e){var t=e.replace(/\\+/g," ").split("="),i=t.shift(),a=t.length>0?t.join("="):void 0;a=void 0===a?null:o(a),r(o(i),a,n)})),Object.keys(n).sort().reduce((function(e,t){var r=n[t];return Boolean(r)&&"object"==typeof r&&!Array.isArray(r)?e[t]=function e(t){return Array.isArray(t)?t.sort():"object"==typeof t?e(Object.keys(t)).sort((function(e,t){return Number(e)-Number(t)})).map((function(e){return t[e]})):t}(r):e[t]=r,e}),Object.create(null))):n}t.extract=s,t.parse=f,t.stringify=function(e,t){!1===(t=i({encode:!0,strict:!0,arrayFormat:"none"},t)).sort&&(t.sort=function(){});var r=function(e){switch(e.arrayFormat){case"index":return function(t,r,n){return null===r?[a(t,e),"[",n,"]"].join(""):[a(t,e),"[",a(n,e),"]=",a(r,e)].join("")};case"bracket":return function(t,r){return null===r?a(t,e):[a(t,e),"[]=",a(r,e)].join("")};default:return function(t,r){return null===r?a(t,e):[a(t,e),"=",a(r,e)].join("")}}}(t);return e?Object.keys(e).sort(t.sort).map((function(n){var i=e[n];if(void 0===i)return"";if(null===i)return a(n,t);if(Array.isArray(i)){var o=[];return i.slice().forEach((function(e){void 0!==e&&o.push(r(n,e,o.length))})),o.join("&")}return a(n,t)+"="+a(i,t)})).filter((function(e){return e.length>0})).join("&"):""},t.parseUrl=function(e,t){return{url:e.split("?")[0]||"",query:f(s(e),t)}}},function(e,t,r){"use strict";var n=e.exports=r(148);n.build="light",n.load=function(e,t,r){return"function"==typeof t?(r=t,t=new n.Root):t||(t=new n.Root),t.load(e,r)},n.loadSync=function(e,t){return t||(t=new n.Root),t.loadSync(e)},n.encoder=r(90),n.decoder=r(91),n.verifier=r(92),n.converter=r(93),n.ReflectionObject=r(26),n.Namespace=r(36),n.Root=r(61),n.Enum=r(15),n.Type=r(56),n.Field=r(24),n.OneOf=r(44),n.MapField=r(57),n.Service=r(58),n.Method=r(59),n.Message=r(60),n.wrappers=r(94),n.types=r(27),n.util=r(7),n.ReflectionObject._configure(n.Root),n.Namespace._configure(n.Type,n.Service,n.Enum),n.Root._configure(n.Type),n.Field._configure(n.Type)},function(e,t,r){"use strict";e.exports=function(e,t){for(var r=new Array(arguments.length-1),n=0,i=2,o=!0;i>>0,8|i.mapKey[c.keyType],c.keyType),void 0===l?r("types[%i].encode(%s[ks[i]],w.uint32(18).fork()).ldelim().ldelim()",u,t):r(".uint32(%i).%s(%s[ks[i]]).ldelim()",16|l,h,t),r("}")("}")):c.repeated?(r("if(%s!=null&&%s.length){",t,t),c.packed&&void 0!==i.packed[h]?r("w.uint32(%i).fork()",(c.id<<3|2)>>>0)("for(var i=0;i<%s.length;++i)",t)("w.%s(%s[i])",h,t)("w.ldelim()"):(r("for(var i=0;i<%s.length;++i)",t),void 0===l?a(r,c,u,t+"[i]"):r("w.uint32(%i).%s(%s[i])",(c.id<<3|l)>>>0,h,t)),r("}")):(c.optional&&r("if(%s!=null&&m.hasOwnProperty(%j))",t,c.name),void 0===l?a(r,c,u,t):r("w.uint32(%i).%s(%s)",(c.id<<3|l)>>>0,h,t))}return r("return w")};var n=r(15),i=r(27),o=r(7);function a(e,t,r,n){return t.resolvedType.group?e("types[%i].encode(%s,w.uint32(%i)).uint32(%i)",r,n,(t.id<<3|3)>>>0,(t.id<<3|4)>>>0):e("types[%i].encode(%s,w.uint32(%i).fork()).ldelim()",r,n,(t.id<<3|2)>>>0)}},function(e,t,r){"use strict";e.exports=function(e){var t=o.codegen(["r","l"],e.name+"$decode")("if(!(r instanceof Reader))")("r=Reader.create(r)")("var c=l===undefined?r.len:r.pos+l,m=new this.ctor"+(e.fieldsArray.filter((function(e){return e.map})).length?",k":""))("while(r.pos>>3){");for(var r=0;r>>0",n,n);break;case"int32":case"sint32":case"sfixed32":e("m%s=d%s|0",n,n);break;case"uint64":f=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":e("if(util.Long)")("(m%s=util.Long.fromValue(d%s)).unsigned=%j",n,n,f)(\'else if(typeof d%s==="string")\',n)("m%s=parseInt(d%s,10)",n,n)(\'else if(typeof d%s==="number")\',n)("m%s=d%s",n,n)(\'else if(typeof d%s==="object")\',n)("m%s=new util.LongBits(d%s.low>>>0,d%s.high>>>0).toNumber(%s)",n,n,n,f?"true":"");break;case"bytes":e(\'if(typeof d%s==="string")\',n)("util.base64.decode(d%s,m%s=util.newBuffer(util.base64.length(d%s)),0)",n,n,n)("else if(d%s.length)",n)("m%s=d%s",n,n);break;case"string":e("m%s=String(d%s)",n,n);break;case"bool":e("m%s=Boolean(d%s)",n,n)}}return e}function s(e,t,r,n){if(t.resolvedType)t.resolvedType instanceof i?e("d%s=o.enums===String?types[%i].values[m%s]:m%s",n,r,n,n):e("d%s=types[%i].toObject(m%s,o)",n,r,n);else{var o=!1;switch(t.type){case"double":case"float":e("d%s=o.json&&!isFinite(m%s)?String(m%s):m%s",n,n,n,n);break;case"uint64":o=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":e(\'if(typeof m%s==="number")\',n)("d%s=o.longs===String?String(m%s):m%s",n,n,n)("else")("d%s=o.longs===String?util.Long.prototype.toString.call(m%s):o.longs===Number?new util.LongBits(m%s.low>>>0,m%s.high>>>0).toNumber(%s):m%s",n,n,n,n,o?"true":"",n);break;case"bytes":e("d%s=o.bytes===String?util.base64.encode(m%s,0,m%s.length):o.bytes===Array?Array.prototype.slice.call(m%s):m%s",n,n,n,n,n);break;default:e("d%s=m%s",n,n)}}return e}n.fromObject=function(e){var t=e.fieldsArray,r=o.codegen(["d"],e.name+"$fromObject")("if(d instanceof this.ctor)")("return d");if(!t.length)return r("return new this.ctor");r("var m=new this.ctor");for(var n=0;n]/g,i=/(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)")/g,o=/(?:\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')/g,a=/^ *[*/]+ */,s=/^\\s*\\*?\\/*/,f=/\\n/g,c=/\\s/,u=/\\\\(.?)/g,h={0:"\\0",r:"\\r",n:"\\n",t:"\\t"};function l(e){return e.replace(u,(function(e,t){switch(t){case"\\\\":case"":return t;default:return h[t]||""}}))}function d(e,t){e=e.toString();var r=0,u=e.length,h=1,d=null,p=null,y=0,b=!1,v=[],g=null;function m(e){return Error("illegal "+e+" (line "+h+")")}function w(t){return e.charAt(t)}function _(r,n){d=e.charAt(r++),y=h,b=!1;var i,o=r-(t?2:3);do{if(--o<0||"\\n"===(i=e.charAt(o))){b=!0;break}}while(" "===i||"\\t"===i);for(var c=e.substring(r,n).split(f),u=0;u0)return v.shift();if(g)return function(){var t="\'"===g?o:i;t.lastIndex=r-1;var n=t.exec(e);if(!n)throw m("string");return r=t.lastIndex,k(g),g=null,l(n[1])}();var a,s,f,d,p;do{if(r===u)return null;for(a=!1;c.test(f=w(r));)if("\\n"===f&&++h,++r===u)return null;if("/"===w(r)){if(++r===u)throw m("comment");if("/"===w(r))if(t){if(d=r,p=!1,E(r)){p=!0;do{if((r=S(r))===u)break;r++}while(E(r))}else r=Math.min(u,S(r)+1);p&&_(d,r),h++,a=!0}else{for(p="/"===w(d=r+1);"\\n"!==w(++r);)if(r===u)return null;++r,p&&_(d,r-1),++h,a=!0}else{if("*"!==(f=w(r)))return"/";d=r+1,p=t||"*"===w(d);do{if("\\n"===f&&++h,++r===u)throw m("comment");s=f,f=w(r)}while("*"!==s||"/"!==f);++r,p&&_(d,r-2),a=!0}}}while(a);var y=r;if(n.lastIndex=0,!n.test(w(y++)))for(;y>8,a=255&i;o?r.push(o,a):r.push(a)}return r},n.zero2=i,n.toHex=o,n.encode=function(e,t){return"hex"===t?o(e):e}},function(e,t,r){"use strict";var n=t;n.base=r(45),n.short=r(181),n.mont=r(182),n.edwards=r(183)},function(e,t,r){"use strict";var n=r(17).rotr32;function i(e,t,r){return e&t^~e&r}function o(e,t,r){return e&t^e&r^t&r}function a(e,t,r){return e^t^r}t.ft_1=function(e,t,r,n){return 0===e?i(t,r,n):1===e||3===e?a(t,r,n):2===e?o(t,r,n):void 0},t.ch32=i,t.maj32=o,t.p32=a,t.s0_256=function(e){return n(e,2)^n(e,13)^n(e,22)},t.s1_256=function(e){return n(e,6)^n(e,11)^n(e,25)},t.g0_256=function(e){return n(e,7)^n(e,18)^e>>>3},t.g1_256=function(e){return n(e,17)^n(e,19)^e>>>10}},function(e,t,r){"use strict";var n=r(17),i=r(37),o=r(98),a=r(12),s=n.sum32,f=n.sum32_4,c=n.sum32_5,u=o.ch32,h=o.maj32,l=o.s0_256,d=o.s1_256,p=o.g0_256,y=o.g1_256,b=i.BlockHash,v=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function g(){if(!(this instanceof g))return new g;b.call(this),this.h=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],this.k=v,this.W=new Array(64)}n.inherits(g,b),e.exports=g,g.blockSize=512,g.outSize=256,g.hmacStrength=192,g.padLength=64,g.prototype._update=function(e,t){for(var r=this.W,n=0;n<16;n++)r[n]=e[t+n];for(;n=this._blockSize;){for(var o=this._blockOffset;o0;++a)this._length[a]+=s,(s=this._length[a]/4294967296|0)>0&&(this._length[a]-=4294967296*s);return this},o.prototype._update=function(){throw new Error("_update is not implemented")},o.prototype.digest=function(e){if(this._finalized)throw new Error("Digest already called");this._finalized=!0;var t=this._digest();void 0!==e&&(t=t.toString(e)),this._block.fill(0),this._blockOffset=0;for(var r=0;r<4;++r)this._length[r]=0;return t},o.prototype._digest=function(){throw new Error("_digest is not implemented")},e.exports=o},function(e,t,r){"use strict";(function(t,n){var i=r(46);e.exports=m;var o,a=r(87);m.ReadableState=g,r(67).EventEmitter;var s=function(e,t){return e.listeners(t).length},f=r(103),c=r(0).Buffer,u=t.Uint8Array||function(){},h=Object.create(r(38));h.inherits=r(2);var l=r(198),d=void 0;d=l&&l.debuglog?l.debuglog("stream"):function(){};var p,y=r(199),b=r(104);h.inherits(m,f);var v=["error","close","destroy","pause","resume"];function g(e,t){e=e||{};var n=t instanceof(o=o||r(25));this.objectMode=!!e.objectMode,n&&(this.objectMode=this.objectMode||!!e.readableObjectMode);var i=e.highWaterMark,a=e.readableHighWaterMark,s=this.objectMode?16:16384;this.highWaterMark=i||0===i?i:n&&(a||0===a)?a:s,this.highWaterMark=Math.floor(this.highWaterMark),this.buffer=new y,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(p||(p=r(70).StringDecoder),this.decoder=new p(e.encoding),this.encoding=e.encoding)}function m(e){if(o=o||r(25),!(this instanceof m))return new m(e);this._readableState=new g(e,this),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),f.call(this)}function w(e,t,r,n,i){var o,a=e._readableState;return null===t?(a.reading=!1,function(e,t){if(!t.ended){if(t.decoder){var r=t.decoder.end();r&&r.length&&(t.buffer.push(r),t.length+=t.objectMode?1:r.length)}t.ended=!0,S(e)}}(e,a)):(i||(o=function(e,t){var r,n;return n=t,c.isBuffer(n)||n instanceof u||"string"==typeof t||void 0===t||e.objectMode||(r=new TypeError("Invalid non-string/buffer chunk")),r}(a,t)),o?e.emit("error",o):a.objectMode||t&&t.length>0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===c.prototype||(t=function(e){return c.from(e)}(t)),n?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):_(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!r?(t=a.decoder.write(t),a.objectMode||0!==t.length?_(e,a,t,!1):k(e,a)):_(e,a,t,!1))):n||(a.reading=!1)),function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=8388608?e=8388608:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function S(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(d("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?i.nextTick(A,e):A(e))}function A(e){d("emit readable"),e.emit("readable"),I(e)}function k(e,t){t.readingMore||(t.readingMore=!0,i.nextTick(O,e,t))}function O(e,t){for(var r=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(r=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):r=function(e,t,r){var n;return eo.length?o.length:e;if(a===o.length?i+=o:i+=o.slice(0,e),0==(e-=a)){a===o.length?(++n,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=o.slice(a));break}++n}return t.length-=n,i}(e,t):function(e,t){var r=c.allocUnsafe(e),n=t.head,i=1;for(n.data.copy(r),e-=n.data.length;n=n.next;){var o=n.data,a=e>o.length?o.length:e;if(o.copy(r,r.length-e,0,a),0==(e-=a)){a===o.length?(++i,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=o.slice(a));break}++i}return t.length-=i,r}(e,t),n}(e,t.buffer,t.decoder),r);var r}function R(e){var t=e._readableState;if(t.length>0)throw new Error(\'"endReadable()" called on non-empty stream\');t.endEmitted||(t.ended=!0,i.nextTick(P,t,e))}function P(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function j(e,t){for(var r=0,n=e.length;r=t.highWaterMark||t.ended))return d("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?R(this):S(this),null;if(0===(e=E(e,t))&&t.ended)return 0===t.length&&R(this),null;var n,i=t.needReadable;return d("need readable",i),(0===t.length||t.length-e0?M(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),r!==e&&t.ended&&R(this)),null!==n&&this.emit("data",n),n},m.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},m.prototype.pipe=function(e,t){var r=this,o=this._readableState;switch(o.pipesCount){case 0:o.pipes=e;break;case 1:o.pipes=[o.pipes,e];break;default:o.pipes.push(e)}o.pipesCount+=1,d("pipe count=%d opts=%j",o.pipesCount,t);var f=t&&!1===t.end||e===n.stdout||e===n.stderr?g:c;function c(){d("onend"),e.end()}o.endEmitted?i.nextTick(f):r.once("end",f),e.on("unpipe",(function t(n,i){d("onunpipe"),n===r&&i&&!1===i.hasUnpiped&&(i.hasUnpiped=!0,d("cleanup"),e.removeListener("close",b),e.removeListener("finish",v),e.removeListener("drain",u),e.removeListener("error",y),e.removeListener("unpipe",t),r.removeListener("end",c),r.removeListener("end",g),r.removeListener("data",p),h=!0,!o.awaitDrain||e._writableState&&!e._writableState.needDrain||u())}));var u=function(e){return function(){var t=e._readableState;d("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&s(e,"data")&&(t.flowing=!0,I(e))}}(r);e.on("drain",u);var h=!1,l=!1;function p(t){d("ondata"),l=!1,!1!==e.write(t)||l||((1===o.pipesCount&&o.pipes===e||o.pipesCount>1&&-1!==j(o.pipes,e))&&!h&&(d("false write response, pause",r._readableState.awaitDrain),r._readableState.awaitDrain++,l=!0),r.pause())}function y(t){d("onerror",t),g(),e.removeListener("error",y),0===s(e,"error")&&e.emit("error",t)}function b(){e.removeListener("finish",v),g()}function v(){d("onfinish"),e.removeListener("close",b),g()}function g(){d("unpipe"),r.unpipe(e)}return r.on("data",p),function(e,t,r){if("function"==typeof e.prependListener)return e.prependListener(t,r);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(r):e._events[t]=[r,e._events[t]]:e.on(t,r)}(e,"error",y),e.once("close",b),e.once("finish",v),e.emit("pipe",r),o.flowing||(d("pipe resume"),r.resume()),e},m.prototype.unpipe=function(e){var t=this._readableState,r={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,r)),this;if(!e){var n=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var o=0;o=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},r(201),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,r(10))},function(e,t,r){"use strict";e.exports=a;var n=r(25),i=Object.create(r(38));function o(e,t){var r=this._transformState;r.transforming=!1;var n=r.writecb;if(!n)return this.emit("error",new Error("write callback called multiple times"));r.writechunk=null,r.writecb=null,null!=t&&this.push(t),n(e);var i=this._readableState;i.reading=!1,(i.needReadable||i.length>>2|e<<30)^(e>>>13|e<<19)^(e>>>22|e<<10)}function l(e){return(e>>>6|e<<26)^(e>>>11|e<<21)^(e>>>25|e<<7)}function d(e){return(e>>>7|e<<25)^(e>>>18|e<<14)^e>>>3}n(f,i),f.prototype.init=function(){return this._a=1779033703,this._b=3144134277,this._c=1013904242,this._d=2773480762,this._e=1359893119,this._f=2600822924,this._g=528734635,this._h=1541459225,this},f.prototype._update=function(e){for(var t,r=this._w,n=0|this._a,i=0|this._b,o=0|this._c,s=0|this._d,f=0|this._e,p=0|this._f,y=0|this._g,b=0|this._h,v=0;v<16;++v)r[v]=e.readInt32BE(4*v);for(;v<64;++v)r[v]=0|(((t=r[v-2])>>>17|t<<15)^(t>>>19|t<<13)^t>>>10)+r[v-7]+d(r[v-15])+r[v-16];for(var g=0;g<64;++g){var m=b+l(f)+c(f,p,y)+a[g]+r[g]|0,w=h(n)+u(n,i,o)|0;b=y,y=p,p=f,f=s+m|0,s=o,o=i,i=n,n=m+w|0}this._a=n+this._a|0,this._b=i+this._b|0,this._c=o+this._c|0,this._d=s+this._d|0,this._e=f+this._e|0,this._f=p+this._f|0,this._g=y+this._g|0,this._h=b+this._h|0},f.prototype._hash=function(){var e=o.allocUnsafe(32);return e.writeInt32BE(this._a,0),e.writeInt32BE(this._b,4),e.writeInt32BE(this._c,8),e.writeInt32BE(this._d,12),e.writeInt32BE(this._e,16),e.writeInt32BE(this._f,20),e.writeInt32BE(this._g,24),e.writeInt32BE(this._h,28),e},e.exports=f},function(e,t,r){var n=r(2),i=r(28),o=r(0).Buffer,a=[1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591],s=new Array(160);function f(){this.init(),this._w=s,i.call(this,128,112)}function c(e,t,r){return r^e&(t^r)}function u(e,t,r){return e&t|r&(e|t)}function h(e,t){return(e>>>28|t<<4)^(t>>>2|e<<30)^(t>>>7|e<<25)}function l(e,t){return(e>>>14|t<<18)^(e>>>18|t<<14)^(t>>>9|e<<23)}function d(e,t){return(e>>>1|t<<31)^(e>>>8|t<<24)^e>>>7}function p(e,t){return(e>>>1|t<<31)^(e>>>8|t<<24)^(e>>>7|t<<25)}function y(e,t){return(e>>>19|t<<13)^(t>>>29|e<<3)^e>>>6}function b(e,t){return(e>>>19|t<<13)^(t>>>29|e<<3)^(e>>>6|t<<26)}function v(e,t){return e>>>0>>0?1:0}n(f,i),f.prototype.init=function(){return this._ah=1779033703,this._bh=3144134277,this._ch=1013904242,this._dh=2773480762,this._eh=1359893119,this._fh=2600822924,this._gh=528734635,this._hh=1541459225,this._al=4089235720,this._bl=2227873595,this._cl=4271175723,this._dl=1595750129,this._el=2917565137,this._fl=725511199,this._gl=4215389547,this._hl=327033209,this},f.prototype._update=function(e){for(var t=this._w,r=0|this._ah,n=0|this._bh,i=0|this._ch,o=0|this._dh,s=0|this._eh,f=0|this._fh,g=0|this._gh,m=0|this._hh,w=0|this._al,_=0|this._bl,E=0|this._cl,S=0|this._dl,A=0|this._el,k=0|this._fl,O=0|this._gl,x=0|this._hl,T=0;T<32;T+=2)t[T]=e.readInt32BE(4*T),t[T+1]=e.readInt32BE(4*T+4);for(;T<160;T+=2){var I=t[T-30],M=t[T-30+1],R=d(I,M),P=p(M,I),j=y(I=t[T-4],M=t[T-4+1]),B=b(M,I),C=t[T-14],N=t[T-14+1],L=t[T-32],D=t[T-32+1],U=P+N|0,F=R+C+v(U,P)|0;F=(F=F+j+v(U=U+B|0,B)|0)+L+v(U=U+D|0,D)|0,t[T]=F,t[T+1]=U}for(var H=0;H<160;H+=2){F=t[H],U=t[H+1];var q=u(r,n,i),z=u(w,_,E),K=h(r,w),V=h(w,r),Y=l(s,A),G=l(A,s),W=a[H],X=a[H+1],J=c(s,f,g),$=c(A,k,O),Z=x+G|0,Q=m+Y+v(Z,x)|0;Q=(Q=(Q=Q+J+v(Z=Z+$|0,$)|0)+W+v(Z=Z+X|0,X)|0)+F+v(Z=Z+U|0,U)|0;var ee=V+z|0,te=K+q+v(ee,V)|0;m=g,x=O,g=f,O=k,f=s,k=A,s=o+Q+v(A=S+Z|0,S)|0,o=i,S=E,i=n,E=_,n=r,_=w,r=Q+te+v(w=Z+ee|0,Z)|0}this._al=this._al+w|0,this._bl=this._bl+_|0,this._cl=this._cl+E|0,this._dl=this._dl+S|0,this._el=this._el+A|0,this._fl=this._fl+k|0,this._gl=this._gl+O|0,this._hl=this._hl+x|0,this._ah=this._ah+r+v(this._al,w)|0,this._bh=this._bh+n+v(this._bl,_)|0,this._ch=this._ch+i+v(this._cl,E)|0,this._dh=this._dh+o+v(this._dl,S)|0,this._eh=this._eh+s+v(this._el,A)|0,this._fh=this._fh+f+v(this._fl,k)|0,this._gh=this._gh+g+v(this._gl,O)|0,this._hh=this._hh+m+v(this._hl,x)|0},f.prototype._hash=function(){var e=o.allocUnsafe(64);function t(t,r,n){e.writeInt32BE(t,n),e.writeInt32BE(r,n+4)}return t(this._ah,this._al,0),t(this._bh,this._bl,8),t(this._ch,this._cl,16),t(this._dh,this._dl,24),t(this._eh,this._el,32),t(this._fh,this._fl,40),t(this._gh,this._gl,48),t(this._hh,this._hl,56),e},e.exports=f},function(e,t,r){(function(t){var r=Math.pow(2,30)-1;function n(e,r){if("string"!=typeof e&&!t.isBuffer(e))throw new TypeError(r+" must be a buffer or string")}e.exports=function(e,t,i,o){if(n(e,"Password"),n(t,"Salt"),"number"!=typeof i)throw new TypeError("Iterations not a number");if(i<0)throw new TypeError("Bad iterations");if("number"!=typeof o)throw new TypeError("Key length not a number");if(o<0||o>r||o!=o)throw new TypeError("Bad key length")}}).call(this,r(3).Buffer)},function(e,t,r){(function(t){var r;r=t.browser||parseInt(t.version.split(".")[0].slice(1),10)>=6?"utf-8":"binary",e.exports=r}).call(this,r(16))},function(e,t,r){var n=r(112),i=r(71),o=r(72),a=r(109),s=r(110),f=r(0).Buffer,c=f.alloc(128),u={md5:16,sha1:20,sha224:28,sha256:32,sha384:48,sha512:64,rmd160:20,ripemd160:20};function h(e,t,r){var a=function(e){return"rmd160"===e||"ripemd160"===e?function(e){return(new i).update(e).digest()}:"md5"===e?n:function(t){return o(e).update(t).digest()}}(e),s="sha512"===e||"sha384"===e?128:64;t.length>s?t=a(t):t.lengthr?t=("rmd160"===e?new f:c(e)).update(t).digest():t.length>>0},t.writeUInt32BE=function(e,t,r){e[0+r]=t>>>24,e[1+r]=t>>>16&255,e[2+r]=t>>>8&255,e[3+r]=255&t},t.ip=function(e,t,r,n){for(var i=0,o=0,a=6;a>=0;a-=2){for(var s=0;s<=24;s+=8)i<<=1,i|=t>>>s+a&1;for(s=0;s<=24;s+=8)i<<=1,i|=e>>>s+a&1}for(a=6;a>=0;a-=2){for(s=1;s<=25;s+=8)o<<=1,o|=t>>>s+a&1;for(s=1;s<=25;s+=8)o<<=1,o|=e>>>s+a&1}r[n+0]=i>>>0,r[n+1]=o>>>0},t.rip=function(e,t,r,n){for(var i=0,o=0,a=0;a<4;a++)for(var s=24;s>=0;s-=8)i<<=1,i|=t>>>s+a&1,i<<=1,i|=e>>>s+a&1;for(a=4;a<8;a++)for(s=24;s>=0;s-=8)o<<=1,o|=t>>>s+a&1,o<<=1,o|=e>>>s+a&1;r[n+0]=i>>>0,r[n+1]=o>>>0},t.pc1=function(e,t,r,n){for(var i=0,o=0,a=7;a>=5;a--){for(var s=0;s<=24;s+=8)i<<=1,i|=t>>s+a&1;for(s=0;s<=24;s+=8)i<<=1,i|=e>>s+a&1}for(s=0;s<=24;s+=8)i<<=1,i|=t>>s+a&1;for(a=1;a<=3;a++){for(s=0;s<=24;s+=8)o<<=1,o|=t>>s+a&1;for(s=0;s<=24;s+=8)o<<=1,o|=e>>s+a&1}for(s=0;s<=24;s+=8)o<<=1,o|=e>>s+a&1;r[n+0]=i>>>0,r[n+1]=o>>>0},t.r28shl=function(e,t){return e<>>28-t};var n=[14,11,17,4,27,23,25,0,13,22,7,18,5,9,16,24,2,20,12,21,1,8,15,26,15,4,25,19,9,1,26,16,5,11,23,8,12,7,17,0,22,3,10,14,6,20,27,24];t.pc2=function(e,t,r,i){for(var o=0,a=0,s=n.length>>>1,f=0;f>>n[f]&1;for(f=s;f>>n[f]&1;r[i+0]=o>>>0,r[i+1]=a>>>0},t.expand=function(e,t,r){var n=0,i=0;n=(1&e)<<5|e>>>27;for(var o=23;o>=15;o-=4)n<<=6,n|=e>>>o&63;for(o=11;o>=3;o-=4)i|=e>>>o&63,i<<=6;i|=(31&e)<<1|e>>>31,t[r+0]=n>>>0,t[r+1]=i>>>0};var i=[14,0,4,15,13,7,1,4,2,14,15,2,11,13,8,1,3,10,10,6,6,12,12,11,5,9,9,5,0,3,7,8,4,15,1,12,14,8,8,2,13,4,6,9,2,1,11,7,15,5,12,11,9,3,7,14,3,10,10,0,5,6,0,13,15,3,1,13,8,4,14,7,6,15,11,2,3,8,4,14,9,12,7,0,2,1,13,10,12,6,0,9,5,11,10,5,0,13,14,8,7,10,11,1,10,3,4,15,13,4,1,2,5,11,8,6,12,7,6,12,9,0,3,5,2,14,15,9,10,13,0,7,9,0,14,9,6,3,3,4,15,6,5,10,1,2,13,8,12,5,7,14,11,12,4,11,2,15,8,1,13,1,6,10,4,13,9,0,8,6,15,9,3,8,0,7,11,4,1,15,2,14,12,3,5,11,10,5,14,2,7,12,7,13,13,8,14,11,3,5,0,6,6,15,9,0,10,3,1,4,2,7,8,2,5,12,11,1,12,10,4,14,15,9,10,3,6,15,9,0,0,6,12,10,11,1,7,13,13,8,15,9,1,4,3,5,14,11,5,12,2,7,8,2,4,14,2,14,12,11,4,2,1,12,7,4,10,7,11,13,6,1,8,5,5,0,3,15,15,10,13,3,0,9,14,8,9,6,4,11,2,8,1,12,11,7,10,1,13,14,7,2,8,13,15,6,9,15,12,0,5,9,6,10,3,4,0,5,14,3,12,10,1,15,10,4,15,2,9,7,2,12,6,9,8,5,0,6,13,1,3,13,4,14,14,0,7,11,5,3,11,8,9,4,14,3,15,2,5,12,2,9,8,5,12,15,3,10,7,11,0,14,4,1,10,7,1,6,13,0,11,8,6,13,4,13,11,0,2,11,14,7,15,4,0,9,8,1,13,10,3,14,12,3,9,5,7,12,5,2,10,15,6,8,1,6,1,6,4,11,11,13,13,8,12,1,3,4,7,10,14,7,10,9,15,5,6,0,8,15,0,14,5,2,9,3,2,12,13,1,2,15,8,13,4,8,6,10,15,3,11,7,1,4,10,12,9,5,3,6,14,11,5,0,0,14,12,9,7,2,7,2,11,1,4,14,1,7,9,4,12,10,14,8,2,13,0,15,6,12,10,9,13,0,15,3,3,5,5,6,8,11];t.substitute=function(e,t){for(var r=0,n=0;n<4;n++)r<<=4,r|=i[64*n+(e>>>18-6*n&63)];for(n=0;n<4;n++)r<<=4,r|=i[256+64*n+(t>>>18-6*n&63)];return r>>>0};var o=[16,25,12,11,3,20,4,15,31,17,9,6,27,14,1,22,30,24,8,18,0,5,29,23,13,19,2,26,10,21,28,7];t.permute=function(e){for(var t=0,r=0;r>>o[r]&1;return t>>>0},t.padSplit=function(e,t,r){for(var n=e.toString(2);n.length>>1];r=o.r28shl(r,s),i=o.r28shl(i,s),o.pc2(r,i,e.keys,a)}},f.prototype._update=function(e,t,r,n){var i=this._desState,a=o.readUInt32BE(e,t),s=o.readUInt32BE(e,t+4);o.ip(a,s,i.tmp,0),a=i.tmp[0],s=i.tmp[1],"encrypt"===this.type?this._encrypt(i,a,s,i.tmp,0):this._decrypt(i,a,s,i.tmp,0),a=i.tmp[0],s=i.tmp[1],o.writeUInt32BE(r,a,n),o.writeUInt32BE(r,s,n+4)},f.prototype._pad=function(e,t){for(var r=e.length-t,n=t;n>>0,a=l}o.rip(s,a,n,i)},f.prototype._decrypt=function(e,t,r,n,i){for(var a=r,s=t,f=e.keys.length-2;f>=0;f-=2){var c=e.keys[f],u=e.keys[f+1];o.expand(a,e.tmp,0),c^=e.tmp[0],u^=e.tmp[1];var h=o.substitute(c,u),l=a;a=(s^o.permute(h))>>>0,s=l}o.rip(a,s,n,i)}},function(e,t,r){var n=r(39),i=r(0).Buffer,o=r(120);function a(e){var t=e._cipher.encryptBlockRaw(e._prev);return o(e._prev),t}t.encrypt=function(e,t){var r=Math.ceil(t.length/16),o=e._cache.length;e._cache=i.concat([e._cache,i.allocUnsafe(16*r)]);for(var s=0;se;)r.ishrn(1);if(r.isEven()&&r.iadd(s),r.testn(1)||r.iadd(f),t.cmp(f)){if(!t.cmp(c))for(;r.mod(u).cmp(h);)r.iadd(d)}else for(;r.mod(o).cmp(l);)r.iadd(d);if(y(p=r.shrn(1))&&y(r)&&b(p)&&b(r)&&a.test(p)&&a.test(r))return r}}},function(e,t,r){var n=r(6),i=r(62);function o(e){this.rand=e||new i.Rand}e.exports=o,o.create=function(e){return new o(e)},o.prototype._randbelow=function(e){var t=e.bitLength(),r=Math.ceil(t/8);do{var i=new n(this.rand.generate(r))}while(i.cmp(e)>=0);return i},o.prototype._randrange=function(e,t){var r=t.sub(e);return e.add(this._randbelow(r))},o.prototype.test=function(e,t,r){var i=e.bitLength(),o=n.mont(e),a=new n(1).toRed(o);t||(t=Math.max(1,i/48|0));for(var s=e.subn(1),f=0;!s.testn(f);f++);for(var c=e.shrn(f),u=s.toRed(o);t>0;t--){var h=this._randrange(new n(2),s);r&&r(h);var l=h.toRed(o).redPow(c);if(0!==l.cmp(a)&&0!==l.cmp(u)){for(var d=1;d0;t--){var u=this._randrange(new n(2),a),h=e.gcd(u);if(0!==h.cmpn(1))return h;var l=u.toRed(i).redPow(f);if(0!==l.cmp(o)&&0!==l.cmp(c)){for(var d=1;d>6],i=0==(32&r);if(31==(31&r)){var o=r;for(r=0;128==(128&o);){if(o=e.readUInt8(t),e.isError(o))return o;r<<=7,r|=127&o}}else r&=31;return{cls:n,primitive:i,tag:r,tagStr:s.tag[r]}}function h(e,t,r){var n=e.readUInt8(r);if(e.isError(n))return n;if(!t&&128===n)return null;if(0==(128&n))return n;var i=127&n;if(i>4)return e.error("length octect is too long");n=0;for(var o=0;o=31?n.error("Multi-octet tag encoding unsupported"):(t||(i|=32),i|s.tagClassByName[r||"universal"]<<6)}(e,t,r,this.reporter);if(n.length<128)return(o=new i(2))[0]=a,o[1]=n.length,this._createEncoderBuffer([o,n]);for(var f=1,c=n.length;c>=256;c>>=8)f++;(o=new i(2+f))[0]=a,o[1]=128|f,c=1+f;for(var u=n.length;u>0;c--,u>>=8)o[c]=255&u;return this._createEncoderBuffer([o,n])},c.prototype._encodeStr=function(e,t){if("bitstr"===t)return this._createEncoderBuffer([0|e.unused,e.data]);if("bmpstr"===t){for(var r=new i(2*e.length),n=0;n=40)return this.reporter.error("Second objid identifier OOB");e.splice(0,2,40*e[0]+e[1])}var o=0;for(n=0;n=128;a>>=7)o++}var s=new i(o),f=s.length-1;for(n=e.length-1;n>=0;n--)for(a=e[n],s[f--]=127&a;(a>>=7)>0;)s[f--]=128|127&a;return this._createEncoderBuffer(s)},c.prototype._encodeTime=function(e,t){var r,n=new Date(e);return"gentime"===t?r=[u(n.getFullYear()),u(n.getUTCMonth()+1),u(n.getUTCDate()),u(n.getUTCHours()),u(n.getUTCMinutes()),u(n.getUTCSeconds()),"Z"].join(""):"utctime"===t?r=[u(n.getFullYear()%100),u(n.getUTCMonth()+1),u(n.getUTCDate()),u(n.getUTCHours()),u(n.getUTCMinutes()),u(n.getUTCSeconds()),"Z"].join(""):this.reporter.error("Encoding "+t+" time is not supported yet"),this._encodeStr(r,"octstr")},c.prototype._encodeNull=function(){return this._createEncoderBuffer("")},c.prototype._encodeInt=function(e,t){if("string"==typeof e){if(!t)return this.reporter.error("String int or enum given, but no values map");if(!t.hasOwnProperty(e))return this.reporter.error("Values map doesn\'t contain: "+JSON.stringify(e));e=t[e]}if("number"!=typeof e&&!i.isBuffer(e)){var r=e.toArray();!e.sign&&128&r[0]&&r.unshift(0),e=new i(r)}if(i.isBuffer(e)){var n=e.length;0===e.length&&n++;var o=new i(n);return e.copy(o),0===e.length&&(o[0]=0),this._createEncoderBuffer(o)}if(e<128)return this._createEncoderBuffer(e);if(e<256)return this._createEncoderBuffer([0,e]);n=1;for(var a=e;a>=256;a>>=8)n++;for(a=(o=new Array(n)).length-1;a>=0;a--)o[a]=255&e,e>>=8;return 128&o[0]&&o.unshift(0),this._createEncoderBuffer(new i(o))},c.prototype._encodeBool=function(e){return this._createEncoderBuffer(e?255:0)},c.prototype._use=function(e,t){return"function"==typeof e&&(e=e(t)),e._getEncoder("der").tree},c.prototype._skipDefault=function(e,t,r){var n,i=this._baseState;if(null===i.default)return!1;var o=e.join();if(void 0===i.defaultBuffer&&(i.defaultBuffer=this._encodeValue(i.default,t,r).join()),o.length!==i.defaultBuffer.length)return!1;for(n=0;n>>32-t}function s(e,t,r){let n;for(n=0;n<16;n++)t[n]=(255&e[4*n+0])<<0,t[n]|=(255&e[4*n+1])<<8,t[n]|=(255&e[4*n+2])<<16,t[n]|=(255&e[4*n+3])<<24;for(c(t,0,r,0,16),n=8;n>0;n-=2)r[4]^=a(r[0]+r[12],7),r[8]^=a(r[4]+r[0],9),r[12]^=a(r[8]+r[4],13),r[0]^=a(r[12]+r[8],18),r[9]^=a(r[5]+r[1],7),r[13]^=a(r[9]+r[5],9),r[1]^=a(r[13]+r[9],13),r[5]^=a(r[1]+r[13],18),r[14]^=a(r[10]+r[6],7),r[2]^=a(r[14]+r[10],9),r[6]^=a(r[2]+r[14],13),r[10]^=a(r[6]+r[2],18),r[3]^=a(r[15]+r[11],7),r[7]^=a(r[3]+r[15],9),r[11]^=a(r[7]+r[3],13),r[15]^=a(r[11]+r[7],18),r[1]^=a(r[0]+r[3],7),r[2]^=a(r[1]+r[0],9),r[3]^=a(r[2]+r[1],13),r[0]^=a(r[3]+r[2],18),r[6]^=a(r[5]+r[4],7),r[7]^=a(r[6]+r[5],9),r[4]^=a(r[7]+r[6],13),r[5]^=a(r[4]+r[7],18),r[11]^=a(r[10]+r[9],7),r[8]^=a(r[11]+r[10],9),r[9]^=a(r[8]+r[11],13),r[10]^=a(r[9]+r[8],18),r[12]^=a(r[15]+r[14],7),r[13]^=a(r[12]+r[15],9),r[14]^=a(r[13]+r[12],13),r[15]^=a(r[14]+r[13],18);for(n=0;n<16;++n)t[n]=r[n]+t[n];for(n=0;n<16;n++){let r=4*n;e[r+0]=t[n]>>0&255,e[r+1]=t[n]>>8&255,e[r+2]=t[n]>>16&255,e[r+3]=t[n]>>24&255}}function f(e,t,r,n,i){for(let o=0;o 0 and a power of 2");if(n>2147483647/128/o)throw Error("Parameter N is too large");if(o>2147483647/128/a)throw Error("Parameter r is too large");let c,u=t.alloc(256*o),h=t.alloc(128*o*n),l=new Int32Array(16),d=new Int32Array(16),p=t.alloc(64),y=i.pbkdf2Sync(e,r,1,128*a*o,"sha256");if(f){let e=a*n*2,t=0;c=function(){++t,t%1e3==0&&f({current:t,total:e,percent:t/e*100})}}return{XY:u,V:h,B32:l,x:d,_X:p,B:y,tickCallback:c}},smix:async function(e,t,r,i,a,s,c,u,h,l,d){d=d||5e3;let p,y=128*r;for(e.copy(s,0,t,t+y),p=0;pn(e)),o(s,0,y,r,c,u,h),l&&l();for(p=0;pn(e)),o(s,0,y,r,c,u,h),l&&l()}s.copy(e,t,0,0+y)},smixSync:function(e,t,r,n,i,a,s,c,u,h){let l,d=128*r;for(e.copy(a,0,t,t+d),l=0;l=48&&e<=57)return e-48;if(e>=65&&e<=70)return e-55;if(e>=97&&e<=102)return e-87;throw new Error("invalid bloom")}function a(t,r){if(!function(t){return t instanceof e||t instanceof Uint8Array?256===t.length:!(!/^(0x)?[0-9a-f]{512}$/i.test(t)||!/^(0x)?[0-9a-f]{512}$/.test(t)&&!/^(0x)?[0-9A-F]{512}$/.test(t))}(t))throw new Error("Invalid Bloom");for(var n=0;n<12;n+=4){var i=(parseInt(r.substr(n,2),16)<<8)+parseInt(r.substr(n+2,2),16)&2047,a=1<=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}},function(e,t,r){var n=r(169),i=r(170),o=r(171);e.exports=function(e,t){return n(e)||i(e,t)||o()}},function(e,t,r){"use strict";var n=r(172);e.exports=t=n.descriptor=n.Root.fromJSON(r(176)).lookup(".google.protobuf");var i=n.Namespace,o=n.Root,a=n.Enum,s=n.Type,f=n.Field,c=n.MapField,u=n.OneOf,h=n.Service,l=n.Method;o.fromDescriptor=function(e){"number"==typeof e.length&&(e=t.FileDescriptorSet.decode(e));var r=new o;if(e.file)for(var n,i,c,u=0;u2?n-2:0),o=2;o0){var u=new h.a(this,c,t);return u.at(e)}throw new Error("no such contract")}return this.getContractFileDescriptorSet(e).then((function(n){if(n&&n.file&&n.file.length>0){var i=new h.a(r,n,t).at(e);return s(null,i),i}if(s(new Error("no such contract")),s.length>0)throw new Error("no such contract")}))}},{key:"getMerklePath",value:function(t,r){for(var n=this,i=arguments.length,o=new Array(i>2?i-2:0),a=2;at.params.length-1?(Object(s.isFunction)(e)&&(r.callback=e,r.isSync=!1),Object(s.isBoolean)(e.sync)&&(r.isSync=e.sync)):r.params[t.params[n]]=e})),r}},{key:"run",value:function(){for(var e=this,t=arguments.length,r=new Array(t),n=0;n2&&void 0!==arguments[2]?arguments[2]:y,v=d({},y,{},b),g=v.cipher,m=void 0===g?"aes-128-ctr":g,w=/128/.test(m)?16:32,_=c()(32),E=(p[m.toLowerCase()]||{}).iv,S=c()(void 0===E?16:E),A=a()(e.from(r,"utf8"),_,v.n,v.r,v.p,v.dklen),k=Object(s.createCipheriv)(m,A.slice(0,w),S),O=e.concat([k.update(e.from(i,"hex")),k.final()]),x=Object(s.createCipheriv)(m,A.slice(0,w),S),T=e.concat([x.update(e.from(n,"utf8")),x.final()]),I=e.concat([A.slice(16),O]),M=Object(u.a)(I).replace("0x","");return{version:1,type:"aelf",nickName:f,address:l,crypto:{cipher:m,ciphertext:O.toString("hex"),cipherparams:{iv:S.toString("hex")},mnemonicEncrypted:T.toString("hex"),kdf:"scrypt",kdfparams:{r:v.r,n:v.n,p:v.p,dklen:v.dklen,salt:_.toString("hex")},mac:M}}}function v(t,r){var n=t.crypto,i=t.nickName,o=void 0===i?"":i,f=t.address,c=void 0===f?"":f,l=n.kdfparams,p=n.mac,y=n.cipherparams,b=n.mnemonicEncrypted,v=void 0===b?"":b,g=n.ciphertext,m=n.cipher,w=void 0===m?"aes-128-ctr":m,_=/128/.test(w)?16:32,E=e.from(y.iv,"hex"),S=a()(e.from(r),e.from(l.salt,"hex"),l.n,l.r,l.p,l.dklen||l.dkLen),A=e.concat([S.slice(16),e.from(g,"hex")]);if(Object(u.a)(A).replace("0x","")!==p)throw d({},h.b.INVALID_PASSWORD);var k=Object(s.createDecipheriv)(w,S.slice(0,_),E),O=e.concat([k.update(e.from(g,"hex")),k.final()]).toString("hex"),x=Object(s.createDecipheriv)(w,S.slice(0,_),E);return{nickName:o,address:c,mnemonic:e.concat([x.update(e.from(v,"hex")),x.final()]).toString("utf8"),privateKey:O}}var g=function(e,t){try{return!!v(e,t).privateKey}catch(e){return!1}}}.call(this,r(3).Buffer)},function(e,t){function r(e,t,r,n,i,o,a){try{var s=e[o](a),f=s.value}catch(e){return void r(e)}s.done?t(f):Promise.resolve(f).then(n,i)}e.exports=function(e){return function(){var t=this,n=arguments;return new Promise((function(i,o){var a=e.apply(t,n);function s(e){r(a,i,o,s,f,"next",e)}function f(e){r(a,i,o,s,f,"throw",e)}s(void 0)}))}}},function(e,t,r){"use strict";var n=t;function i(){n.Reader._configure(n.BufferReader),n.util._configure()}n.build="minimal",n.Writer=r(54),n.BufferWriter=r(157),n.Reader=r(55),n.BufferReader=r(158),n.util=r(19),n.rpc=r(88),n.roots=r(89),n.configure=i,n.Writer._configure(n.BufferWriter),i()},function(e,t,r){"use strict";var n=t;n.length=function(e){var t=e.length;if(!t)return 0;for(var r=0;--t%4>1&&"="===e.charAt(t);)++r;return Math.ceil(3*e.length)/4-r};for(var i=new Array(64),o=new Array(123),a=0;a<64;)o[i[a]=a<26?a+65:a<52?a+71:a<62?a-4:a-59|43]=a++;n.encode=function(e,t,r){for(var n,o=null,a=[],s=0,f=0;t>2],n=(3&c)<<4,f=1;break;case 1:a[s++]=i[n|c>>4],n=(15&c)<<2,f=2;break;case 2:a[s++]=i[n|c>>6],a[s++]=i[63&c],f=0}s>8191&&((o||(o=[])).push(String.fromCharCode.apply(String,a)),s=0)}return f&&(a[s++]=i[n],a[s++]=61,1===f&&(a[s++]=61)),o?(s&&o.push(String.fromCharCode.apply(String,a.slice(0,s))),o.join("")):String.fromCharCode.apply(String,a.slice(0,s))},n.decode=function(e,t,r){for(var n,i=r,a=0,s=0;s1)break;if(void 0===(f=o[f]))throw Error("invalid encoding");switch(a){case 0:n=f,a=1;break;case 1:t[r++]=n<<2|(48&f)>>4,n=f,a=2;break;case 2:t[r++]=(15&n)<<4|(60&f)>>2,n=f,a=3;break;case 3:t[r++]=(3&n)<<6|f,a=0}}if(1===a)throw Error("invalid encoding");return r-i},n.test=function(e){return/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e)}},function(e,t,r){"use strict";function n(){this._listeners={}}e.exports=n,n.prototype.on=function(e,t,r){return(this._listeners[e]||(this._listeners[e]=[])).push({fn:t,ctx:r||this}),this},n.prototype.off=function(e,t){if(void 0===e)this._listeners={};else if(void 0===t)this._listeners[e]=[];else for(var r=this._listeners[e],n=0;n0?0:2147483648,r,n);else if(isNaN(t))e(2143289344,r,n);else if(t>34028234663852886e22)e((i<<31|2139095040)>>>0,r,n);else if(t<11754943508222875e-54)e((i<<31|Math.round(t/1401298464324817e-60))>>>0,r,n);else{var o=Math.floor(Math.log(t)/Math.LN2);e((i<<31|o+127<<23|8388607&Math.round(t*Math.pow(2,-o)*8388608))>>>0,r,n)}}function r(e,t,r){var n=e(t,r),i=2*(n>>31)+1,o=n>>>23&255,a=8388607&n;return 255===o?a?NaN:i*(1/0):0===o?1401298464324817e-60*i*a:i*Math.pow(2,o-150)*(a+8388608)}e.writeFloatLE=t.bind(null,i),e.writeFloatBE=t.bind(null,o),e.readFloatLE=r.bind(null,a),e.readFloatBE=r.bind(null,s)}(),"undefined"!=typeof Float64Array?function(){var t=new Float64Array([-0]),r=new Uint8Array(t.buffer),n=128===r[7];function i(e,n,i){t[0]=e,n[i]=r[0],n[i+1]=r[1],n[i+2]=r[2],n[i+3]=r[3],n[i+4]=r[4],n[i+5]=r[5],n[i+6]=r[6],n[i+7]=r[7]}function o(e,n,i){t[0]=e,n[i]=r[7],n[i+1]=r[6],n[i+2]=r[5],n[i+3]=r[4],n[i+4]=r[3],n[i+5]=r[2],n[i+6]=r[1],n[i+7]=r[0]}function a(e,n){return r[0]=e[n],r[1]=e[n+1],r[2]=e[n+2],r[3]=e[n+3],r[4]=e[n+4],r[5]=e[n+5],r[6]=e[n+6],r[7]=e[n+7],t[0]}function s(e,n){return r[7]=e[n],r[6]=e[n+1],r[5]=e[n+2],r[4]=e[n+3],r[3]=e[n+4],r[2]=e[n+5],r[1]=e[n+6],r[0]=e[n+7],t[0]}e.writeDoubleLE=n?i:o,e.writeDoubleBE=n?o:i,e.readDoubleLE=n?a:s,e.readDoubleBE=n?s:a}():function(){function t(e,t,r,n,i,o){var a=n<0?1:0;if(a&&(n=-n),0===n)e(0,i,o+t),e(1/n>0?0:2147483648,i,o+r);else if(isNaN(n))e(0,i,o+t),e(2146959360,i,o+r);else if(n>17976931348623157e292)e(0,i,o+t),e((a<<31|2146435072)>>>0,i,o+r);else{var s;if(n<22250738585072014e-324)e((s=n/5e-324)>>>0,i,o+t),e((a<<31|s/4294967296)>>>0,i,o+r);else{var f=Math.floor(Math.log(n)/Math.LN2);1024===f&&(f=1023),e(4503599627370496*(s=n*Math.pow(2,-f))>>>0,i,o+t),e((a<<31|f+1023<<20|1048576*s&1048575)>>>0,i,o+r)}}}function r(e,t,r,n,i){var o=e(n,i+t),a=e(n,i+r),s=2*(a>>31)+1,f=a>>>20&2047,c=4294967296*(1048575&a)+o;return 2047===f?c?NaN:s*(1/0):0===f?5e-324*s*c:s*Math.pow(2,f-1075)*(c+4503599627370496)}e.writeDoubleLE=t.bind(null,i,0,4),e.writeDoubleBE=t.bind(null,o,4,0),e.readDoubleLE=r.bind(null,a,0,4),e.readDoubleBE=r.bind(null,s,4,0)}(),e}function i(e,t,r){t[r]=255&e,t[r+1]=e>>>8&255,t[r+2]=e>>>16&255,t[r+3]=e>>>24}function o(e,t,r){t[r]=e>>>24,t[r+1]=e>>>16&255,t[r+2]=e>>>8&255,t[r+3]=255&e}function a(e,t){return(e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24)>>>0}function s(e,t){return(e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3])>>>0}e.exports=n(n)},function(e,t,r){"use strict";var n=t;n.length=function(e){for(var t=0,r=0,n=0;n191&&n<224?o[a++]=(31&n)<<6|63&e[t++]:n>239&&n<365?(n=((7&n)<<18|(63&e[t++])<<12|(63&e[t++])<<6|63&e[t++])-65536,o[a++]=55296+(n>>10),o[a++]=56320+(1023&n)):o[a++]=(15&n)<<12|(63&e[t++])<<6|63&e[t++],a>8191&&((i||(i=[])).push(String.fromCharCode.apply(String,o)),a=0);return i?(a&&i.push(String.fromCharCode.apply(String,o.slice(0,a))),i.join("")):String.fromCharCode.apply(String,o.slice(0,a))},n.write=function(e,t,r){for(var n,i,o=r,a=0;a>6|192,t[r++]=63&n|128):55296==(64512&n)&&56320==(64512&(i=e.charCodeAt(a+1)))?(n=65536+((1023&n)<<10)+(1023&i),++a,t[r++]=n>>18|240,t[r++]=n>>12&63|128,t[r++]=n>>6&63|128,t[r++]=63&n|128):(t[r++]=n>>12|224,t[r++]=n>>6&63|128,t[r++]=63&n|128);return r-o}},function(e,t,r){"use strict";e.exports=function(e,t,r){var n=r||8192,i=n>>>1,o=null,a=n;return function(r){if(r<1||r>i)return e(r);a+r>n&&(o=e(n),a=0);var s=t.call(o,a,a+=r);return 7&a&&(a=1+(7|a)),s}}},function(e,t,r){"use strict";e.exports=i;var n=r(19);function i(e,t){this.lo=e>>>0,this.hi=t>>>0}var o=i.zero=new i(0,0);o.toNumber=function(){return 0},o.zzEncode=o.zzDecode=function(){return this},o.length=function(){return 1};var a=i.zeroHash="\\0\\0\\0\\0\\0\\0\\0\\0";i.fromNumber=function(e){if(0===e)return o;var t=e<0;t&&(e=-e);var r=e>>>0,n=(e-r)/4294967296>>>0;return t&&(n=~n>>>0,r=~r>>>0,++r>4294967295&&(r=0,++n>4294967295&&(n=0))),new i(r,n)},i.from=function(e){if("number"==typeof e)return i.fromNumber(e);if(n.isString(e)){if(!n.Long)return i.fromNumber(parseInt(e,10));e=n.Long.fromString(e)}return e.low||e.high?new i(e.low>>>0,e.high>>>0):o},i.prototype.toNumber=function(e){if(!e&&this.hi>>>31){var t=1+~this.lo>>>0,r=~this.hi>>>0;return t||(r=r+1>>>0),-(t+4294967296*r)}return this.lo+4294967296*this.hi},i.prototype.toLong=function(e){return n.Long?new n.Long(0|this.lo,0|this.hi,Boolean(e)):{low:0|this.lo,high:0|this.hi,unsigned:Boolean(e)}};var s=String.prototype.charCodeAt;i.fromHash=function(e){return e===a?o:new i((s.call(e,0)|s.call(e,1)<<8|s.call(e,2)<<16|s.call(e,3)<<24)>>>0,(s.call(e,4)|s.call(e,5)<<8|s.call(e,6)<<16|s.call(e,7)<<24)>>>0)},i.prototype.toHash=function(){return String.fromCharCode(255&this.lo,this.lo>>>8&255,this.lo>>>16&255,this.lo>>>24,255&this.hi,this.hi>>>8&255,this.hi>>>16&255,this.hi>>>24)},i.prototype.zzEncode=function(){var e=this.hi>>31;return this.hi=((this.hi<<1|this.lo>>>31)^e)>>>0,this.lo=(this.lo<<1^e)>>>0,this},i.prototype.zzDecode=function(){var e=-(1&this.lo);return this.lo=((this.lo>>>1|this.hi<<31)^e)>>>0,this.hi=(this.hi>>>1^e)>>>0,this},i.prototype.length=function(){var e=this.lo,t=(this.lo>>>28|this.hi<<4)>>>0,r=this.hi>>>24;return 0===r?0===t?e<16384?e<128?1:2:e<2097152?3:4:t<16384?t<128?5:6:t<2097152?7:8:r<128?9:10}},function(e,t,r){"use strict";t.byteLength=function(e){var t=c(e),r=t[0],n=t[1];return 3*(r+n)/4-n},t.toByteArray=function(e){var t,r,n=c(e),a=n[0],s=n[1],f=new o(function(e,t,r){return 3*(t+r)/4-r}(0,a,s)),u=0,h=s>0?a-4:a;for(r=0;r>16&255,f[u++]=t>>8&255,f[u++]=255&t;return 2===s&&(t=i[e.charCodeAt(r)]<<2|i[e.charCodeAt(r+1)]>>4,f[u++]=255&t),1===s&&(t=i[e.charCodeAt(r)]<<10|i[e.charCodeAt(r+1)]<<4|i[e.charCodeAt(r+2)]>>2,f[u++]=t>>8&255,f[u++]=255&t),f},t.fromByteArray=function(e){for(var t,r=e.length,i=r%3,o=[],a=0,s=r-i;as?s:a+16383));return 1===i?(t=e[r-1],o.push(n[t>>2]+n[t<<4&63]+"==")):2===i&&(t=(e[r-2]<<8)+e[r-1],o.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"=")),o.join("")};for(var n=[],i=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,f=a.length;s0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function u(e,t,r){for(var i,o,a=[],s=t;s>18&63]+n[o>>12&63]+n[o>>6&63]+n[63&o]);return a.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,r,n,i){var o,a,s=8*i-n-1,f=(1<>1,u=-7,h=r?i-1:0,l=r?-1:1,d=e[t+h];for(h+=l,o=d&(1<<-u)-1,d>>=-u,u+=s;u>0;o=256*o+e[t+h],h+=l,u-=8);for(a=o&(1<<-u)-1,o>>=-u,u+=n;u>0;a=256*a+e[t+h],h+=l,u-=8);if(0===o)o=1-c;else{if(o===f)return a?NaN:1/0*(d?-1:1);a+=Math.pow(2,n),o-=c}return(d?-1:1)*a*Math.pow(2,o-n)},t.write=function(e,t,r,n,i,o){var a,s,f,c=8*o-i-1,u=(1<>1,l=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,d=n?0:o-1,p=n?1:-1,y=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=u):(a=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-a))<1&&(a--,f*=2),(t+=a+h>=1?l/f:l*Math.pow(2,1-h))*f>=2&&(a++,f/=2),a+h>=u?(s=0,a=u):a+h>=1?(s=(t*f-1)*Math.pow(2,i),a+=h):(s=t*Math.pow(2,h-1)*Math.pow(2,i),a=0));i>=8;e[r+d]=255&s,d+=p,s/=256,i-=8);for(a=a<0;e[r+d]=255&a,d+=p,a/=256,c-=8);e[r+d-p]|=128*y}},function(e,t,r){"use strict";e.exports=a;var n=r(54);(a.prototype=Object.create(n.prototype)).constructor=a;var i=r(19),o=i.Buffer;function a(){n.call(this)}a.alloc=function(e){return(a.alloc=i._Buffer_allocUnsafe)(e)};var s=o&&o.prototype instanceof Uint8Array&&"set"===o.prototype.set.name?function(e,t,r){t.set(e,r)}:function(e,t,r){if(e.copy)e.copy(t,r,0,e.length);else for(var n=0;n>>0;return this.uint32(t),t&&this._push(s,t,e),this},a.prototype.string=function(e){var t=o.byteLength(e);return this.uint32(t),t&&this._push(f,t,e),this}},function(e,t,r){"use strict";e.exports=o;var n=r(55);(o.prototype=Object.create(n.prototype)).constructor=o;var i=r(19);function o(e){n.call(this,e)}i.Buffer&&(o.prototype._slice=i.Buffer.prototype.slice),o.prototype.string=function(){var e=this.uint32();return this.buf.utf8Slice(this.pos,this.pos=Math.min(this.pos+e,this.len))}},function(e,t,r){"use strict";e.exports=i;var n=r(19);function i(e,t,r){if("function"!=typeof e)throw TypeError("rpcImpl must be a function");n.EventEmitter.call(this),this.rpcImpl=e,this.requestDelimited=Boolean(t),this.responseDelimited=Boolean(r)}(i.prototype=Object.create(n.EventEmitter.prototype)).constructor=i,i.prototype.rpcCall=function e(t,r,i,o,a){if(!o)throw TypeError("request must be specified");var s=this;if(!a)return n.asPromise(e,s,t,r,i,o);if(s.rpcImpl)try{return s.rpcImpl(t,r[s.requestDelimited?"encodeDelimited":"encode"](o).finish(),(function(e,r){if(e)return s.emit("error",e,t),a(e);if(null!==r){if(!(r instanceof i))try{r=i[s.responseDelimited?"decodeDelimited":"decode"](r)}catch(e){return s.emit("error",e,t),a(e)}return s.emit("data",r,t),a(null,r)}s.end(!0)}))}catch(e){return s.emit("error",e,t),void setTimeout((function(){a(e)}),0)}else setTimeout((function(){a(Error("already ended"))}),0)},i.prototype.end=function(e){return this.rpcImpl&&(e||this.rpcImpl(null,null,null),this.rpcImpl=null,this.emit("end").off()),this}},function(e,t,r){"use strict";function n(e,t){"string"==typeof e&&(t=e,e=void 0);var r=[];function i(e){if("string"!=typeof e){var t=o();if(n.verbose&&console.log("codegen: "+t),t="return "+t,e){for(var a=Object.keys(e),s=new Array(a.length+1),f=new Array(a.length),c=0;c0&&".."!==t[o-1]?t.splice(--o,2):r?t.splice(o,1):++o:"."===t[o]?t.splice(o,1):++o;return n+t.join("/")};n.resolve=function(e,t,r){return r||(t=o(t)),i(t)?t:(r||(e=o(e)),(e=e.replace(/(?:\\/|^)[^/]+$/,"")).length?o(e+"/"+t):t)}},function(e,t){(function(t){e.exports=t}).call(this,{})},function(e,t){e.exports=function(e){if(Array.isArray(e)){for(var t=0,r=new Array(e.length);t=0||(i[r]=e[r]);return i}},function(e,t,r){"use strict";var n=r(0).Buffer;e.exports=function(e){if(e.length>=255)throw new TypeError("Alphabet too long");var t=new Uint8Array(256);t.fill(255);for(var r=0;r>>0,u=new Uint8Array(c);e[r];){var h=t[e.charCodeAt(r)];if(255===h)return;for(var l=0,d=c-1;(0!==h||l>>0,u[d]=h%256>>>0,h=h/256>>>0;if(0!==h)throw new Error("Non-zero carry");o=l,r++}if(" "!==e[r]){for(var p=c-o;p!==c&&0===u[p];)p++;var y=n.allocUnsafe(i+(c-p));y.fill(0,0,i);for(var b=i;p!==c;)y[b++]=u[p++];return y}}}return{encode:function(t){if(!n.isBuffer(t))throw new TypeError("Expected Buffer");if(0===t.length)return"";for(var r=0,i=0,o=0,f=t.length;o!==f&&0===t[o];)o++,r++;for(var u=(f-o)*c+1>>>0,h=new Uint8Array(u);o!==f;){for(var l=t[o],d=0,p=u-1;(0!==l||d>>0,h[p]=l%a>>>0,l=l/a>>>0;if(0!==l)throw new Error("Non-zero carry");i=d,o++}for(var y=u-i;y!==u&&0===h[y];)y++;for(var b=s.repeat(r);y");var n=R();if(!_.test(n))throw F(n,"name");B("=");var i=new s(U(n),K(R()),t,r);X(i,(function(e){if("option"!==e)throw F(e);$(i,e),B(";")}),(function(){Q(i)})),e.add(i)}(r);break;case"required":case"optional":case"repeated":J(r,e);break;case"oneof":!function(e,t){if(!_.test(t=R()))throw F(t,"name");var r=new f(U(t));X(r,(function(e){"option"===e?($(r,e),B(";")):(P(e),J(r,"optional"))})),e.add(r)}(r,e);break;case"extensions":z(r.extensions||(r.extensions=[]));break;case"reserved":z(r.reserved||(r.reserved=[]),!0);break;default:if(!L||!E.test(e))throw F(e);P(e),J(r,"optional")}})),e.add(r)}(e,t),!0;case"enum":return function(e,t){if(!_.test(t=R()))throw F(t,"name");var r=new c(t);X(r,(function(e){switch(e){case"option":$(r,e),B(";");break;case"reserved":z(r.reserved||(r.reserved=[]),!0);break;default:!function(e,t){if(!_.test(t))throw F(t,"name");B("=");var r=K(R(),!0),n={};X(n,(function(e){if("option"!==e)throw F(e);$(n,e),B(";")}),(function(){Q(n)})),e.add(t,r,n.comment)}(r,e)}})),e.add(r)}(e,t),!0;case"service":return function(e,t){if(!_.test(t=R()))throw F(t,"service name");var r=new u(t);X(r,(function(e){if(!W(r,e)){if("rpc"!==e)throw F(e);!function(e,t){var r=t;if(!_.test(t=R()))throw F(t,"name");var n,i,o,a,s=t;if(B("("),B("stream",!0)&&(i=!0),!E.test(t=R()))throw F(t);if(n=t,B(")"),B("returns"),B("("),B("stream",!0)&&(a=!0),!E.test(t=R()))throw F(t);o=t,B(")");var f=new h(s,r,n,o,i,a);X(f,(function(e){if("option"!==e)throw F(e);$(f,e),B(";")})),e.add(f)}(r,e)}})),e.add(r)}(e,t),!0;case"extend":return function(e,t){if(!E.test(t=R()))throw F(t,"reference");var r=t;X(null,(function(t){switch(t){case"required":case"repeated":case"optional":J(e,t,r);break;default:if(!L||!E.test(t))throw F(t);P(t),J(e,"optional",r)}}))}(e,t),!0}return!1}function X(e,t,r){var n=M.line;if(e&&(e.comment=C(),e.filename=A.filename),B("{",!0)){for(var i;"}"!==(i=R());)t(i);B(";",!0)}else r&&r(),B(";"),e&&"string"!=typeof e.comment&&(e.comment=C(n))}function J(e,t,r){var n=R();if("group"!==n){if(!E.test(n))throw F(n,"type");var i=R();if(!_.test(i))throw F(i,"name");i=U(i),B("=");var s=new a(i,K(R()),n,t,r);X(s,(function(e){if("option"!==e)throw F(e);$(s,e),B(";")}),(function(){Q(s)})),e.add(s),L||!s.repeated||void 0===l.packed[n]&&void 0!==l.basic[n]||s.setOption("packed",!1,!0)}else!function(e,t){var r=R();if(!_.test(r))throw F(r,"name");var n=d.lcFirst(r);r===n&&(r=d.ucFirst(r)),B("=");var i=K(R()),s=new o(r);s.group=!0;var f=new a(n,i,r,t);f.filename=A.filename,X(s,(function(e){switch(e){case"option":$(s,e),B(";");break;case"required":case"optional":case"repeated":J(s,e);break;default:throw F(e)}})),e.add(s).add(f)}(e,t)}function $(e,t){var r=B("(",!0);if(!E.test(t=R()))throw F(t,"name");var n=t;r&&(B(")"),n="("+n+")",t=j(),S.test(t)&&(n+=t,R())),B("="),function e(t,r){if(B("{",!0))do{if(!_.test(I=R()))throw F(I,"name");"{"===j()?e(t,r+"."+I):(B(":"),"{"===j()?e(t,r+"."+I):Z(t,r+"."+I,q(!0))),B(",",!0)}while(!B("}",!0));else Z(t,r,q(!0))}(e,n)}function Z(e,t,r){e.setOption&&e.setOption(t,r)}function Q(e){if(B("[",!0)){do{$(e,"option")}while(B(",",!0));B("]")}return e}for(;null!==(I=R());)switch(I){case"package":if(!N)throw F(I);V();break;case"import":if(!N)throw F(I);Y();break;case"syntax":if(!N)throw F(I);G();break;case"option":if(!N)throw F(I);$(D,I),B(";");break;default:if(W(D,I)){N=!1;continue}throw F(I)}return A.filename=null,{package:k,imports:O,weakImports:x,syntax:T,root:t}}},function(e,t,r){"use strict";e.exports=o;var n,i=/\\/|\\./;function o(e,t){i.test(e)||(e="google/protobuf/"+e+".proto",t={nested:{google:{nested:{protobuf:{nested:t}}}}}),o[e]=t}o("any",{Any:{fields:{type_url:{type:"string",id:1},value:{type:"bytes",id:2}}}}),o("duration",{Duration:n={fields:{seconds:{type:"int64",id:1},nanos:{type:"int32",id:2}}}}),o("timestamp",{Timestamp:n}),o("empty",{Empty:{fields:{}}}),o("struct",{Struct:{fields:{fields:{keyType:"string",type:"Value",id:1}}},Value:{oneofs:{kind:{oneof:["nullValue","numberValue","stringValue","boolValue","structValue","listValue"]}},fields:{nullValue:{type:"NullValue",id:1},numberValue:{type:"double",id:2},stringValue:{type:"string",id:3},boolValue:{type:"bool",id:4},structValue:{type:"Struct",id:5},listValue:{type:"ListValue",id:6}}},NullValue:{values:{NULL_VALUE:0}},ListValue:{fields:{values:{rule:"repeated",type:"Value",id:1}}}}),o("wrappers",{DoubleValue:{fields:{value:{type:"double",id:1}}},FloatValue:{fields:{value:{type:"float",id:1}}},Int64Value:{fields:{value:{type:"int64",id:1}}},UInt64Value:{fields:{value:{type:"uint64",id:1}}},Int32Value:{fields:{value:{type:"int32",id:1}}},UInt32Value:{fields:{value:{type:"uint32",id:1}}},BoolValue:{fields:{value:{type:"bool",id:1}}},StringValue:{fields:{value:{type:"string",id:1}}},BytesValue:{fields:{value:{type:"bytes",id:1}}}}),o("field_mask",{FieldMask:{fields:{paths:{rule:"repeated",type:"string",id:1}}}}),o.get=function(e){return o[e]||null}},function(e){e.exports=JSON.parse(\'{"nested":{"google":{"nested":{"protobuf":{"nested":{"FileDescriptorSet":{"fields":{"file":{"rule":"repeated","type":"FileDescriptorProto","id":1}}},"FileDescriptorProto":{"fields":{"name":{"type":"string","id":1},"package":{"type":"string","id":2},"dependency":{"rule":"repeated","type":"string","id":3},"publicDependency":{"rule":"repeated","type":"int32","id":10,"options":{"packed":false}},"weakDependency":{"rule":"repeated","type":"int32","id":11,"options":{"packed":false}},"messageType":{"rule":"repeated","type":"DescriptorProto","id":4},"enumType":{"rule":"repeated","type":"EnumDescriptorProto","id":5},"service":{"rule":"repeated","type":"ServiceDescriptorProto","id":6},"extension":{"rule":"repeated","type":"FieldDescriptorProto","id":7},"options":{"type":"FileOptions","id":8},"sourceCodeInfo":{"type":"SourceCodeInfo","id":9},"syntax":{"type":"string","id":12}}},"DescriptorProto":{"fields":{"name":{"type":"string","id":1},"field":{"rule":"repeated","type":"FieldDescriptorProto","id":2},"extension":{"rule":"repeated","type":"FieldDescriptorProto","id":6},"nestedType":{"rule":"repeated","type":"DescriptorProto","id":3},"enumType":{"rule":"repeated","type":"EnumDescriptorProto","id":4},"extensionRange":{"rule":"repeated","type":"ExtensionRange","id":5},"oneofDecl":{"rule":"repeated","type":"OneofDescriptorProto","id":8},"options":{"type":"MessageOptions","id":7},"reservedRange":{"rule":"repeated","type":"ReservedRange","id":9},"reservedName":{"rule":"repeated","type":"string","id":10}},"nested":{"ExtensionRange":{"fields":{"start":{"type":"int32","id":1},"end":{"type":"int32","id":2}}},"ReservedRange":{"fields":{"start":{"type":"int32","id":1},"end":{"type":"int32","id":2}}}}},"FieldDescriptorProto":{"fields":{"name":{"type":"string","id":1},"number":{"type":"int32","id":3},"label":{"type":"Label","id":4},"type":{"type":"Type","id":5},"typeName":{"type":"string","id":6},"extendee":{"type":"string","id":2},"defaultValue":{"type":"string","id":7},"oneofIndex":{"type":"int32","id":9},"jsonName":{"type":"string","id":10},"options":{"type":"FieldOptions","id":8}},"nested":{"Type":{"values":{"TYPE_DOUBLE":1,"TYPE_FLOAT":2,"TYPE_INT64":3,"TYPE_UINT64":4,"TYPE_INT32":5,"TYPE_FIXED64":6,"TYPE_FIXED32":7,"TYPE_BOOL":8,"TYPE_STRING":9,"TYPE_GROUP":10,"TYPE_MESSAGE":11,"TYPE_BYTES":12,"TYPE_UINT32":13,"TYPE_ENUM":14,"TYPE_SFIXED32":15,"TYPE_SFIXED64":16,"TYPE_SINT32":17,"TYPE_SINT64":18}},"Label":{"values":{"LABEL_OPTIONAL":1,"LABEL_REQUIRED":2,"LABEL_REPEATED":3}}}},"OneofDescriptorProto":{"fields":{"name":{"type":"string","id":1},"options":{"type":"OneofOptions","id":2}}},"EnumDescriptorProto":{"fields":{"name":{"type":"string","id":1},"value":{"rule":"repeated","type":"EnumValueDescriptorProto","id":2},"options":{"type":"EnumOptions","id":3}}},"EnumValueDescriptorProto":{"fields":{"name":{"type":"string","id":1},"number":{"type":"int32","id":2},"options":{"type":"EnumValueOptions","id":3}}},"ServiceDescriptorProto":{"fields":{"name":{"type":"string","id":1},"method":{"rule":"repeated","type":"MethodDescriptorProto","id":2},"options":{"type":"ServiceOptions","id":3}}},"MethodDescriptorProto":{"fields":{"name":{"type":"string","id":1},"inputType":{"type":"string","id":2},"outputType":{"type":"string","id":3},"options":{"type":"MethodOptions","id":4},"clientStreaming":{"type":"bool","id":5},"serverStreaming":{"type":"bool","id":6}}},"FileOptions":{"fields":{"javaPackage":{"type":"string","id":1},"javaOuterClassname":{"type":"string","id":8},"javaMultipleFiles":{"type":"bool","id":10},"javaGenerateEqualsAndHash":{"type":"bool","id":20,"options":{"deprecated":true}},"javaStringCheckUtf8":{"type":"bool","id":27},"optimizeFor":{"type":"OptimizeMode","id":9,"options":{"default":"SPEED"}},"goPackage":{"type":"string","id":11},"ccGenericServices":{"type":"bool","id":16},"javaGenericServices":{"type":"bool","id":17},"pyGenericServices":{"type":"bool","id":18},"deprecated":{"type":"bool","id":23},"ccEnableArenas":{"type":"bool","id":31},"objcClassPrefix":{"type":"string","id":36},"csharpNamespace":{"type":"string","id":37},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]],"reserved":[[38,38]],"nested":{"OptimizeMode":{"values":{"SPEED":1,"CODE_SIZE":2,"LITE_RUNTIME":3}}}},"MessageOptions":{"fields":{"messageSetWireFormat":{"type":"bool","id":1},"noStandardDescriptorAccessor":{"type":"bool","id":2},"deprecated":{"type":"bool","id":3},"mapEntry":{"type":"bool","id":7},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]],"reserved":[[8,8]]},"FieldOptions":{"fields":{"ctype":{"type":"CType","id":1,"options":{"default":"STRING"}},"packed":{"type":"bool","id":2},"jstype":{"type":"JSType","id":6,"options":{"default":"JS_NORMAL"}},"lazy":{"type":"bool","id":5},"deprecated":{"type":"bool","id":3},"weak":{"type":"bool","id":10},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]],"reserved":[[4,4]],"nested":{"CType":{"values":{"STRING":0,"CORD":1,"STRING_PIECE":2}},"JSType":{"values":{"JS_NORMAL":0,"JS_STRING":1,"JS_NUMBER":2}}}},"OneofOptions":{"fields":{"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]]},"EnumOptions":{"fields":{"allowAlias":{"type":"bool","id":2},"deprecated":{"type":"bool","id":3},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]]},"EnumValueOptions":{"fields":{"deprecated":{"type":"bool","id":1},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]]},"ServiceOptions":{"fields":{"deprecated":{"type":"bool","id":33},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]]},"MethodOptions":{"fields":{"deprecated":{"type":"bool","id":33},"uninterpretedOption":{"rule":"repeated","type":"UninterpretedOption","id":999}},"extensions":[[1000,536870911]]},"UninterpretedOption":{"fields":{"name":{"rule":"repeated","type":"NamePart","id":2},"identifierValue":{"type":"string","id":3},"positiveIntValue":{"type":"uint64","id":4},"negativeIntValue":{"type":"int64","id":5},"doubleValue":{"type":"double","id":6},"stringValue":{"type":"bytes","id":7},"aggregateValue":{"type":"string","id":8}},"nested":{"NamePart":{"fields":{"namePart":{"rule":"required","type":"string","id":1},"isExtension":{"rule":"required","type":"bool","id":2}}}}},"SourceCodeInfo":{"fields":{"location":{"rule":"repeated","type":"Location","id":1}},"nested":{"Location":{"fields":{"path":{"rule":"repeated","type":"int32","id":1},"span":{"rule":"repeated","type":"int32","id":2},"leadingComments":{"type":"string","id":3},"trailingComments":{"type":"string","id":4},"leadingDetachedComments":{"rule":"repeated","type":"string","id":6}}}}},"GeneratedCodeInfo":{"fields":{"annotation":{"rule":"repeated","type":"Annotation","id":1}},"nested":{"Annotation":{"fields":{"path":{"rule":"repeated","type":"int32","id":1},"sourceFile":{"type":"string","id":2},"begin":{"type":"int32","id":3},"end":{"type":"int32","id":4}}}}}}}}}}}\')},function(e){e.exports=JSON.parse(\'{"name":"elliptic","version":"6.5.2","description":"EC cryptography","main":"lib/elliptic.js","files":["lib"],"scripts":{"jscs":"jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js test/index.js","jshint":"jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js test/index.js","lint":"npm run jscs && npm run jshint","unit":"istanbul test _mocha --reporter=spec test/index.js","test":"npm run lint && npm run unit","version":"grunt dist && git add dist/"},"repository":{"type":"git","url":"git@github.com:indutny/elliptic"},"keywords":["EC","Elliptic","curve","Cryptography"],"author":"Fedor Indutny ","license":"MIT","bugs":{"url":"https://github.com/indutny/elliptic/issues"},"homepage":"https://github.com/indutny/elliptic","devDependencies":{"brfs":"^1.4.3","coveralls":"^3.0.8","grunt":"^1.0.4","grunt-browserify":"^5.0.0","grunt-cli":"^1.2.0","grunt-contrib-connect":"^1.0.0","grunt-contrib-copy":"^1.0.0","grunt-contrib-uglify":"^1.0.1","grunt-mocha-istanbul":"^3.0.1","grunt-saucelabs":"^9.0.1","istanbul":"^0.4.2","jscs":"^3.0.7","jshint":"^2.10.3","mocha":"^6.2.2"},"dependencies":{"bn.js":"^4.4.0","brorand":"^1.0.1","hash.js":"^1.0.0","hmac-drbg":"^1.0.0","inherits":"^2.0.1","minimalistic-assert":"^1.0.0","minimalistic-crypto-utils":"^1.0.0"}}\')},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,t){},function(e,t){},function(e,t,r){"use strict";var n=r(11),i=r(6),o=r(2),a=r(45),s=n.assert;function f(e){a.call(this,"short",e),this.a=new i(e.a,16).toRed(this.red),this.b=new i(e.b,16).toRed(this.red),this.tinv=this.two.redInvm(),this.zeroA=0===this.a.fromRed().cmpn(0),this.threeA=0===this.a.fromRed().sub(this.p).cmpn(-3),this.endo=this._getEndomorphism(e),this._endoWnafT1=new Array(4),this._endoWnafT2=new Array(4)}function c(e,t,r,n){a.BasePoint.call(this,e,"affine"),null===t&&null===r?(this.x=null,this.y=null,this.inf=!0):(this.x=new i(t,16),this.y=new i(r,16),n&&(this.x.forceRed(this.curve.red),this.y.forceRed(this.curve.red)),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.inf=!1)}function u(e,t,r,n){a.BasePoint.call(this,e,"jacobian"),null===t&&null===r&&null===n?(this.x=this.curve.one,this.y=this.curve.one,this.z=new i(0)):(this.x=new i(t,16),this.y=new i(r,16),this.z=new i(n,16)),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)),this.zOne=this.z===this.curve.one}o(f,a),e.exports=f,f.prototype._getEndomorphism=function(e){if(this.zeroA&&this.g&&this.n&&1===this.p.modn(3)){var t,r;if(e.beta)t=new i(e.beta,16).toRed(this.red);else{var n=this._getEndoRoots(this.p);t=(t=n[0].cmp(n[1])<0?n[0]:n[1]).toRed(this.red)}if(e.lambda)r=new i(e.lambda,16);else{var o=this._getEndoRoots(this.n);0===this.g.mul(o[0]).x.cmp(this.g.x.redMul(t))?r=o[0]:(r=o[1],s(0===this.g.mul(r).x.cmp(this.g.x.redMul(t))))}return{beta:t,lambda:r,basis:e.basis?e.basis.map((function(e){return{a:new i(e.a,16),b:new i(e.b,16)}})):this._getEndoBasis(r)}}},f.prototype._getEndoRoots=function(e){var t=e===this.p?this.red:i.mont(e),r=new i(2).toRed(t).redInvm(),n=r.redNeg(),o=new i(3).toRed(t).redNeg().redSqrt().redMul(r);return[n.redAdd(o).fromRed(),n.redSub(o).fromRed()]},f.prototype._getEndoBasis=function(e){for(var t,r,n,o,a,s,f,c,u,h=this.n.ushrn(Math.floor(this.n.bitLength()/2)),l=e,d=this.n.clone(),p=new i(1),y=new i(0),b=new i(0),v=new i(1),g=0;0!==l.cmpn(0);){var m=d.div(l);c=d.sub(m.mul(l)),u=b.sub(m.mul(p));var w=v.sub(m.mul(y));if(!n&&c.cmp(h)<0)t=f.neg(),r=p,n=c.neg(),o=u;else if(n&&2==++g)break;f=c,d=l,l=c,b=p,p=u,v=y,y=w}a=c.neg(),s=u;var _=n.sqr().add(o.sqr());return a.sqr().add(s.sqr()).cmp(_)>=0&&(a=t,s=r),n.negative&&(n=n.neg(),o=o.neg()),a.negative&&(a=a.neg(),s=s.neg()),[{a:n,b:o},{a:a,b:s}]},f.prototype._endoSplit=function(e){var t=this.endo.basis,r=t[0],n=t[1],i=n.b.mul(e).divRound(this.n),o=r.b.neg().mul(e).divRound(this.n),a=i.mul(r.a),s=o.mul(n.a),f=i.mul(r.b),c=o.mul(n.b);return{k1:e.sub(a).sub(s),k2:f.add(c).neg()}},f.prototype.pointFromX=function(e,t){(e=new i(e,16)).red||(e=e.toRed(this.red));var r=e.redSqr().redMul(e).redIAdd(e.redMul(this.a)).redIAdd(this.b),n=r.redSqrt();if(0!==n.redSqr().redSub(r).cmp(this.zero))throw new Error("invalid point");var o=n.fromRed().isOdd();return(t&&!o||!t&&o)&&(n=n.redNeg()),this.point(e,n)},f.prototype.validate=function(e){if(e.inf)return!0;var t=e.x,r=e.y,n=this.a.redMul(t),i=t.redSqr().redMul(t).redIAdd(n).redIAdd(this.b);return 0===r.redSqr().redISub(i).cmpn(0)},f.prototype._endoWnafMulAdd=function(e,t,r){for(var n=this._endoWnafT1,i=this._endoWnafT2,o=0;o":""},c.prototype.isInfinity=function(){return this.inf},c.prototype.add=function(e){if(this.inf)return e;if(e.inf)return this;if(this.eq(e))return this.dbl();if(this.neg().eq(e))return this.curve.point(null,null);if(0===this.x.cmp(e.x))return this.curve.point(null,null);var t=this.y.redSub(e.y);0!==t.cmpn(0)&&(t=t.redMul(this.x.redSub(e.x).redInvm()));var r=t.redSqr().redISub(this.x).redISub(e.x),n=t.redMul(this.x.redSub(r)).redISub(this.y);return this.curve.point(r,n)},c.prototype.dbl=function(){if(this.inf)return this;var e=this.y.redAdd(this.y);if(0===e.cmpn(0))return this.curve.point(null,null);var t=this.curve.a,r=this.x.redSqr(),n=e.redInvm(),i=r.redAdd(r).redIAdd(r).redIAdd(t).redMul(n),o=i.redSqr().redISub(this.x.redAdd(this.x)),a=i.redMul(this.x.redSub(o)).redISub(this.y);return this.curve.point(o,a)},c.prototype.getX=function(){return this.x.fromRed()},c.prototype.getY=function(){return this.y.fromRed()},c.prototype.mul=function(e){return e=new i(e,16),this.isInfinity()?this:this._hasDoubles(e)?this.curve._fixedNafMul(this,e):this.curve.endo?this.curve._endoWnafMulAdd([this],[e]):this.curve._wnafMul(this,e)},c.prototype.mulAdd=function(e,t,r){var n=[this,t],i=[e,r];return this.curve.endo?this.curve._endoWnafMulAdd(n,i):this.curve._wnafMulAdd(1,n,i,2)},c.prototype.jmulAdd=function(e,t,r){var n=[this,t],i=[e,r];return this.curve.endo?this.curve._endoWnafMulAdd(n,i,!0):this.curve._wnafMulAdd(1,n,i,2,!0)},c.prototype.eq=function(e){return this===e||this.inf===e.inf&&(this.inf||0===this.x.cmp(e.x)&&0===this.y.cmp(e.y))},c.prototype.neg=function(e){if(this.inf)return this;var t=this.curve.point(this.x,this.y.redNeg());if(e&&this.precomputed){var r=this.precomputed,n=function(e){return e.neg()};t.precomputed={naf:r.naf&&{wnd:r.naf.wnd,points:r.naf.points.map(n)},doubles:r.doubles&&{step:r.doubles.step,points:r.doubles.points.map(n)}}}return t},c.prototype.toJ=function(){return this.inf?this.curve.jpoint(null,null,null):this.curve.jpoint(this.x,this.y,this.curve.one)},o(u,a.BasePoint),f.prototype.jpoint=function(e,t,r){return new u(this,e,t,r)},u.prototype.toP=function(){if(this.isInfinity())return this.curve.point(null,null);var e=this.z.redInvm(),t=e.redSqr(),r=this.x.redMul(t),n=this.y.redMul(t).redMul(e);return this.curve.point(r,n)},u.prototype.neg=function(){return this.curve.jpoint(this.x,this.y.redNeg(),this.z)},u.prototype.add=function(e){if(this.isInfinity())return e;if(e.isInfinity())return this;var t=e.z.redSqr(),r=this.z.redSqr(),n=this.x.redMul(t),i=e.x.redMul(r),o=this.y.redMul(t.redMul(e.z)),a=e.y.redMul(r.redMul(this.z)),s=n.redSub(i),f=o.redSub(a);if(0===s.cmpn(0))return 0!==f.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var c=s.redSqr(),u=c.redMul(s),h=n.redMul(c),l=f.redSqr().redIAdd(u).redISub(h).redISub(h),d=f.redMul(h.redISub(l)).redISub(o.redMul(u)),p=this.z.redMul(e.z).redMul(s);return this.curve.jpoint(l,d,p)},u.prototype.mixedAdd=function(e){if(this.isInfinity())return e.toJ();if(e.isInfinity())return this;var t=this.z.redSqr(),r=this.x,n=e.x.redMul(t),i=this.y,o=e.y.redMul(t).redMul(this.z),a=r.redSub(n),s=i.redSub(o);if(0===a.cmpn(0))return 0!==s.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var f=a.redSqr(),c=f.redMul(a),u=r.redMul(f),h=s.redSqr().redIAdd(c).redISub(u).redISub(u),l=s.redMul(u.redISub(h)).redISub(i.redMul(c)),d=this.z.redMul(a);return this.curve.jpoint(h,l,d)},u.prototype.dblp=function(e){if(0===e)return this;if(this.isInfinity())return this;if(!e)return this.dbl();if(this.curve.zeroA||this.curve.threeA){for(var t=this,r=0;r=0)return!1;if(r.redIAdd(i),0===this.x.cmp(r))return!0}},u.prototype.inspect=function(){return this.isInfinity()?"":""},u.prototype.isInfinity=function(){return 0===this.z.cmpn(0)}},function(e,t,r){"use strict";var n=r(6),i=r(2),o=r(45),a=r(11);function s(e){o.call(this,"mont",e),this.a=new n(e.a,16).toRed(this.red),this.b=new n(e.b,16).toRed(this.red),this.i4=new n(4).toRed(this.red).redInvm(),this.two=new n(2).toRed(this.red),this.a24=this.i4.redMul(this.a.redAdd(this.two))}function f(e,t,r){o.BasePoint.call(this,e,"projective"),null===t&&null===r?(this.x=this.curve.one,this.z=this.curve.zero):(this.x=new n(t,16),this.z=new n(r,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)))}i(s,o),e.exports=s,s.prototype.validate=function(e){var t=e.normalize().x,r=t.redSqr(),n=r.redMul(t).redAdd(r.redMul(this.a)).redAdd(t);return 0===n.redSqrt().redSqr().cmp(n)},i(f,o.BasePoint),s.prototype.decodePoint=function(e,t){return this.point(a.toArray(e,t),1)},s.prototype.point=function(e,t){return new f(this,e,t)},s.prototype.pointFromJSON=function(e){return f.fromJSON(this,e)},f.prototype.precompute=function(){},f.prototype._encode=function(){return this.getX().toArray("be",this.curve.p.byteLength())},f.fromJSON=function(e,t){return new f(e,t[0],t[1]||e.one)},f.prototype.inspect=function(){return this.isInfinity()?"":""},f.prototype.isInfinity=function(){return 0===this.z.cmpn(0)},f.prototype.dbl=function(){var e=this.x.redAdd(this.z).redSqr(),t=this.x.redSub(this.z).redSqr(),r=e.redSub(t),n=e.redMul(t),i=r.redMul(t.redAdd(this.curve.a24.redMul(r)));return this.curve.point(n,i)},f.prototype.add=function(){throw new Error("Not supported on Montgomery curve")},f.prototype.diffAdd=function(e,t){var r=this.x.redAdd(this.z),n=this.x.redSub(this.z),i=e.x.redAdd(e.z),o=e.x.redSub(e.z).redMul(r),a=i.redMul(n),s=t.z.redMul(o.redAdd(a).redSqr()),f=t.x.redMul(o.redISub(a).redSqr());return this.curve.point(s,f)},f.prototype.mul=function(e){for(var t=e.clone(),r=this,n=this.curve.point(null,null),i=[];0!==t.cmpn(0);t.iushrn(1))i.push(t.andln(1));for(var o=i.length-1;o>=0;o--)0===i[o]?(r=r.diffAdd(n,this),n=n.dbl()):(n=r.diffAdd(n,this),r=r.dbl());return n},f.prototype.mulAdd=function(){throw new Error("Not supported on Montgomery curve")},f.prototype.jumlAdd=function(){throw new Error("Not supported on Montgomery curve")},f.prototype.eq=function(e){return 0===this.getX().cmp(e.getX())},f.prototype.normalize=function(){return this.x=this.x.redMul(this.z.redInvm()),this.z=this.curve.one,this},f.prototype.getX=function(){return this.normalize(),this.x.fromRed()}},function(e,t,r){"use strict";var n=r(11),i=r(6),o=r(2),a=r(45),s=n.assert;function f(e){this.twisted=1!=(0|e.a),this.mOneA=this.twisted&&-1==(0|e.a),this.extended=this.mOneA,a.call(this,"edwards",e),this.a=new i(e.a,16).umod(this.red.m),this.a=this.a.toRed(this.red),this.c=new i(e.c,16).toRed(this.red),this.c2=this.c.redSqr(),this.d=new i(e.d,16).toRed(this.red),this.dd=this.d.redAdd(this.d),s(!this.twisted||0===this.c.fromRed().cmpn(1)),this.oneC=1==(0|e.c)}function c(e,t,r,n,o){a.BasePoint.call(this,e,"projective"),null===t&&null===r&&null===n?(this.x=this.curve.zero,this.y=this.curve.one,this.z=this.curve.one,this.t=this.curve.zero,this.zOne=!0):(this.x=new i(t,16),this.y=new i(r,16),this.z=n?new i(n,16):this.curve.one,this.t=o&&new i(o,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)),this.t&&!this.t.red&&(this.t=this.t.toRed(this.curve.red)),this.zOne=this.z===this.curve.one,this.curve.extended&&!this.t&&(this.t=this.x.redMul(this.y),this.zOne||(this.t=this.t.redMul(this.z.redInvm()))))}o(f,a),e.exports=f,f.prototype._mulA=function(e){return this.mOneA?e.redNeg():this.a.redMul(e)},f.prototype._mulC=function(e){return this.oneC?e:this.c.redMul(e)},f.prototype.jpoint=function(e,t,r,n){return this.point(e,t,r,n)},f.prototype.pointFromX=function(e,t){(e=new i(e,16)).red||(e=e.toRed(this.red));var r=e.redSqr(),n=this.c2.redSub(this.a.redMul(r)),o=this.one.redSub(this.c2.redMul(this.d).redMul(r)),a=n.redMul(o.redInvm()),s=a.redSqrt();if(0!==s.redSqr().redSub(a).cmp(this.zero))throw new Error("invalid point");var f=s.fromRed().isOdd();return(t&&!f||!t&&f)&&(s=s.redNeg()),this.point(e,s)},f.prototype.pointFromY=function(e,t){(e=new i(e,16)).red||(e=e.toRed(this.red));var r=e.redSqr(),n=r.redSub(this.c2),o=r.redMul(this.d).redMul(this.c2).redSub(this.a),a=n.redMul(o.redInvm());if(0===a.cmp(this.zero)){if(t)throw new Error("invalid point");return this.point(this.zero,e)}var s=a.redSqrt();if(0!==s.redSqr().redSub(a).cmp(this.zero))throw new Error("invalid point");return s.fromRed().isOdd()!==t&&(s=s.redNeg()),this.point(s,e)},f.prototype.validate=function(e){if(e.isInfinity())return!0;e.normalize();var t=e.x.redSqr(),r=e.y.redSqr(),n=t.redMul(this.a).redAdd(r),i=this.c2.redMul(this.one.redAdd(this.d.redMul(t).redMul(r)));return 0===n.cmp(i)},o(c,a.BasePoint),f.prototype.pointFromJSON=function(e){return c.fromJSON(this,e)},f.prototype.point=function(e,t,r,n){return new c(this,e,t,r,n)},c.fromJSON=function(e,t){return new c(e,t[0],t[1],t[2])},c.prototype.inspect=function(){return this.isInfinity()?"":""},c.prototype.isInfinity=function(){return 0===this.x.cmpn(0)&&(0===this.y.cmp(this.z)||this.zOne&&0===this.y.cmp(this.curve.c))},c.prototype._extDbl=function(){var e=this.x.redSqr(),t=this.y.redSqr(),r=this.z.redSqr();r=r.redIAdd(r);var n=this.curve._mulA(e),i=this.x.redAdd(this.y).redSqr().redISub(e).redISub(t),o=n.redAdd(t),a=o.redSub(r),s=n.redSub(t),f=i.redMul(a),c=o.redMul(s),u=i.redMul(s),h=a.redMul(o);return this.curve.point(f,c,h,u)},c.prototype._projDbl=function(){var e,t,r,n=this.x.redAdd(this.y).redSqr(),i=this.x.redSqr(),o=this.y.redSqr();if(this.curve.twisted){var a=(c=this.curve._mulA(i)).redAdd(o);if(this.zOne)e=n.redSub(i).redSub(o).redMul(a.redSub(this.curve.two)),t=a.redMul(c.redSub(o)),r=a.redSqr().redSub(a).redSub(a);else{var s=this.z.redSqr(),f=a.redSub(s).redISub(s);e=n.redSub(i).redISub(o).redMul(f),t=a.redMul(c.redSub(o)),r=a.redMul(f)}}else{var c=i.redAdd(o);s=this.curve._mulC(this.z).redSqr(),f=c.redSub(s).redSub(s),e=this.curve._mulC(n.redISub(c)).redMul(f),t=this.curve._mulC(c).redMul(i.redISub(o)),r=c.redMul(f)}return this.curve.point(e,t,r)},c.prototype.dbl=function(){return this.isInfinity()?this:this.curve.extended?this._extDbl():this._projDbl()},c.prototype._extAdd=function(e){var t=this.y.redSub(this.x).redMul(e.y.redSub(e.x)),r=this.y.redAdd(this.x).redMul(e.y.redAdd(e.x)),n=this.t.redMul(this.curve.dd).redMul(e.t),i=this.z.redMul(e.z.redAdd(e.z)),o=r.redSub(t),a=i.redSub(n),s=i.redAdd(n),f=r.redAdd(t),c=o.redMul(a),u=s.redMul(f),h=o.redMul(f),l=a.redMul(s);return this.curve.point(c,u,l,h)},c.prototype._projAdd=function(e){var t,r,n=this.z.redMul(e.z),i=n.redSqr(),o=this.x.redMul(e.x),a=this.y.redMul(e.y),s=this.curve.d.redMul(o).redMul(a),f=i.redSub(s),c=i.redAdd(s),u=this.x.redAdd(this.y).redMul(e.x.redAdd(e.y)).redISub(o).redISub(a),h=n.redMul(f).redMul(u);return this.curve.twisted?(t=n.redMul(c).redMul(a.redSub(this.curve._mulA(o))),r=f.redMul(c)):(t=n.redMul(c).redMul(a.redSub(o)),r=this.curve._mulC(f).redMul(c)),this.curve.point(h,t,r)},c.prototype.add=function(e){return this.isInfinity()?e:e.isInfinity()?this:this.curve.extended?this._extAdd(e):this._projAdd(e)},c.prototype.mul=function(e){return this._hasDoubles(e)?this.curve._fixedNafMul(this,e):this.curve._wnafMul(this,e)},c.prototype.mulAdd=function(e,t,r){return this.curve._wnafMulAdd(1,[this,t],[e,r],2,!1)},c.prototype.jmulAdd=function(e,t,r){return this.curve._wnafMulAdd(1,[this,t],[e,r],2,!0)},c.prototype.normalize=function(){if(this.zOne)return this;var e=this.z.redInvm();return this.x=this.x.redMul(e),this.y=this.y.redMul(e),this.t&&(this.t=this.t.redMul(e)),this.z=this.curve.one,this.zOne=!0,this},c.prototype.neg=function(){return this.curve.point(this.x.redNeg(),this.y,this.z,this.t&&this.t.redNeg())},c.prototype.getX=function(){return this.normalize(),this.x.fromRed()},c.prototype.getY=function(){return this.normalize(),this.y.fromRed()},c.prototype.eq=function(e){return this===e||0===this.getX().cmp(e.getX())&&0===this.getY().cmp(e.getY())},c.prototype.eqXToP=function(e){var t=e.toRed(this.curve.red).redMul(this.z);if(0===this.x.cmp(t))return!0;for(var r=e.clone(),n=this.curve.redN.redMul(this.z);;){if(r.iadd(this.curve.n),r.cmp(this.curve.p)>=0)return!1;if(t.redIAdd(n),0===this.x.cmp(t))return!0}},c.prototype.toP=c.prototype.normalize,c.prototype.mixedAdd=c.prototype.add},function(e,t,r){"use strict";t.sha1=r(185),t.sha224=r(186),t.sha256=r(99),t.sha384=r(187),t.sha512=r(100)},function(e,t,r){"use strict";var n=r(17),i=r(37),o=r(98),a=n.rotl32,s=n.sum32,f=n.sum32_5,c=o.ft_1,u=i.BlockHash,h=[1518500249,1859775393,2400959708,3395469782];function l(){if(!(this instanceof l))return new l;u.call(this),this.h=[1732584193,4023233417,2562383102,271733878,3285377520],this.W=new Array(80)}n.inherits(l,u),e.exports=l,l.blockSize=512,l.outSize=160,l.hmacStrength=80,l.padLength=64,l.prototype._update=function(e,t){for(var r=this.W,n=0;n<16;n++)r[n]=e[t+n];for(;nthis.blockSize&&(e=(new this.Hash).update(e).digest()),i(e.length<=this.blockSize);for(var t=e.length;t0))return a.iaddn(1),this.keyFromPrivate(a)}},h.prototype._truncateToN=function(e,t){var r=8*e.byteLength()-this.n.bitLength();return r>0&&(e=e.ushrn(r)),!t&&e.cmp(this.n)>=0?e.sub(this.n):e},h.prototype.sign=function(e,t,r,o){"object"==typeof r&&(o=r,r=null),o||(o={}),t=this.keyFromPrivate(t,r),e=this._truncateToN(new n(e,16));for(var a=this.n.byteLength(),s=t.getPrivate().toArray("be",a),f=e.toArray("be",a),c=new i({hash:this.hash,entropy:s,nonce:f,pers:o.pers,persEnc:o.persEnc||"utf8"}),h=this.n.sub(new n(1)),l=0;;l++){var d=o.k?o.k(l):new n(c.generate(this.n.byteLength()));if(!((d=this._truncateToN(d,!0)).cmpn(1)<=0||d.cmp(h)>=0)){var p=this.g.mul(d);if(!p.isInfinity()){var y=p.getX(),b=y.umod(this.n);if(0!==b.cmpn(0)){var v=d.invm(this.n).mul(b.mul(t.getPrivate()).iadd(e));if(0!==(v=v.umod(this.n)).cmpn(0)){var g=(p.getY().isOdd()?1:0)|(0!==y.cmp(b)?2:0);return o.canonical&&v.cmp(this.nh)>0&&(v=this.n.sub(v),g^=1),new u({r:b,s:v,recoveryParam:g})}}}}}},h.prototype.verify=function(e,t,r,i){e=this._truncateToN(new n(e,16)),r=this.keyFromPublic(r,i);var o=(t=new u(t,"hex")).r,a=t.s;if(o.cmpn(1)<0||o.cmp(this.n)>=0)return!1;if(a.cmpn(1)<0||a.cmp(this.n)>=0)return!1;var s,f=a.invm(this.n),c=f.mul(e).umod(this.n),h=f.mul(o).umod(this.n);return this.curve._maxwellTrick?!(s=this.g.jmulAdd(c,r.getPublic(),h)).isInfinity()&&s.eqXToP(o):!(s=this.g.mulAdd(c,r.getPublic(),h)).isInfinity()&&0===s.getX().umod(this.n).cmp(o)},h.prototype.recoverPubKey=function(e,t,r,i){f((3&r)===r,"The recovery param is more than two bits"),t=new u(t,i);var o=this.n,a=new n(e),s=t.r,c=t.s,h=1&r,l=r>>1;if(s.cmp(this.curve.p.umod(this.curve.n))>=0&&l)throw new Error("Unable to find sencond key candinate");s=l?this.curve.pointFromX(s.add(this.curve.n),h):this.curve.pointFromX(s,h);var d=t.r.invm(o),p=o.sub(a).mul(d).umod(o),y=c.mul(d).umod(o);return this.g.mulAdd(p,s,y)},h.prototype.getKeyRecoveryParam=function(e,t,r,n){if(null!==(t=new u(t,n)).recoveryParam)return t.recoveryParam;for(var i=0;i<4;i++){var o;try{o=this.recoverPubKey(e,t,i)}catch(e){continue}if(o.eq(r))return i}throw new Error("Unable to find valid recovery factor")}},function(e,t,r){"use strict";var n=r(64),i=r(96),o=r(12);function a(e){if(!(this instanceof a))return new a(e);this.hash=e.hash,this.predResist=!!e.predResist,this.outLen=this.hash.outSize,this.minEntropy=e.minEntropy||this.hash.hmacStrength,this._reseed=null,this.reseedInterval=null,this.K=null,this.V=null;var t=i.toArray(e.entropy,e.entropyEnc||"hex"),r=i.toArray(e.nonce,e.nonceEnc||"hex"),n=i.toArray(e.pers,e.persEnc||"hex");o(t.length>=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._init(t,r,n)}e.exports=a,a.prototype._init=function(e,t,r){var n=e.concat(t).concat(r);this.K=new Array(this.outLen/8),this.V=new Array(this.outLen/8);for(var i=0;i=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._update(e.concat(r||[])),this._reseed=1},a.prototype.generate=function(e,t,r,n){if(this._reseed>this.reseedInterval)throw new Error("Reseed is required");"string"!=typeof t&&(n=r,r=t,t=null),r&&(r=i.toArray(r,n||"hex"),this._update(r));for(var o=[];o.length"}},function(e,t,r){"use strict";var n=r(6),i=r(11),o=i.assert;function a(e,t){if(e instanceof a)return e;this._importDER(e,t)||(o(e.r&&e.s,"Signature without r or s"),this.r=new n(e.r,16),this.s=new n(e.s,16),void 0===e.recoveryParam?this.recoveryParam=null:this.recoveryParam=e.recoveryParam)}function s(){this.place=0}function f(e,t){var r=e[t.place++];if(!(128&r))return r;for(var n=15&r,i=0,o=0,a=t.place;o>>3);for(e.push(128|r);--r;)e.push(t>>>(r<<3)&255);e.push(t)}}e.exports=a,a.prototype._importDER=function(e,t){e=i.toArray(e,t);var r=new s;if(48!==e[r.place++])return!1;if(f(e,r)+r.place!==e.length)return!1;if(2!==e[r.place++])return!1;var o=f(e,r),a=e.slice(r.place,o+r.place);if(r.place+=o,2!==e[r.place++])return!1;var c=f(e,r);if(e.length!==c+r.place)return!1;var u=e.slice(r.place,c+r.place);return 0===a[0]&&128&a[1]&&(a=a.slice(1)),0===u[0]&&128&u[1]&&(u=u.slice(1)),this.r=new n(a),this.s=new n(u),this.recoveryParam=null,!0},a.prototype.toDER=function(e){var t=this.r.toArray(),r=this.s.toArray();for(128&t[0]&&(t=[0].concat(t)),128&r[0]&&(r=[0].concat(r)),t=c(t),r=c(r);!(r[0]||128&r[1]);)r=r.slice(1);var n=[2];u(n,t.length),(n=n.concat(t)).push(2),u(n,r.length);var o=n.concat(r),a=[48];return u(a,o.length),a=a.concat(o),i.encode(a,e)}},function(e,t,r){"use strict";var n=r(64),i=r(63),o=r(11),a=o.assert,s=o.parseBytes,f=r(196),c=r(197);function u(e){if(a("ed25519"===e,"only tested with ed25519 so far"),!(this instanceof u))return new u(e);e=i[e].curve,this.curve=e,this.g=e.g,this.g.precompute(e.n.bitLength()+1),this.pointClass=e.point().constructor,this.encodingLength=Math.ceil(e.n.bitLength()/8),this.hash=n.sha512}e.exports=u,u.prototype.sign=function(e,t){e=s(e);var r=this.keyFromSecret(t),n=this.hashInt(r.messagePrefix(),e),i=this.g.mul(n),o=this.encodePoint(i),a=this.hashInt(o,r.pubBytes(),e).mul(r.priv()),f=n.add(a).umod(this.curve.n);return this.makeSignature({R:i,S:f,Rencoded:o})},u.prototype.verify=function(e,t,r){e=s(e),t=this.makeSignature(t);var n=this.keyFromPublic(r),i=this.hashInt(t.Rencoded(),n.pubBytes(),e),o=this.g.mul(t.S());return t.R().add(n.pub().mul(i)).eq(o)},u.prototype.hashInt=function(){for(var e=this.hash(),t=0;t0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,r=""+t.data;t=t.next;)r+=e+t.data;return r},e.prototype.concat=function(e){if(0===this.length)return n.alloc(0);if(1===this.length)return this.head.data;for(var t,r,i=n.allocUnsafe(e>>>0),o=this.head,a=0;o;)t=i,r=a,o.data.copy(t,r),a+=o.data.length,o=o.next;return i},e}(),i&&i.inspect&&i.inspect.custom&&(e.exports.prototype[i.inspect.custom]=function(){var e=i.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,r){(function(e,t){!function(e,r){"use strict";if(!e.setImmediate){var n,i,o,a,s,f=1,c={},u=!1,h=e.document,l=Object.getPrototypeOf&&Object.getPrototypeOf(e);l=l&&l.setTimeout?l:e,"[object process]"==={}.toString.call(e.process)?n=function(e){t.nextTick((function(){p(e)}))}:function(){if(e.postMessage&&!e.importScripts){var t=!0,r=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=r,t}}()?(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&p(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),n=function(t){e.postMessage(a+t,"*")}):e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){p(e.data)},n=function(e){o.port2.postMessage(e)}):h&&"onreadystatechange"in h.createElement("script")?(i=h.documentElement,n=function(e){var t=h.createElement("script");t.onreadystatechange=function(){p(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):n=function(e){setTimeout(p,0,e)},l.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r>>2}function u(e,t,r,n){return 0===e?t&r|~t&n:2===e?t&r|t&n|r&n:t^r^n}n(f,i),f.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},f.prototype._update=function(e){for(var t,r=this._w,n=0|this._a,i=0|this._b,o=0|this._c,s=0|this._d,f=0|this._e,h=0;h<16;++h)r[h]=e.readInt32BE(4*h);for(;h<80;++h)r[h]=r[h-3]^r[h-8]^r[h-14]^r[h-16];for(var l=0;l<80;++l){var d=~~(l/20),p=0|((t=n)<<5|t>>>27)+u(d,i,o,s)+f+r[l]+a[d];f=s,s=o,o=c(i),i=n,n=p}this._a=n+this._a|0,this._b=i+this._b|0,this._c=o+this._c|0,this._d=s+this._d|0,this._e=f+this._e|0},f.prototype._hash=function(){var e=o.allocUnsafe(20);return e.writeInt32BE(0|this._a,0),e.writeInt32BE(0|this._b,4),e.writeInt32BE(0|this._c,8),e.writeInt32BE(0|this._d,12),e.writeInt32BE(0|this._e,16),e},e.exports=f},function(e,t,r){var n=r(2),i=r(28),o=r(0).Buffer,a=[1518500249,1859775393,-1894007588,-899497514],s=new Array(80);function f(){this.init(),this._w=s,i.call(this,64,56)}function c(e){return e<<5|e>>>27}function u(e){return e<<30|e>>>2}function h(e,t,r,n){return 0===e?t&r|~t&n:2===e?t&r|t&n|r&n:t^r^n}n(f,i),f.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},f.prototype._update=function(e){for(var t,r=this._w,n=0|this._a,i=0|this._b,o=0|this._c,s=0|this._d,f=0|this._e,l=0;l<16;++l)r[l]=e.readInt32BE(4*l);for(;l<80;++l)r[l]=(t=r[l-3]^r[l-8]^r[l-14]^r[l-16])<<1|t>>>31;for(var d=0;d<80;++d){var p=~~(d/20),y=c(n)+h(p,i,o,s)+f+r[d]+a[p]|0;f=s,s=o,o=u(i),i=n,n=y}this._a=n+this._a|0,this._b=i+this._b|0,this._c=o+this._c|0,this._d=s+this._d|0,this._e=f+this._e|0},f.prototype._hash=function(){var e=o.allocUnsafe(20);return e.writeInt32BE(0|this._a,0),e.writeInt32BE(0|this._b,4),e.writeInt32BE(0|this._c,8),e.writeInt32BE(0|this._d,12),e.writeInt32BE(0|this._e,16),e},e.exports=f},function(e,t,r){var n=r(2),i=r(107),o=r(28),a=r(0).Buffer,s=new Array(64);function f(){this.init(),this._w=s,o.call(this,64,56)}n(f,i),f.prototype.init=function(){return this._a=3238371032,this._b=914150663,this._c=812702999,this._d=4144912697,this._e=4290775857,this._f=1750603025,this._g=1694076839,this._h=3204075428,this},f.prototype._hash=function(){var e=a.allocUnsafe(28);return e.writeInt32BE(this._a,0),e.writeInt32BE(this._b,4),e.writeInt32BE(this._c,8),e.writeInt32BE(this._d,12),e.writeInt32BE(this._e,16),e.writeInt32BE(this._f,20),e.writeInt32BE(this._g,24),e},e.exports=f},function(e,t,r){var n=r(2),i=r(108),o=r(28),a=r(0).Buffer,s=new Array(160);function f(){this.init(),this._w=s,o.call(this,128,112)}n(f,i),f.prototype.init=function(){return this._ah=3418070365,this._bh=1654270250,this._ch=2438529370,this._dh=355462360,this._eh=1731405415,this._fh=2394180231,this._gh=3675008525,this._hh=1203062813,this._al=3238371032,this._bl=914150663,this._cl=812702999,this._dl=4144912697,this._el=4290775857,this._fl=1750603025,this._gl=1694076839,this._hl=3204075428,this},f.prototype._hash=function(){var e=a.allocUnsafe(48);function t(t,r,n){e.writeInt32BE(t,n),e.writeInt32BE(r,n+4)}return t(this._ah,this._al,0),t(this._bh,this._bl,8),t(this._ch,this._cl,16),t(this._dh,this._dl,24),t(this._eh,this._el,32),t(this._fh,this._fl,40),e},e.exports=f},function(e,t,r){(function(t,n){var i,o=r(109),a=r(110),s=r(111),f=r(0).Buffer,c=t.crypto&&t.crypto.subtle,u={sha:"SHA-1","sha-1":"SHA-1",sha1:"SHA-1",sha256:"SHA-256","sha-256":"SHA-256",sha384:"SHA-384","sha-384":"SHA-384","sha-512":"SHA-512",sha512:"SHA-512"},h=[];function l(e,t,r,n,i){return c.importKey("raw",e,{name:"PBKDF2"},!1,["deriveBits"]).then((function(e){return c.deriveBits({name:"PBKDF2",salt:t,iterations:r,hash:{name:i}},e,n<<3)})).then((function(e){return f.from(e)}))}e.exports=function(e,r,d,p,y,b){"function"==typeof y&&(b=y,y=void 0);var v=u[(y=y||"sha1").toLowerCase()];if(!v||"function"!=typeof t.Promise)return n.nextTick((function(){var t;try{t=s(e,r,d,p,y)}catch(e){return b(e)}b(null,t)}));if(o(e,r,d,p),"function"!=typeof b)throw new Error("No callback provided to pbkdf2");f.isBuffer(e)||(e=f.from(e,a)),f.isBuffer(r)||(r=f.from(r,a)),function(e,t){e.then((function(e){n.nextTick((function(){t(null,e)}))}),(function(e){n.nextTick((function(){t(e)}))}))}(function(e){if(t.process&&!t.process.browser)return Promise.resolve(!1);if(!c||!c.importKey||!c.deriveBits)return Promise.resolve(!1);if(void 0!==h[e])return h[e];var r=l(i=i||f.alloc(8),i,10,128,e).then((function(){return!0})).catch((function(){return!1}));return h[e]=r,r}(v).then((function(t){return t?l(e,r,d,p,v):s(e,r,d,p,y)})),b)}}).call(this,r(10),r(16))},function(e){e.exports=JSON.parse(\'["abandon","ability","able","about","above","absent","absorb","abstract","absurd","abuse","access","accident","account","accuse","achieve","acid","acoustic","acquire","across","act","action","actor","actress","actual","adapt","add","addict","address","adjust","admit","adult","advance","advice","aerobic","affair","afford","afraid","again","age","agent","agree","ahead","aim","air","airport","aisle","alarm","album","alcohol","alert","alien","all","alley","allow","almost","alone","alpha","already","also","alter","always","amateur","amazing","among","amount","amused","analyst","anchor","ancient","anger","angle","angry","animal","ankle","announce","annual","another","answer","antenna","antique","anxiety","any","apart","apology","appear","apple","approve","april","arch","arctic","area","arena","argue","arm","armed","armor","army","around","arrange","arrest","arrive","arrow","art","artefact","artist","artwork","ask","aspect","assault","asset","assist","assume","asthma","athlete","atom","attack","attend","attitude","attract","auction","audit","august","aunt","author","auto","autumn","average","avocado","avoid","awake","aware","away","awesome","awful","awkward","axis","baby","bachelor","bacon","badge","bag","balance","balcony","ball","bamboo","banana","banner","bar","barely","bargain","barrel","base","basic","basket","battle","beach","bean","beauty","because","become","beef","before","begin","behave","behind","believe","below","belt","bench","benefit","best","betray","better","between","beyond","bicycle","bid","bike","bind","biology","bird","birth","bitter","black","blade","blame","blanket","blast","bleak","bless","blind","blood","blossom","blouse","blue","blur","blush","board","boat","body","boil","bomb","bone","bonus","book","boost","border","boring","borrow","boss","bottom","bounce","box","boy","bracket","brain","brand","brass","brave","bread","breeze","brick","bridge","brief","bright","bring","brisk","broccoli","broken","bronze","broom","brother","brown","brush","bubble","buddy","budget","buffalo","build","bulb","bulk","bullet","bundle","bunker","burden","burger","burst","bus","business","busy","butter","buyer","buzz","cabbage","cabin","cable","cactus","cage","cake","call","calm","camera","camp","can","canal","cancel","candy","cannon","canoe","canvas","canyon","capable","capital","captain","car","carbon","card","cargo","carpet","carry","cart","case","cash","casino","castle","casual","cat","catalog","catch","category","cattle","caught","cause","caution","cave","ceiling","celery","cement","census","century","cereal","certain","chair","chalk","champion","change","chaos","chapter","charge","chase","chat","cheap","check","cheese","chef","cherry","chest","chicken","chief","child","chimney","choice","choose","chronic","chuckle","chunk","churn","cigar","cinnamon","circle","citizen","city","civil","claim","clap","clarify","claw","clay","clean","clerk","clever","click","client","cliff","climb","clinic","clip","clock","clog","close","cloth","cloud","clown","club","clump","cluster","clutch","coach","coast","coconut","code","coffee","coil","coin","collect","color","column","combine","come","comfort","comic","common","company","concert","conduct","confirm","congress","connect","consider","control","convince","cook","cool","copper","copy","coral","core","corn","correct","cost","cotton","couch","country","couple","course","cousin","cover","coyote","crack","cradle","craft","cram","crane","crash","crater","crawl","crazy","cream","credit","creek","crew","cricket","crime","crisp","critic","crop","cross","crouch","crowd","crucial","cruel","cruise","crumble","crunch","crush","cry","crystal","cube","culture","cup","cupboard","curious","current","curtain","curve","cushion","custom","cute","cycle","dad","damage","damp","dance","danger","daring","dash","daughter","dawn","day","deal","debate","debris","decade","december","decide","decline","decorate","decrease","deer","defense","define","defy","degree","delay","deliver","demand","demise","denial","dentist","deny","depart","depend","deposit","depth","deputy","derive","describe","desert","design","desk","despair","destroy","detail","detect","develop","device","devote","diagram","dial","diamond","diary","dice","diesel","diet","differ","digital","dignity","dilemma","dinner","dinosaur","direct","dirt","disagree","discover","disease","dish","dismiss","disorder","display","distance","divert","divide","divorce","dizzy","doctor","document","dog","doll","dolphin","domain","donate","donkey","donor","door","dose","double","dove","draft","dragon","drama","drastic","draw","dream","dress","drift","drill","drink","drip","drive","drop","drum","dry","duck","dumb","dune","during","dust","dutch","duty","dwarf","dynamic","eager","eagle","early","earn","earth","easily","east","easy","echo","ecology","economy","edge","edit","educate","effort","egg","eight","either","elbow","elder","electric","elegant","element","elephant","elevator","elite","else","embark","embody","embrace","emerge","emotion","employ","empower","empty","enable","enact","end","endless","endorse","enemy","energy","enforce","engage","engine","enhance","enjoy","enlist","enough","enrich","enroll","ensure","enter","entire","entry","envelope","episode","equal","equip","era","erase","erode","erosion","error","erupt","escape","essay","essence","estate","eternal","ethics","evidence","evil","evoke","evolve","exact","example","excess","exchange","excite","exclude","excuse","execute","exercise","exhaust","exhibit","exile","exist","exit","exotic","expand","expect","expire","explain","expose","express","extend","extra","eye","eyebrow","fabric","face","faculty","fade","faint","faith","fall","false","fame","family","famous","fan","fancy","fantasy","farm","fashion","fat","fatal","father","fatigue","fault","favorite","feature","february","federal","fee","feed","feel","female","fence","festival","fetch","fever","few","fiber","fiction","field","figure","file","film","filter","final","find","fine","finger","finish","fire","firm","first","fiscal","fish","fit","fitness","fix","flag","flame","flash","flat","flavor","flee","flight","flip","float","flock","floor","flower","fluid","flush","fly","foam","focus","fog","foil","fold","follow","food","foot","force","forest","forget","fork","fortune","forum","forward","fossil","foster","found","fox","fragile","frame","frequent","fresh","friend","fringe","frog","front","frost","frown","frozen","fruit","fuel","fun","funny","furnace","fury","future","gadget","gain","galaxy","gallery","game","gap","garage","garbage","garden","garlic","garment","gas","gasp","gate","gather","gauge","gaze","general","genius","genre","gentle","genuine","gesture","ghost","giant","gift","giggle","ginger","giraffe","girl","give","glad","glance","glare","glass","glide","glimpse","globe","gloom","glory","glove","glow","glue","goat","goddess","gold","good","goose","gorilla","gospel","gossip","govern","gown","grab","grace","grain","grant","grape","grass","gravity","great","green","grid","grief","grit","grocery","group","grow","grunt","guard","guess","guide","guilt","guitar","gun","gym","habit","hair","half","hammer","hamster","hand","happy","harbor","hard","harsh","harvest","hat","have","hawk","hazard","head","health","heart","heavy","hedgehog","height","hello","helmet","help","hen","hero","hidden","high","hill","hint","hip","hire","history","hobby","hockey","hold","hole","holiday","hollow","home","honey","hood","hope","horn","horror","horse","hospital","host","hotel","hour","hover","hub","huge","human","humble","humor","hundred","hungry","hunt","hurdle","hurry","hurt","husband","hybrid","ice","icon","idea","identify","idle","ignore","ill","illegal","illness","image","imitate","immense","immune","impact","impose","improve","impulse","inch","include","income","increase","index","indicate","indoor","industry","infant","inflict","inform","inhale","inherit","initial","inject","injury","inmate","inner","innocent","input","inquiry","insane","insect","inside","inspire","install","intact","interest","into","invest","invite","involve","iron","island","isolate","issue","item","ivory","jacket","jaguar","jar","jazz","jealous","jeans","jelly","jewel","job","join","joke","journey","joy","judge","juice","jump","jungle","junior","junk","just","kangaroo","keen","keep","ketchup","key","kick","kid","kidney","kind","kingdom","kiss","kit","kitchen","kite","kitten","kiwi","knee","knife","knock","know","lab","label","labor","ladder","lady","lake","lamp","language","laptop","large","later","latin","laugh","laundry","lava","law","lawn","lawsuit","layer","lazy","leader","leaf","learn","leave","lecture","left","leg","legal","legend","leisure","lemon","lend","length","lens","leopard","lesson","letter","level","liar","liberty","library","license","life","lift","light","like","limb","limit","link","lion","liquid","list","little","live","lizard","load","loan","lobster","local","lock","logic","lonely","long","loop","lottery","loud","lounge","love","loyal","lucky","luggage","lumber","lunar","lunch","luxury","lyrics","machine","mad","magic","magnet","maid","mail","main","major","make","mammal","man","manage","mandate","mango","mansion","manual","maple","marble","march","margin","marine","market","marriage","mask","mass","master","match","material","math","matrix","matter","maximum","maze","meadow","mean","measure","meat","mechanic","medal","media","melody","melt","member","memory","mention","menu","mercy","merge","merit","merry","mesh","message","metal","method","middle","midnight","milk","million","mimic","mind","minimum","minor","minute","miracle","mirror","misery","miss","mistake","mix","mixed","mixture","mobile","model","modify","mom","moment","monitor","monkey","monster","month","moon","moral","more","morning","mosquito","mother","motion","motor","mountain","mouse","move","movie","much","muffin","mule","multiply","muscle","museum","mushroom","music","must","mutual","myself","mystery","myth","naive","name","napkin","narrow","nasty","nation","nature","near","neck","need","negative","neglect","neither","nephew","nerve","nest","net","network","neutral","never","news","next","nice","night","noble","noise","nominee","noodle","normal","north","nose","notable","note","nothing","notice","novel","now","nuclear","number","nurse","nut","oak","obey","object","oblige","obscure","observe","obtain","obvious","occur","ocean","october","odor","off","offer","office","often","oil","okay","old","olive","olympic","omit","once","one","onion","online","only","open","opera","opinion","oppose","option","orange","orbit","orchard","order","ordinary","organ","orient","original","orphan","ostrich","other","outdoor","outer","output","outside","oval","oven","over","own","owner","oxygen","oyster","ozone","pact","paddle","page","pair","palace","palm","panda","panel","panic","panther","paper","parade","parent","park","parrot","party","pass","patch","path","patient","patrol","pattern","pause","pave","payment","peace","peanut","pear","peasant","pelican","pen","penalty","pencil","people","pepper","perfect","permit","person","pet","phone","photo","phrase","physical","piano","picnic","picture","piece","pig","pigeon","pill","pilot","pink","pioneer","pipe","pistol","pitch","pizza","place","planet","plastic","plate","play","please","pledge","pluck","plug","plunge","poem","poet","point","polar","pole","police","pond","pony","pool","popular","portion","position","possible","post","potato","pottery","poverty","powder","power","practice","praise","predict","prefer","prepare","present","pretty","prevent","price","pride","primary","print","priority","prison","private","prize","problem","process","produce","profit","program","project","promote","proof","property","prosper","protect","proud","provide","public","pudding","pull","pulp","pulse","pumpkin","punch","pupil","puppy","purchase","purity","purpose","purse","push","put","puzzle","pyramid","quality","quantum","quarter","question","quick","quit","quiz","quote","rabbit","raccoon","race","rack","radar","radio","rail","rain","raise","rally","ramp","ranch","random","range","rapid","rare","rate","rather","raven","raw","razor","ready","real","reason","rebel","rebuild","recall","receive","recipe","record","recycle","reduce","reflect","reform","refuse","region","regret","regular","reject","relax","release","relief","rely","remain","remember","remind","remove","render","renew","rent","reopen","repair","repeat","replace","report","require","rescue","resemble","resist","resource","response","result","retire","retreat","return","reunion","reveal","review","reward","rhythm","rib","ribbon","rice","rich","ride","ridge","rifle","right","rigid","ring","riot","ripple","risk","ritual","rival","river","road","roast","robot","robust","rocket","romance","roof","rookie","room","rose","rotate","rough","round","route","royal","rubber","rude","rug","rule","run","runway","rural","sad","saddle","sadness","safe","sail","salad","salmon","salon","salt","salute","same","sample","sand","satisfy","satoshi","sauce","sausage","save","say","scale","scan","scare","scatter","scene","scheme","school","science","scissors","scorpion","scout","scrap","screen","script","scrub","sea","search","season","seat","second","secret","section","security","seed","seek","segment","select","sell","seminar","senior","sense","sentence","series","service","session","settle","setup","seven","shadow","shaft","shallow","share","shed","shell","sheriff","shield","shift","shine","ship","shiver","shock","shoe","shoot","shop","short","shoulder","shove","shrimp","shrug","shuffle","shy","sibling","sick","side","siege","sight","sign","silent","silk","silly","silver","similar","simple","since","sing","siren","sister","situate","six","size","skate","sketch","ski","skill","skin","skirt","skull","slab","slam","sleep","slender","slice","slide","slight","slim","slogan","slot","slow","slush","small","smart","smile","smoke","smooth","snack","snake","snap","sniff","snow","soap","soccer","social","sock","soda","soft","solar","soldier","solid","solution","solve","someone","song","soon","sorry","sort","soul","sound","soup","source","south","space","spare","spatial","spawn","speak","special","speed","spell","spend","sphere","spice","spider","spike","spin","spirit","split","spoil","sponsor","spoon","sport","spot","spray","spread","spring","spy","square","squeeze","squirrel","stable","stadium","staff","stage","stairs","stamp","stand","start","state","stay","steak","steel","stem","step","stereo","stick","still","sting","stock","stomach","stone","stool","story","stove","strategy","street","strike","strong","struggle","student","stuff","stumble","style","subject","submit","subway","success","such","sudden","suffer","sugar","suggest","suit","summer","sun","sunny","sunset","super","supply","supreme","sure","surface","surge","surprise","surround","survey","suspect","sustain","swallow","swamp","swap","swarm","swear","sweet","swift","swim","swing","switch","sword","symbol","symptom","syrup","system","table","tackle","tag","tail","talent","talk","tank","tape","target","task","taste","tattoo","taxi","teach","team","tell","ten","tenant","tennis","tent","term","test","text","thank","that","theme","then","theory","there","they","thing","this","thought","three","thrive","throw","thumb","thunder","ticket","tide","tiger","tilt","timber","time","tiny","tip","tired","tissue","title","toast","tobacco","today","toddler","toe","together","toilet","token","tomato","tomorrow","tone","tongue","tonight","tool","tooth","top","topic","topple","torch","tornado","tortoise","toss","total","tourist","toward","tower","town","toy","track","trade","traffic","tragic","train","transfer","trap","trash","travel","tray","treat","tree","trend","trial","tribe","trick","trigger","trim","trip","trophy","trouble","truck","true","truly","trumpet","trust","truth","try","tube","tuition","tumble","tuna","tunnel","turkey","turn","turtle","twelve","twenty","twice","twin","twist","two","type","typical","ugly","umbrella","unable","unaware","uncle","uncover","under","undo","unfair","unfold","unhappy","uniform","unique","unit","universe","unknown","unlock","until","unusual","unveil","update","upgrade","uphold","upon","upper","upset","urban","urge","usage","use","used","useful","useless","usual","utility","vacant","vacuum","vague","valid","valley","valve","van","vanish","vapor","various","vast","vault","vehicle","velvet","vendor","venture","venue","verb","verify","version","very","vessel","veteran","viable","vibrant","vicious","victory","video","view","village","vintage","violin","virtual","virus","visa","visit","visual","vital","vivid","vocal","voice","void","volcano","volume","vote","voyage","wage","wagon","wait","walk","wall","walnut","want","warfare","warm","warrior","wash","wasp","waste","water","wave","way","wealth","weapon","wear","weasel","weather","web","wedding","weekend","weird","welcome","west","wet","whale","what","wheat","wheel","when","where","whip","whisper","wide","width","wife","wild","will","win","window","wine","wing","wink","winner","winter","wire","wisdom","wise","wish","witness","wolf","woman","wonder","wood","wool","word","work","world","worry","worth","wrap","wreck","wrestle","wrist","write","wrong","yard","year","yellow","you","young","youth","zebra","zero","zone","zoo"]\')},function(e,t,r){"use strict";(function(t){var n=r(114);\n/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */function i(e,t){if(e===t)return 0;for(var r=e.length,n=t.length,i=0,o=Math.min(r,n);i=0;c--)if(u[c]!==h[c])return!1;for(c=u.length-1;c>=0;c--)if(!m(e[s=u[c]],t[s],r,n))return!1;return!0}(e,t,r,n))}return r?e===t:e==t}function w(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function _(e,t){if(!e||!t)return!1;if("[object RegExp]"==Object.prototype.toString.call(t))return t.test(e);try{if(e instanceof t)return!0}catch(e){}return!Error.isPrototypeOf(t)&&!0===t.call({},e)}function E(e,t,r,n){var i;if("function"!=typeof t)throw new TypeError(\'"block" argument must be a function\');"string"==typeof r&&(n=r,r=null),i=function(e){var t;try{e()}catch(e){t=e}return t}(t),n=(r&&r.name?" ("+r.name+").":".")+(n?" "+n:"."),e&&!i&&v(i,r,"Missing expected exception"+n);var o="string"==typeof n,s=!e&&i&&!r;if((!e&&a.isError(i)&&o&&_(i,r)||s)&&v(i,r,"Got unwanted exception"+n),e&&i&&r&&!_(i,r)||!e&&i)throw i}l.AssertionError=function(e){this.name="AssertionError",this.actual=e.actual,this.expected=e.expected,this.operator=e.operator,e.message?(this.message=e.message,this.generatedMessage=!1):(this.message=function(e){return y(b(e.actual),128)+" "+e.operator+" "+y(b(e.expected),128)}(this),this.generatedMessage=!0);var t=e.stackStartFunction||v;if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var r=new Error;if(r.stack){var n=r.stack,i=p(t),o=n.indexOf("\\n"+i);if(o>=0){var a=n.indexOf("\\n",o+1);n=n.substring(a+1)}this.stack=n}}},a.inherits(l.AssertionError,Error),l.fail=v,l.ok=g,l.equal=function(e,t,r){e!=t&&v(e,t,r,"==",l.equal)},l.notEqual=function(e,t,r){e==t&&v(e,t,r,"!=",l.notEqual)},l.deepEqual=function(e,t,r){m(e,t,!1)||v(e,t,r,"deepEqual",l.deepEqual)},l.deepStrictEqual=function(e,t,r){m(e,t,!0)||v(e,t,r,"deepStrictEqual",l.deepStrictEqual)},l.notDeepEqual=function(e,t,r){m(e,t,!1)&&v(e,t,r,"notDeepEqual",l.notDeepEqual)},l.notDeepStrictEqual=function e(t,r,n){m(t,r,!0)&&v(t,r,n,"notDeepStrictEqual",e)},l.strictEqual=function(e,t,r){e!==t&&v(e,t,r,"===",l.strictEqual)},l.notStrictEqual=function(e,t,r){e===t&&v(e,t,r,"!==",l.notStrictEqual)},l.throws=function(e,t,r){E(!0,e,t,r)},l.doesNotThrow=function(e,t,r){E(!1,e,t,r)},l.ifError=function(e){if(e)throw e},l.strict=n((function e(t,r){t||v(t,!0,r,"==",e)}),l,{equal:l.strictEqual,deepEqual:l.deepStrictEqual,notEqual:l.notStrictEqual,notDeepEqual:l.notDeepStrictEqual}),l.strict.strict=l.strict;var S=Object.keys||function(e){var t=[];for(var r in e)s.call(e,r)&&t.push(r);return t}}).call(this,r(10))},function(e,t,r){(function(e){var n=Object.getOwnPropertyDescriptors||function(e){for(var t=Object.keys(e),r={},n=0;n=o)return e;switch(e){case"%s":return String(n[r++]);case"%d":return Number(n[r++]);case"%j":try{return JSON.stringify(n[r++])}catch(e){return"[Circular]"}default:return e}})),f=n[r];r=3&&(n.depth=arguments[2]),arguments.length>=4&&(n.colors=arguments[3]),p(r)?n.showHidden=r:r&&t._extend(n,r),g(n.showHidden)&&(n.showHidden=!1),g(n.depth)&&(n.depth=2),g(n.colors)&&(n.colors=!1),g(n.customInspect)&&(n.customInspect=!0),n.colors&&(n.stylize=f),u(n,e,n.depth)}function f(e,t){var r=s.styles[t];return r?"["+s.colors[r][0]+"m"+e+"["+s.colors[r][1]+"m":e}function c(e,t){return e}function u(e,r,n){if(e.customInspect&&r&&S(r.inspect)&&r.inspect!==t.inspect&&(!r.constructor||r.constructor.prototype!==r)){var i=r.inspect(n,e);return v(i)||(i=u(e,i,n)),i}var o=function(e,t){if(g(t))return e.stylize("undefined","undefined");if(v(t)){var r="\'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/\'/g,"\\\\\'").replace(/\\\\"/g,\'"\')+"\'";return e.stylize(r,"string")}return b(t)?e.stylize(""+t,"number"):p(t)?e.stylize(""+t,"boolean"):y(t)?e.stylize("null","null"):void 0}(e,r);if(o)return o;var a=Object.keys(r),s=function(e){var t={};return e.forEach((function(e,r){t[e]=!0})),t}(a);if(e.showHidden&&(a=Object.getOwnPropertyNames(r)),E(r)&&(a.indexOf("message")>=0||a.indexOf("description")>=0))return h(r);if(0===a.length){if(S(r)){var f=r.name?": "+r.name:"";return e.stylize("[Function"+f+"]","special")}if(m(r))return e.stylize(RegExp.prototype.toString.call(r),"regexp");if(_(r))return e.stylize(Date.prototype.toString.call(r),"date");if(E(r))return h(r)}var c,w="",A=!1,k=["{","}"];return d(r)&&(A=!0,k=["[","]"]),S(r)&&(w=" [Function"+(r.name?": "+r.name:"")+"]"),m(r)&&(w=" "+RegExp.prototype.toString.call(r)),_(r)&&(w=" "+Date.prototype.toUTCString.call(r)),E(r)&&(w=" "+h(r)),0!==a.length||A&&0!=r.length?n<0?m(r)?e.stylize(RegExp.prototype.toString.call(r),"regexp"):e.stylize("[Object]","special"):(e.seen.push(r),c=A?function(e,t,r,n,i){for(var o=[],a=0,s=t.length;a60?r[0]+(""===t?"":t+"\\n ")+" "+e.join(",\\n ")+" "+r[1]:r[0]+t+" "+e.join(", ")+" "+r[1]}(c,w,k)):k[0]+w+k[1]}function h(e){return"["+Error.prototype.toString.call(e)+"]"}function l(e,t,r,n,i,o){var a,s,f;if((f=Object.getOwnPropertyDescriptor(t,i)||{value:t[i]}).get?s=f.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):f.set&&(s=e.stylize("[Setter]","special")),T(n,i)||(a="["+i+"]"),s||(e.seen.indexOf(f.value)<0?(s=y(r)?u(e,f.value,null):u(e,f.value,r-1)).indexOf("\\n")>-1&&(s=o?s.split("\\n").map((function(e){return" "+e})).join("\\n").substr(2):"\\n"+s.split("\\n").map((function(e){return" "+e})).join("\\n")):s=e.stylize("[Circular]","special")),g(a)){if(o&&i.match(/^\\d+$/))return s;(a=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.substr(1,a.length-2),a=e.stylize(a,"name")):(a=a.replace(/\'/g,"\\\\\'").replace(/\\\\"/g,\'"\').replace(/(^"|"$)/g,"\'"),a=e.stylize(a,"string"))}return a+": "+s}function d(e){return Array.isArray(e)}function p(e){return"boolean"==typeof e}function y(e){return null===e}function b(e){return"number"==typeof e}function v(e){return"string"==typeof e}function g(e){return void 0===e}function m(e){return w(e)&&"[object RegExp]"===A(e)}function w(e){return"object"==typeof e&&null!==e}function _(e){return w(e)&&"[object Date]"===A(e)}function E(e){return w(e)&&("[object Error]"===A(e)||e instanceof Error)}function S(e){return"function"==typeof e}function A(e){return Object.prototype.toString.call(e)}function k(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(r){if(g(o)&&(o=e.env.NODE_DEBUG||""),r=r.toUpperCase(),!a[r])if(new RegExp("\\\\b"+r+"\\\\b","i").test(o)){var n=e.pid;a[r]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",r,n,e)}}else a[r]=function(){};return a[r]},t.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.isArray=d,t.isBoolean=p,t.isNull=y,t.isNullOrUndefined=function(e){return null==e},t.isNumber=b,t.isString=v,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=g,t.isRegExp=m,t.isObject=w,t.isDate=_,t.isError=E,t.isFunction=S,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=r(216);var O=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function x(){var e=new Date,t=[k(e.getHours()),k(e.getMinutes()),k(e.getSeconds())].join(":");return[e.getDate(),O[e.getMonth()],t].join(" ")}function T(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",x(),t.format.apply(t,arguments))},t.inherits=r(2),t._extend=function(e,t){if(!t||!w(t))return e;for(var r=Object.keys(t),n=r.length;n--;)e[r[n]]=t[r[n]];return e};var I="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function M(e,t){if(!e){var r=new Error("Promise was rejected with a falsy value");r.reason=e,e=r}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError(\'The "original" argument must be of type Function\');if(I&&e[I]){var t;if("function"!=typeof(t=e[I]))throw new TypeError(\'The "util.promisify.custom" argument must be of type Function\');return Object.defineProperty(t,I,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,r,n=new Promise((function(e,n){t=e,r=n})),i=[],o=0;o64?t=e(t):t.length<64&&(t=i.concat([t,a],64));for(var r=this._ipad=i.allocUnsafe(64),n=this._opad=i.allocUnsafe(64),s=0;s<64;s++)r[s]=54^t[s],n[s]=92^t[s];this._hash=[r]}n(s,o),s.prototype._update=function(e){this._hash.push(e)},s.prototype._final=function(){var e=this._alg(i.concat(this._hash));return this._alg(i.concat([this._opad,e]))},e.exports=s},function(e,t,r){e.exports=r(116)},function(e,t,r){var n=r(22),i=r(220),o=r(2),a=r(0).Buffer,s={"des-ede3-cbc":i.CBC.instantiate(i.EDE),"des-ede3":i.EDE,"des-ede-cbc":i.CBC.instantiate(i.EDE),"des-ede":i.EDE,"des-cbc":i.CBC.instantiate(i.DES),"des-ecb":i.DES};function f(e){n.call(this);var t,r=e.mode.toLowerCase(),i=s[r];t=e.decrypt?"decrypt":"encrypt";var o=e.key;a.isBuffer(o)||(o=a.from(o)),"des-ede"!==r&&"des-ede-cbc"!==r||(o=a.concat([o,o.slice(0,8)]));var f=e.iv;a.isBuffer(f)||(f=a.from(f)),this._des=i.create({key:o,iv:f,type:t})}s.des=s["des-cbc"],s.des3=s["des-ede3-cbc"],e.exports=f,o(f,n),f.prototype._update=function(e){return a.from(this._des.update(e))},f.prototype._final=function(){return a.from(this._des.final())}},function(e,t,r){"use strict";t.utils=r(117),t.Cipher=r(74),t.DES=r(118),t.CBC=r(221),t.EDE=r(222)},function(e,t,r){"use strict";var n=r(12),i=r(2),o={};function a(e){n.equal(e.length,8,"Invalid IV length"),this.iv=new Array(8);for(var t=0;t15){var e=this.cache.slice(0,16);return this.cache=this.cache.slice(16),e}return null},l.prototype.flush=function(){for(var e=16-this.cache.length,t=o.allocUnsafe(e),r=-1;++r>a%8,e._prev=o(e._prev,r?n:i);return s}function o(e,t){var r=e.length,i=-1,o=n.allocUnsafe(e.length);for(e=n.concat([e,n.from([t])]);++i>7;return o}t.encrypt=function(e,t,r){for(var o=t.length,a=n.allocUnsafe(o),s=-1;++s>>0,0),t.writeUInt32BE(e[1]>>>0,4),t.writeUInt32BE(e[2]>>>0,8),t.writeUInt32BE(e[3]>>>0,12),t}function a(e){this.h=e,this.state=n.alloc(16,0),this.cache=n.allocUnsafe(0)}a.prototype.ghash=function(e){for(var t=-1;++t0;t--)n[t]=n[t]>>>1|(1&n[t-1])<<31;n[0]=n[0]>>>1,r&&(n[0]=n[0]^225<<24)}this.state=o(i)},a.prototype.update=function(e){var t;for(this.cache=n.concat([this.cache,e]);this.cache.length>=16;)t=this.cache.slice(0,16),this.cache=this.cache.slice(16),this.ghash(t)},a.prototype.final=function(e,t){return this.cache.length&&this.ghash(n.concat([this.cache,i],16)),this.ghash(o([0,e,0,t])),this.state},e.exports=a},function(e,t,r){var n=r(122),i=r(0).Buffer,o=r(76),a=r(123),s=r(22),f=r(48),c=r(49);function u(e,t,r){s.call(this),this._cache=new h,this._last=void 0,this._cipher=new f.AES(t),this._prev=i.from(r),this._mode=e,this._autopadding=!0}function h(){this.cache=i.allocUnsafe(0)}function l(e,t,r){var s=o[e.toLowerCase()];if(!s)throw new TypeError("invalid suite type");if("string"==typeof r&&(r=i.from(r)),"GCM"!==s.mode&&r.length!==s.iv)throw new TypeError("invalid iv length "+r.length);if("string"==typeof t&&(t=i.from(t)),t.length!==s.key/8)throw new TypeError("invalid key length "+t.length);return"stream"===s.type?new a(s.module,t,r,!0):"auth"===s.type?new n(s.module,t,r,!0):new u(s.module,t,r)}r(2)(u,s),u.prototype._update=function(e){var t,r;this._cache.add(e);for(var n=[];t=this._cache.get(this._autopadding);)r=this._mode.decrypt(this,t),n.push(r);return i.concat(n)},u.prototype._final=function(){var e=this._cache.flush();if(this._autopadding)return function(e){var t=e[15];if(t<1||t>16)throw new Error("unable to decrypt data");for(var r=-1;++r16)return t=this.cache.slice(0,16),this.cache=this.cache.slice(16),t}else if(this.cache.length>=16)return t=this.cache.slice(0,16),this.cache=this.cache.slice(16),t;return null},h.prototype.flush=function(){if(this.cache.length)return this.cache},t.createDecipher=function(e,t){var r=o[e.toLowerCase()];if(!r)throw new TypeError("invalid suite type");var n=c(t,!1,r.key,r.iv);return l(e,n.key,n.iv)},t.createDecipheriv=l},function(e,t){t["des-ecb"]={key:8,iv:0},t["des-cbc"]=t.des={key:8,iv:8},t["des-ede3-cbc"]=t.des3={key:24,iv:8},t["des-ede3"]={key:24,iv:0},t["des-ede-cbc"]={key:16,iv:8},t["des-ede"]={key:16,iv:0}},function(e,t,r){(function(e){var n=r(124),i=r(234),o=r(235),a={binary:!0,hex:!0,base64:!0};t.DiffieHellmanGroup=t.createDiffieHellmanGroup=t.getDiffieHellman=function(t){var r=new e(i[t].prime,"hex"),n=new e(i[t].gen,"hex");return new o(r,n)},t.createDiffieHellman=t.DiffieHellman=function t(r,i,s,f){return e.isBuffer(i)||void 0===a[i]?t(r,"binary",i,s):(i=i||"binary",f=f||"binary",s=s||new e([2]),e.isBuffer(s)||(s=new e(s,f)),"number"==typeof r?new o(n(r,s),s,!0):(e.isBuffer(r)||(r=new e(r,i)),new o(r,s,!0)))}}).call(this,r(3).Buffer)},function(e){e.exports=JSON.parse(\'{"modp1":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"},"modp2":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff"},"modp5":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff"},"modp14":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff"},"modp15":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff"},"modp16":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff"},"modp17":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff"},"modp18":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff"}}\')},function(e,t,r){(function(t){var n=r(6),i=new(r(125)),o=new n(24),a=new n(11),s=new n(10),f=new n(3),c=new n(7),u=r(124),h=r(18);function l(e,r){return r=r||"utf8",t.isBuffer(e)||(e=new t(e,r)),this._pub=new n(e),this}function d(e,r){return r=r||"utf8",t.isBuffer(e)||(e=new t(e,r)),this._priv=new n(e),this}e.exports=y;var p={};function y(e,t,r){this.setGenerator(t),this.__prime=new n(e),this._prime=n.mont(this.__prime),this._primeLen=e.length,this._pub=void 0,this._priv=void 0,this._primeCode=void 0,r?(this.setPublicKey=l,this.setPrivateKey=d):this._primeCode=8}function b(e,r){var n=new t(e.toArray());return r?n.toString(r):n}Object.defineProperty(y.prototype,"verifyError",{enumerable:!0,get:function(){return"number"!=typeof this._primeCode&&(this._primeCode=function(e,t){var r=t.toString("hex"),n=[r,e.toString(16)].join("_");if(n in p)return p[n];var h,l=0;if(e.isEven()||!u.simpleSieve||!u.fermatTest(e)||!i.test(e))return l+=1,l+="02"===r||"05"===r?8:4,p[n]=l,l;switch(i.test(e.shrn(1))||(l+=2),r){case"02":e.mod(o).cmp(a)&&(l+=8);break;case"05":(h=e.mod(s)).cmp(f)&&h.cmp(c)&&(l+=8);break;default:l+=4}return p[n]=l,l}(this.__prime,this.__gen)),this._primeCode}}),y.prototype.generateKeys=function(){return this._priv||(this._priv=new n(h(this._primeLen))),this._pub=this._gen.toRed(this._prime).redPow(this._priv).fromRed(),this.getPublicKey()},y.prototype.computeSecret=function(e){var r=(e=(e=new n(e)).toRed(this._prime)).redPow(this._priv).fromRed(),i=new t(r.toArray()),o=this.getPrime();if(i.length0&&r.ishrn(n),r}function h(e,r,i){var o,a;do{for(o=new t(0);8*o.length=t)throw new Error("invalid sig")}e.exports=function(e,r,f,c,u){var h=o(f);if("ec"===h.type){if("ecdsa"!==c&&"ecdsa/rsa"!==c)throw new Error("wrong public key type");return function(e,t,r){var n=a[r.data.algorithm.curve.join(".")];if(!n)throw new Error("unknown curve "+r.data.algorithm.curve.join("."));var o=new i(n),s=r.data.subjectPrivateKey.data;return o.verify(t,e,s)}(e,r,h)}if("dsa"===h.type){if("dsa"!==c)throw new Error("wrong public key type");return function(e,t,r){var i=r.data.p,a=r.data.q,f=r.data.g,c=r.data.pub_key,u=o.signature.decode(e,"der"),h=u.s,l=u.r;s(h,a),s(l,a);var d=n.mont(i),p=h.invm(a);return 0===f.toRed(d).redPow(new n(t).mul(p).mod(a)).fromRed().mul(c.toRed(d).redPow(l.mul(p).mod(a)).fromRed()).mod(i).mod(a).cmp(l)}(e,r,h)}if("rsa"!==c&&"ecdsa/rsa"!==c)throw new Error("wrong public key type");r=t.concat([u,r]);for(var l=h.modulus.byteLength(),d=[1],p=0;r.length+d.length+2r-l-2)throw new Error("message too long");var d=h.alloc(r-n-l-2),p=r-u-1,y=i(u),b=s(h.concat([c,d,h.alloc(1,1),t],p),a(y,p)),v=s(y,a(b,u));return new f(h.concat([h.alloc(1),v,b],r))}(p,t);else if(1===l)d=function(e,t,r){var n,o=t.length,a=e.modulus.byteLength();if(o>a-11)throw new Error("message too long");return n=r?h.alloc(a-o-3,255):function(e){for(var t,r=h.allocUnsafe(e),n=0,o=i(2*e),a=0;n=0)throw new Error("data too long for modulus")}return r?u(d,p):c(d,p)}},function(e,t,r){var n=r(50),i=r(131),o=r(132),a=r(6),s=r(77),f=r(21),c=r(133),u=r(0).Buffer;e.exports=function(e,t,r){var h;h=e.padding?e.padding:r?1:4;var l,d=n(e),p=d.modulus.byteLength();if(t.length>p||new a(t).cmp(d.modulus)>=0)throw new Error("decryption error");l=r?c(new a(t),d):s(t,d);var y=u.alloc(p-l.length);if(l=u.concat([y,l],p),4===h)return function(e,t){var r=e.modulus.byteLength(),n=f("sha1").update(u.alloc(0)).digest(),a=n.length;if(0!==t[0])throw new Error("decryption error");var s=t.slice(1,a+1),c=t.slice(a+1),h=o(s,i(c,a)),l=o(c,i(h,r-a-1));if(function(e,t){e=u.from(e),t=u.from(t);var r=0,n=e.length;e.length!==t.length&&(r++,n=Math.min(e.length,t.length));for(var i=-1;++i=t.length){o++;break}var a=t.slice(2,i-1);if(("0002"!==n.toString("hex")&&!r||"0001"!==n.toString("hex")&&r)&&o++,a.length<8&&o++,o)throw new Error("decryption error");return t.slice(i)}(0,l,r);if(3===h)return l;throw new Error("unknown padding")}},function(e,t,r){"use strict";(function(e,n){function i(){throw new Error("secure random number generation not supported by this browser\\nuse chrome, FireFox or Internet Explorer 11")}var o=r(0),a=r(18),s=o.Buffer,f=o.kMaxLength,c=e.crypto||e.msCrypto,u=Math.pow(2,32)-1;function h(e,t){if("number"!=typeof e||e!=e)throw new TypeError("offset must be a number");if(e>u||e<0)throw new TypeError("offset must be a uint32");if(e>f||e>t)throw new RangeError("offset out of range")}function l(e,t,r){if("number"!=typeof e||e!=e)throw new TypeError("size must be a number");if(e>u||e<0)throw new TypeError("size must be a uint32");if(e+t>r||e>f)throw new RangeError("buffer too small")}function d(e,t,r,i){if(n.browser){var o=e.buffer,s=new Uint8Array(o,t,r);return c.getRandomValues(s),i?void n.nextTick((function(){i(null,e)})):e}if(!i)return a(r).copy(e,t),e;a(r,(function(r,n){if(r)return i(r);n.copy(e,t),i(null,e)}))}c&&c.getRandomValues||!n.browser?(t.randomFill=function(t,r,n,i){if(!(s.isBuffer(t)||t instanceof e.Uint8Array))throw new TypeError(\'"buf" argument must be a Buffer or Uint8Array\');if("function"==typeof r)i=r,r=0,n=t.length;else if("function"==typeof n)i=n,n=t.length-r;else if("function"!=typeof i)throw new TypeError(\'"cb" argument must be a function\');return h(r,t.length),l(n,r,t.length),d(t,r,n,i)},t.randomFillSync=function(t,r,n){if(void 0===r&&(r=0),!(s.isBuffer(t)||t instanceof e.Uint8Array))throw new TypeError(\'"buf" argument must be a Buffer or Uint8Array\');return h(r,t.length),void 0===n&&(n=t.length-r),l(n,r,t.length),d(t,r,n)}):(t.randomFill=i,t.randomFillSync=i)}).call(this,r(10),r(16))},function(e,t,r){(function(t){var n=r(31),i=r(21);function o(e,r){var i;(Array.isArray(e)||e instanceof Uint8Array)&&(e=new t(e)),null!=r?("number"==typeof r&&(r=new t([r])),i=t.concat([r,e])):i=e;var o=f(i).slice(0,4),a=t.concat([i,o]);return n.encode(a)}function a(e,r){var i,o=n.decode(e),a=new t(o);if(null==r)i=0;else if("number"==typeof r&&(r=new t([r])),i=r.length,a.slice(0,i).toString("hex")!==r.toString("hex"))throw new Error("Invalid version");var s=a.slice(-4),c=a.length-4,u=a.slice(0,c),h=f(u).slice(0,4);if(s.toString("hex")!==h.toString("hex"))throw new Error("Invalid checksum");return u.slice(i)}function s(e,t){try{a(e,t)}catch(e){return!1}return!0}function f(e){var t=i("sha256").update(e).digest();return i("sha256").update(t).digest()}e.exports={encode:o,decode:a,isValid:s,createEncoder:function(e){return function(t){return o(t,e)}},createDecoder:function(e){return function(t){return a(t,e)}},createValidator:function(e){return function(t){return s(t,e)}}}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";e.exports=r(259)(r(263))},function(e,t,r){"use strict";var n=r(260),i=r(261),o=r(134);function a(e,t){return void 0===e?t:(n.isBoolean(e,o.COMPRESSED_TYPE_INVALID),e)}e.exports=function(e){return{privateKeyVerify:function(t){return n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),32===t.length&&e.privateKeyVerify(t)},privateKeyExport:function(t,r){n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),n.isBufferLength(t,32,o.EC_PRIVATE_KEY_LENGTH_INVALID),r=a(r,!0);var s=e.privateKeyExport(t,r);return i.privateKeyExport(t,s,r)},privateKeyImport:function(t){if(n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),(t=i.privateKeyImport(t))&&32===t.length&&e.privateKeyVerify(t))return t;throw new Error(o.EC_PRIVATE_KEY_IMPORT_DER_FAIL)},privateKeyNegate:function(t){return n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),n.isBufferLength(t,32,o.EC_PRIVATE_KEY_LENGTH_INVALID),e.privateKeyNegate(t)},privateKeyModInverse:function(t){return n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),n.isBufferLength(t,32,o.EC_PRIVATE_KEY_LENGTH_INVALID),e.privateKeyModInverse(t)},privateKeyTweakAdd:function(t,r){return n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),n.isBufferLength(t,32,o.EC_PRIVATE_KEY_LENGTH_INVALID),n.isBuffer(r,o.TWEAK_TYPE_INVALID),n.isBufferLength(r,32,o.TWEAK_LENGTH_INVALID),e.privateKeyTweakAdd(t,r)},privateKeyTweakMul:function(t,r){return n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),n.isBufferLength(t,32,o.EC_PRIVATE_KEY_LENGTH_INVALID),n.isBuffer(r,o.TWEAK_TYPE_INVALID),n.isBufferLength(r,32,o.TWEAK_LENGTH_INVALID),e.privateKeyTweakMul(t,r)},publicKeyCreate:function(t,r){return n.isBuffer(t,o.EC_PRIVATE_KEY_TYPE_INVALID),n.isBufferLength(t,32,o.EC_PRIVATE_KEY_LENGTH_INVALID),r=a(r,!0),e.publicKeyCreate(t,r)},publicKeyConvert:function(t,r){return n.isBuffer(t,o.EC_PUBLIC_KEY_TYPE_INVALID),n.isBufferLength2(t,33,65,o.EC_PUBLIC_KEY_LENGTH_INVALID),r=a(r,!0),e.publicKeyConvert(t,r)},publicKeyVerify:function(t){return n.isBuffer(t,o.EC_PUBLIC_KEY_TYPE_INVALID),e.publicKeyVerify(t)},publicKeyTweakAdd:function(t,r,i){return n.isBuffer(t,o.EC_PUBLIC_KEY_TYPE_INVALID),n.isBufferLength2(t,33,65,o.EC_PUBLIC_KEY_LENGTH_INVALID),n.isBuffer(r,o.TWEAK_TYPE_INVALID),n.isBufferLength(r,32,o.TWEAK_LENGTH_INVALID),i=a(i,!0),e.publicKeyTweakAdd(t,r,i)},publicKeyTweakMul:function(t,r,i){return n.isBuffer(t,o.EC_PUBLIC_KEY_TYPE_INVALID),n.isBufferLength2(t,33,65,o.EC_PUBLIC_KEY_LENGTH_INVALID),n.isBuffer(r,o.TWEAK_TYPE_INVALID),n.isBufferLength(r,32,o.TWEAK_LENGTH_INVALID),i=a(i,!0),e.publicKeyTweakMul(t,r,i)},publicKeyCombine:function(t,r){n.isArray(t,o.EC_PUBLIC_KEYS_TYPE_INVALID),n.isLengthGTZero(t,o.EC_PUBLIC_KEYS_LENGTH_INVALID);for(var i=0;i=r)throw RangeError(n)}}).call(this,r(3).Buffer)},function(e,t,r){"use strict";var n=r(0).Buffer,i=r(262),o=n.from([48,129,211,2,1,1,4,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,129,133,48,129,130,2,1,1,48,44,6,7,42,134,72,206,61,1,1,2,33,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,255,255,252,47,48,6,4,1,0,4,1,7,4,33,2,121,190,102,126,249,220,187,172,85,160,98,149,206,135,11,7,2,155,252,219,45,206,40,217,89,242,129,91,22,248,23,152,2,33,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,186,174,220,230,175,72,160,59,191,210,94,140,208,54,65,65,2,1,1,161,36,3,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]),a=n.from([48,130,1,19,2,1,1,4,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,129,165,48,129,162,2,1,1,48,44,6,7,42,134,72,206,61,1,1,2,33,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,255,255,252,47,48,6,4,1,0,4,1,7,4,65,4,121,190,102,126,249,220,187,172,85,160,98,149,206,135,11,7,2,155,252,219,45,206,40,217,89,242,129,91,22,248,23,152,72,58,218,119,38,163,196,101,93,164,251,252,14,17,8,168,253,23,180,72,166,133,84,25,156,71,208,143,251,16,212,184,2,33,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,186,174,220,230,175,72,160,59,191,210,94,140,208,54,65,65,2,1,1,161,68,3,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);t.privateKeyExport=function(e,t,r){var i=n.from(r?o:a);return e.copy(i,r?8:9),t.copy(i,r?181:214),i},t.privateKeyImport=function(e){var t=e.length,r=0;if(!(t2||t1?e[r+n-2]<<8:0);if(!(t<(r+=n)+i||t32||t1&&0===t[o]&&!(128&t[o+1]);--r,++o);for(var a=n.concat([n.from([0]),e.s]),s=33,f=0;s>1&&0===a[f]&&!(128&a[f+1]);--s,++f);return i.encode(t.slice(o),a.slice(f))},t.signatureImport=function(e){var t=n.alloc(32,0),r=n.alloc(32,0);try{var o=i.decode(e);if(33===o.r.length&&0===o.r[0]&&(o.r=o.r.slice(1)),o.r.length>32)throw new Error("R length is too long");if(33===o.s.length&&0===o.s[0]&&(o.s=o.s.slice(1)),o.s.length>32)throw new Error("S length is too long")}catch(e){return}return o.r.copy(t,32-o.r.length),o.s.copy(r,32-o.s.length),{r:t,s:r}},t.signatureImportLax=function(e){var t=n.alloc(32,0),r=n.alloc(32,0),i=e.length,o=0;if(48===e[o++]){var a=e[o++];if(!(128&a&&(o+=a-128)>i)&&2===e[o++]){var s=e[o++];if(128&s){if(o+(a=s-128)>i)return;for(;a>0&&0===e[o];o+=1,a-=1);for(s=0;a>0;o+=1,a-=1)s=(s<<8)+e[o]}if(!(s>i-o)){var f=o;if(o+=s,2===e[o++]){var c=e[o++];if(128&c){if(o+(a=c-128)>i)return;for(;a>0&&0===e[o];o+=1,a-=1);for(c=0;a>0;o+=1,a-=1)c=(c<<8)+e[o]}if(!(c>i-o)){var u=o;for(o+=c;s>0&&0===e[f];s-=1,f+=1);if(!(s>32)){var h=e.slice(f,f+s);for(h.copy(t,32-h.length);c>0&&0===e[u];c-=1,u+=1);if(!(c>32)){var l=e.slice(u,u+c);return l.copy(r,32-l.length),{r:t,s:r}}}}}}}}}},function(e,t,r){var n=r(0).Buffer;e.exports={check:function(e){if(e.length<8)return!1;if(e.length>72)return!1;if(48!==e[0])return!1;if(e[1]!==e.length-2)return!1;if(2!==e[2])return!1;var t=e[3];if(0===t)return!1;if(5+t>=e.length)return!1;if(2!==e[4+t])return!1;var r=e[5+t];return!(0===r||6+t+r!==e.length||128&e[4]||t>1&&0===e[4]&&!(128&e[5])||128&e[t+6]||r>1&&0===e[t+6]&&!(128&e[t+7]))},decode:function(e){if(e.length<8)throw new Error("DER sequence length is too short");if(e.length>72)throw new Error("DER sequence length is too long");if(48!==e[0])throw new Error("Expected DER sequence");if(e[1]!==e.length-2)throw new Error("DER sequence length is invalid");if(2!==e[2])throw new Error("Expected DER integer");var t=e[3];if(0===t)throw new Error("R length is zero");if(5+t>=e.length)throw new Error("R length is too long");if(2!==e[4+t])throw new Error("Expected DER integer (2)");var r=e[5+t];if(0===r)throw new Error("S length is zero");if(6+t+r!==e.length)throw new Error("S length is invalid");if(128&e[4])throw new Error("R value is negative");if(t>1&&0===e[4]&&!(128&e[5]))throw new Error("R value excessively padded");if(128&e[t+6])throw new Error("S value is negative");if(r>1&&0===e[t+6]&&!(128&e[t+7]))throw new Error("S value excessively padded");return{r:e.slice(4,4+t),s:e.slice(6+t)}},encode:function(e,t){var r=e.length,i=t.length;if(0===r)throw new Error("R length is zero");if(0===i)throw new Error("S length is zero");if(r>33)throw new Error("R length is too long");if(i>33)throw new Error("S length is too long");if(128&e[0])throw new Error("R value is negative");if(128&t[0])throw new Error("S value is negative");if(r>1&&0===e[0]&&!(128&e[1]))throw new Error("R value excessively padded");if(i>1&&0===t[0]&&!(128&t[1]))throw new Error("S value excessively padded");var o=n.allocUnsafe(6+r+i);return o[0]=48,o[1]=o.length-2,o[2]=2,o[3]=e.length,e.copy(o,4),o[4+r]=2,o[5+r]=t.length,t.copy(o,6+r),o}}},function(e,t,r){"use strict";var n=r(0).Buffer,i=r(21),o=r(6),a=r(29).ec,s=r(134),f=new a("secp256k1"),c=f.curve;function u(e){var t=e[0];switch(t){case 2:case 3:return 33!==e.length?null:function(e,t){var r=new o(t);if(r.cmp(c.p)>=0)return null;var n=(r=r.toRed(c.red)).redSqr().redIMul(r).redIAdd(c.b).redSqrt();return 3===e!==n.isOdd()&&(n=n.redNeg()),f.keyPair({pub:{x:r,y:n}})}(t,e.slice(1,33));case 4:case 6:case 7:return 65!==e.length?null:function(e,t,r){var n=new o(t),i=new o(r);if(n.cmp(c.p)>=0||i.cmp(c.p)>=0)return null;if(n=n.toRed(c.red),i=i.toRed(c.red),(6===e||7===e)&&i.isOdd()!==(7===e))return null;var a=n.redSqr().redIMul(n);return i.redSqr().redISub(a.redIAdd(c.b)).isZero()?f.keyPair({pub:{x:n,y:i}}):null}(t,e.slice(1,33),e.slice(33,65));default:return null}}t.privateKeyVerify=function(e){var t=new o(e);return t.cmp(c.n)<0&&!t.isZero()},t.privateKeyExport=function(e,t){var r=new o(e);if(r.cmp(c.n)>=0||r.isZero())throw new Error(s.EC_PRIVATE_KEY_EXPORT_DER_FAIL);return n.from(f.keyFromPrivate(e).getPublic(t,!0))},t.privateKeyNegate=function(e){var t=new o(e);return t.isZero()?n.alloc(32):c.n.sub(t).umod(c.n).toArrayLike(n,"be",32)},t.privateKeyModInverse=function(e){var t=new o(e);if(t.cmp(c.n)>=0||t.isZero())throw new Error(s.EC_PRIVATE_KEY_RANGE_INVALID);return t.invm(c.n).toArrayLike(n,"be",32)},t.privateKeyTweakAdd=function(e,t){var r=new o(t);if(r.cmp(c.n)>=0)throw new Error(s.EC_PRIVATE_KEY_TWEAK_ADD_FAIL);if(r.iadd(new o(e)),r.cmp(c.n)>=0&&r.isub(c.n),r.isZero())throw new Error(s.EC_PRIVATE_KEY_TWEAK_ADD_FAIL);return r.toArrayLike(n,"be",32)},t.privateKeyTweakMul=function(e,t){var r=new o(t);if(r.cmp(c.n)>=0||r.isZero())throw new Error(s.EC_PRIVATE_KEY_TWEAK_MUL_FAIL);return r.imul(new o(e)),r.cmp(c.n)&&(r=r.umod(c.n)),r.toArrayLike(n,"be",32)},t.publicKeyCreate=function(e,t){var r=new o(e);if(r.cmp(c.n)>=0||r.isZero())throw new Error(s.EC_PUBLIC_KEY_CREATE_FAIL);return n.from(f.keyFromPrivate(e).getPublic(t,!0))},t.publicKeyConvert=function(e,t){var r=u(e);if(null===r)throw new Error(s.EC_PUBLIC_KEY_PARSE_FAIL);return n.from(r.getPublic(t,!0))},t.publicKeyVerify=function(e){return null!==u(e)},t.publicKeyTweakAdd=function(e,t,r){var i=u(e);if(null===i)throw new Error(s.EC_PUBLIC_KEY_PARSE_FAIL);if((t=new o(t)).cmp(c.n)>=0)throw new Error(s.EC_PUBLIC_KEY_TWEAK_ADD_FAIL);var a=c.g.mul(t).add(i.pub);if(a.isInfinity())throw new Error(s.EC_PUBLIC_KEY_TWEAK_ADD_FAIL);return n.from(a.encode(!0,r))},t.publicKeyTweakMul=function(e,t,r){var i=u(e);if(null===i)throw new Error(s.EC_PUBLIC_KEY_PARSE_FAIL);if((t=new o(t)).cmp(c.n)>=0||t.isZero())throw new Error(s.EC_PUBLIC_KEY_TWEAK_MUL_FAIL);return n.from(i.pub.mul(t).encode(!0,r))},t.publicKeyCombine=function(e,t){for(var r=new Array(e.length),i=0;i=0||r.cmp(c.n)>=0)throw new Error(s.ECDSA_SIGNATURE_PARSE_FAIL);var i=n.from(e);return 1===r.cmp(f.nh)&&c.n.sub(r).toArrayLike(n,"be",32).copy(i,32),i},t.signatureExport=function(e){var t=e.slice(0,32),r=e.slice(32,64);if(new o(t).cmp(c.n)>=0||new o(r).cmp(c.n)>=0)throw new Error(s.ECDSA_SIGNATURE_PARSE_FAIL);return{r:t,s:r}},t.signatureImport=function(e){var t=new o(e.r);t.cmp(c.n)>=0&&(t=new o(0));var r=new o(e.s);return r.cmp(c.n)>=0&&(r=new o(0)),n.concat([t.toArrayLike(n,"be",32),r.toArrayLike(n,"be",32)])},t.sign=function(e,t,r,i){if("function"==typeof r){var a=r;r=function(r){var f=a(e,t,null,i,r);if(!n.isBuffer(f)||32!==f.length)throw new Error(s.ECDSA_SIGN_FAIL);return new o(f)}}var u=new o(t);if(u.cmp(c.n)>=0||u.isZero())throw new Error(s.ECDSA_SIGN_FAIL);var h=f.sign(e,t,{canonical:!0,k:r,pers:i});return{signature:n.concat([h.r.toArrayLike(n,"be",32),h.s.toArrayLike(n,"be",32)]),recovery:h.recoveryParam}},t.verify=function(e,t,r){var n={r:t.slice(0,32),s:t.slice(32,64)},i=new o(n.r),a=new o(n.s);if(i.cmp(c.n)>=0||a.cmp(c.n)>=0)throw new Error(s.ECDSA_SIGNATURE_PARSE_FAIL);if(1===a.cmp(f.nh)||i.isZero()||a.isZero())return!1;var h=u(r);if(null===h)throw new Error(s.EC_PUBLIC_KEY_PARSE_FAIL);return f.verify(e,n,{x:h.pub.x,y:h.pub.y})},t.recover=function(e,t,r,i){var a={r:t.slice(0,32),s:t.slice(32,64)},u=new o(a.r),h=new o(a.s);if(u.cmp(c.n)>=0||h.cmp(c.n)>=0)throw new Error(s.ECDSA_SIGNATURE_PARSE_FAIL);try{if(u.isZero()||h.isZero())throw new Error;var l=f.recoverPubKey(e,a,r);return n.from(l.encode(!0,i))}catch(e){throw new Error(s.ECDSA_RECOVER_FAIL)}},t.ecdh=function(e,r){var n=t.ecdhUnsafe(e,r,!0);return i("sha256").update(n).digest()},t.ecdhUnsafe=function(e,t,r){var i=u(e);if(null===i)throw new Error(s.EC_PUBLIC_KEY_PARSE_FAIL);var a=new o(t);if(a.cmp(c.n)>=0||a.isZero())throw new Error(s.ECDH_FAIL);return n.from(i.pub.mul(a).encode(!0,r))}},function(e,t,r){var n,i,o;e.exports=(o=r(23),i=(n=o).lib.WordArray,n.enc.Base64={stringify:function(e){var t=e.words,r=e.sigBytes,n=this._map;e.clamp();for(var i=[],o=0;o>>2]>>>24-o%4*8&255)<<16|(t[o+1>>>2]>>>24-(o+1)%4*8&255)<<8|t[o+2>>>2]>>>24-(o+2)%4*8&255,s=0;s<4&&o+.75*s>>6*(3-s)&63));var f=n.charAt(64);if(f)for(;i.length%4;)i.push(f);return i.join("")},parse:function(e){var t=e.length,r=this._map,n=this._reverseMap;if(!n){n=this._reverseMap=[];for(var o=0;o>>6-a%4*2;n[o>>>2]|=(s|f)<<24-o%4*8,o++}return i.create(n,o)}(e,t,n)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},o.enc.Base64)},function(e,t,r){var n;e.exports=(n=r(23),function(e){var t=n,r=t.lib,i=r.WordArray,o=r.Hasher,a=t.algo,s=[];!function(){for(var t=0;t<64;t++)s[t]=4294967296*e.abs(e.sin(t+1))|0}();var f=a.MD5=o.extend({_doReset:function(){this._hash=new i.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(e,t){for(var r=0;r<16;r++){var n=t+r,i=e[n];e[n]=16711935&(i<<8|i>>>24)|4278255360&(i<<24|i>>>8)}var o=this._hash.words,a=e[t+0],f=e[t+1],d=e[t+2],p=e[t+3],y=e[t+4],b=e[t+5],v=e[t+6],g=e[t+7],m=e[t+8],w=e[t+9],_=e[t+10],E=e[t+11],S=e[t+12],A=e[t+13],k=e[t+14],O=e[t+15],x=o[0],T=o[1],I=o[2],M=o[3];x=c(x,T,I,M,a,7,s[0]),M=c(M,x,T,I,f,12,s[1]),I=c(I,M,x,T,d,17,s[2]),T=c(T,I,M,x,p,22,s[3]),x=c(x,T,I,M,y,7,s[4]),M=c(M,x,T,I,b,12,s[5]),I=c(I,M,x,T,v,17,s[6]),T=c(T,I,M,x,g,22,s[7]),x=c(x,T,I,M,m,7,s[8]),M=c(M,x,T,I,w,12,s[9]),I=c(I,M,x,T,_,17,s[10]),T=c(T,I,M,x,E,22,s[11]),x=c(x,T,I,M,S,7,s[12]),M=c(M,x,T,I,A,12,s[13]),I=c(I,M,x,T,k,17,s[14]),x=u(x,T=c(T,I,M,x,O,22,s[15]),I,M,f,5,s[16]),M=u(M,x,T,I,v,9,s[17]),I=u(I,M,x,T,E,14,s[18]),T=u(T,I,M,x,a,20,s[19]),x=u(x,T,I,M,b,5,s[20]),M=u(M,x,T,I,_,9,s[21]),I=u(I,M,x,T,O,14,s[22]),T=u(T,I,M,x,y,20,s[23]),x=u(x,T,I,M,w,5,s[24]),M=u(M,x,T,I,k,9,s[25]),I=u(I,M,x,T,p,14,s[26]),T=u(T,I,M,x,m,20,s[27]),x=u(x,T,I,M,A,5,s[28]),M=u(M,x,T,I,d,9,s[29]),I=u(I,M,x,T,g,14,s[30]),x=h(x,T=u(T,I,M,x,S,20,s[31]),I,M,b,4,s[32]),M=h(M,x,T,I,m,11,s[33]),I=h(I,M,x,T,E,16,s[34]),T=h(T,I,M,x,k,23,s[35]),x=h(x,T,I,M,f,4,s[36]),M=h(M,x,T,I,y,11,s[37]),I=h(I,M,x,T,g,16,s[38]),T=h(T,I,M,x,_,23,s[39]),x=h(x,T,I,M,A,4,s[40]),M=h(M,x,T,I,a,11,s[41]),I=h(I,M,x,T,p,16,s[42]),T=h(T,I,M,x,v,23,s[43]),x=h(x,T,I,M,w,4,s[44]),M=h(M,x,T,I,S,11,s[45]),I=h(I,M,x,T,O,16,s[46]),x=l(x,T=h(T,I,M,x,d,23,s[47]),I,M,a,6,s[48]),M=l(M,x,T,I,g,10,s[49]),I=l(I,M,x,T,k,15,s[50]),T=l(T,I,M,x,b,21,s[51]),x=l(x,T,I,M,S,6,s[52]),M=l(M,x,T,I,p,10,s[53]),I=l(I,M,x,T,_,15,s[54]),T=l(T,I,M,x,f,21,s[55]),x=l(x,T,I,M,m,6,s[56]),M=l(M,x,T,I,O,10,s[57]),I=l(I,M,x,T,v,15,s[58]),T=l(T,I,M,x,A,21,s[59]),x=l(x,T,I,M,y,6,s[60]),M=l(M,x,T,I,E,10,s[61]),I=l(I,M,x,T,d,15,s[62]),T=l(T,I,M,x,w,21,s[63]),o[0]=o[0]+x|0,o[1]=o[1]+T|0,o[2]=o[2]+I|0,o[3]=o[3]+M|0},_doFinalize:function(){var t=this._data,r=t.words,n=8*this._nDataBytes,i=8*t.sigBytes;r[i>>>5]|=128<<24-i%32;var o=e.floor(n/4294967296),a=n;r[15+(i+64>>>9<<4)]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),r[14+(i+64>>>9<<4)]=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8),t.sigBytes=4*(r.length+1),this._process();for(var s=this._hash,f=s.words,c=0;c<4;c++){var u=f[c];f[c]=16711935&(u<<8|u>>>24)|4278255360&(u<<24|u>>>8)}return s},clone:function(){var e=o.clone.call(this);return e._hash=this._hash.clone(),e}});function c(e,t,r,n,i,o,a){var s=e+(t&r|~t&n)+i+a;return(s<>>32-o)+t}function u(e,t,r,n,i,o,a){var s=e+(t&n|r&~n)+i+a;return(s<>>32-o)+t}function h(e,t,r,n,i,o,a){var s=e+(t^r^n)+i+a;return(s<>>32-o)+t}function l(e,t,r,n,i,o,a){var s=e+(r^(t|~n))+i+a;return(s<>>32-o)+t}t.MD5=o._createHelper(f),t.HmacMD5=o._createHmacHelper(f)}(Math),n.MD5)},function(e,t,r){var n,i,o,a,s,f,c,u;e.exports=(i=(n=u=r(23)).lib,o=i.WordArray,a=i.Hasher,s=n.algo,f=[],c=s.SHA1=a.extend({_doReset:function(){this._hash=new o.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(e,t){for(var r=this._hash.words,n=r[0],i=r[1],o=r[2],a=r[3],s=r[4],c=0;c<80;c++){if(c<16)f[c]=0|e[t+c];else{var u=f[c-3]^f[c-8]^f[c-14]^f[c-16];f[c]=u<<1|u>>>31}var h=(n<<5|n>>>27)+s+f[c];h+=c<20?1518500249+(i&o|~i&a):c<40?1859775393+(i^o^a):c<60?(i&o|i&a|o&a)-1894007588:(i^o^a)-899497514,s=a,a=o,o=i<<30|i>>>2,i=n,n=h}r[0]=r[0]+n|0,r[1]=r[1]+i|0,r[2]=r[2]+o|0,r[3]=r[3]+a|0,r[4]=r[4]+s|0},_doFinalize:function(){var e=this._data,t=e.words,r=8*this._nDataBytes,n=8*e.sigBytes;return t[n>>>5]|=128<<24-n%32,t[14+(n+64>>>9<<4)]=Math.floor(r/4294967296),t[15+(n+64>>>9<<4)]=r,e.sigBytes=4*t.length,this._process(),this._hash},clone:function(){var e=a.clone.call(this);return e._hash=this._hash.clone(),e}}),n.SHA1=a._createHelper(c),n.HmacSHA1=a._createHmacHelper(c),u.SHA1)},function(e,t,r){var n,i,o;e.exports=(i=(n=r(23)).lib.Base,o=n.enc.Utf8,void(n.algo.HMAC=i.extend({init:function(e,t){e=this._hasher=new e.init,"string"==typeof t&&(t=o.parse(t));var r=e.blockSize,n=4*r;t.sigBytes>n&&(t=e.finalize(t)),t.clamp();for(var i=this._oKey=t.clone(),a=this._iKey=t.clone(),s=i.words,f=a.words,c=0;c>>2];e.sigBytes-=t}},o.BlockCipher=l.extend({cfg:l.cfg.extend({mode:y,padding:b}),reset:function(){l.reset.call(this);var e=this.cfg,t=e.iv,r=e.mode;if(this._xformMode==this._ENC_XFORM_MODE)var n=r.createEncryptor;else n=r.createDecryptor,this._minBufferSize=1;this._mode&&this._mode.__creator==n?this._mode.init(this,t&&t.words):(this._mode=n.call(r,this,t&&t.words),this._mode.__creator=n)},_doProcessBlock:function(e,t){this._mode.processBlock(e,t)},_doFinalize:function(){var e=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){e.pad(this._data,this.blockSize);var t=this._process(!0)}else t=this._process(!0),e.unpad(t);return t},blockSize:4}),v=o.CipherParams=a.extend({init:function(e){this.mixIn(e)},toString:function(e){return(e||this.formatter).stringify(this)}}),g=(i.format={}).OpenSSL={stringify:function(e){var t=e.ciphertext,r=e.salt;if(r)var n=s.create([1398893684,1701076831]).concat(r).concat(t);else n=t;return n.toString(u)},parse:function(e){var t=u.parse(e),r=t.words;if(1398893684==r[0]&&1701076831==r[1]){var n=s.create(r.slice(2,4));r.splice(0,4),t.sigBytes-=16}return v.create({ciphertext:t,salt:n})}},m=o.SerializableCipher=a.extend({cfg:a.extend({format:g}),encrypt:function(e,t,r,n){n=this.cfg.extend(n);var i=e.createEncryptor(r,n),o=i.finalize(t),a=i.cfg;return v.create({ciphertext:o,key:r,iv:a.iv,algorithm:e,mode:a.mode,padding:a.padding,blockSize:e.blockSize,formatter:n.format})},decrypt:function(e,t,r,n){return n=this.cfg.extend(n),t=this._parse(t,n.format),e.createDecryptor(r,n).finalize(t.ciphertext)},_parse:function(e,t){return"string"==typeof e?t.parse(e,this):e}}),w=(i.kdf={}).OpenSSL={execute:function(e,t,r,n){n||(n=s.random(8));var i=h.create({keySize:t+r}).compute(e,n),o=s.create(i.words.slice(t),4*r);return i.sigBytes=4*t,v.create({key:i,iv:o,salt:n})}},_=o.PasswordBasedCipher=m.extend({cfg:m.cfg.extend({kdf:w}),encrypt:function(e,t,r,n){var i=(n=this.cfg.extend(n)).kdf.execute(r,e.keySize,e.ivSize);n.iv=i.iv;var o=m.encrypt.call(this,e,t,i.key,n);return o.mixIn(i),o},decrypt:function(e,t,r,n){n=this.cfg.extend(n),t=this._parse(t,n.format);var i=n.kdf.execute(r,e.keySize,e.ivSize,t.salt);return n.iv=i.iv,m.decrypt.call(this,e,t,i.key,n)}}))))},function(e,t,r){const n=r(47),{checkAndInit:i,smixSync:o}=r(136);e.exports=function(e,t,r,a,s,f,c){const{XY:u,V:h,B32:l,x:d,_X:p,B:y,tickCallback:b}=i(e,t,r,a,s,f,c);for(var v=0;v=0,o=i&&n.regeneratorRuntime;if(n.regeneratorRuntime=void 0,e.exports=r(272),i)n.regeneratorRuntime=o;else try{delete n.regeneratorRuntime}catch(e){n.regeneratorRuntime=void 0}},function(e,t){!function(t){"use strict";var r=Object.prototype,n=r.hasOwnProperty,i="function"==typeof Symbol?Symbol:{},o=i.iterator||"@@iterator",a=i.asyncIterator||"@@asyncIterator",s=i.toStringTag||"@@toStringTag",f="object"==typeof e,c=t.regeneratorRuntime;if(c)f&&(e.exports=c);else{(c=t.regeneratorRuntime=f?e.exports:{}).wrap=y;var u={},h={};h[o]=function(){return this};var l=Object.getPrototypeOf,d=l&&l(l(O([])));d&&d!==r&&n.call(d,o)&&(h=d);var p=m.prototype=v.prototype=Object.create(h);g.prototype=p.constructor=m,m.constructor=g,m[s]=g.displayName="GeneratorFunction",c.isGeneratorFunction=function(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===g||"GeneratorFunction"===(t.displayName||t.name))},c.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,m):(e.__proto__=m,s in e||(e[s]="GeneratorFunction")),e.prototype=Object.create(p),e},c.awrap=function(e){return{__await:e}},w(_.prototype),_.prototype[a]=function(){return this},c.AsyncIterator=_,c.async=function(e,t,r,n){var i=new _(y(e,t,r,n));return c.isGeneratorFunction(t)?i:i.next().then((function(e){return e.done?e.value:i.next()}))},w(p),p[s]="Generator",p[o]=function(){return this},p.toString=function(){return"[object Generator]"},c.keys=function(e){var t=[];for(var r in e)t.push(r);return t.reverse(),function r(){for(;t.length;){var n=t.pop();if(n in e)return r.value=n,r.done=!1,r}return r.done=!0,r}},c.values=O,k.prototype={constructor:k,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method="next",this.arg=void 0,this.tryEntries.forEach(A),!e)for(var t in this)"t"===t.charAt(0)&&n.call(this,t)&&!isNaN(+t.slice(1))&&(this[t]=void 0)},stop:function(){this.done=!0;var e=this.tryEntries[0].completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var t=this;function r(r,n){return a.type="throw",a.arg=e,t.next=r,n&&(t.method="next",t.arg=void 0),!!n}for(var i=this.tryEntries.length-1;i>=0;--i){var o=this.tryEntries[i],a=o.completion;if("root"===o.tryLoc)return r("end");if(o.tryLoc<=this.prev){var s=n.call(o,"catchLoc"),f=n.call(o,"finallyLoc");if(s&&f){if(this.prev=0;--r){var i=this.tryEntries[r];if(i.tryLoc<=this.prev&&n.call(i,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),A(r),u}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var i=n.arg;A(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:O(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),u}}}function y(e,t,r,n){var i=t&&t.prototype instanceof v?t:v,o=Object.create(i.prototype),a=new k(n||[]);return o._invoke=function(e,t,r){var n="suspendedStart";return function(i,o){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===i)throw o;return{value:void 0,done:!0}}for(r.method=i,r.arg=o;;){var a=r.delegate;if(a){var s=E(a,r);if(s){if(s===u)continue;return s}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var f=b(e,t,r);if("normal"===f.type){if(n=r.done?"completed":"suspendedYield",f.arg===u)continue;return{value:f.arg,done:r.done}}"throw"===f.type&&(n="completed",r.method="throw",r.arg=f.arg)}}}(e,r,a),o}function b(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}function v(){}function g(){}function m(){}function w(e){["next","throw","return"].forEach((function(t){e[t]=function(e){return this._invoke(t,e)}}))}function _(e){var t;this._invoke=function(r,i){function o(){return new Promise((function(t,o){!function t(r,i,o,a){var s=b(e[r],e,i);if("throw"!==s.type){var f=s.arg,c=f.value;return c&&"object"==typeof c&&n.call(c,"__await")?Promise.resolve(c.__await).then((function(e){t("next",e,o,a)}),(function(e){t("throw",e,o,a)})):Promise.resolve(c).then((function(e){f.value=e,o(f)}),a)}a(s.arg)}(r,i,t,o)}))}return t=t?t.then(o,o):o()}}function E(e,t){var r=e.iterator[t.method];if(void 0===r){if(t.delegate=null,"throw"===t.method){if(e.iterator.return&&(t.method="return",t.arg=void 0,E(e,t),"throw"===t.method))return u;t.method="throw",t.arg=new TypeError("The iterator does not provide a \'throw\' method")}return u}var n=b(r,e.iterator,t.arg);if("throw"===n.type)return t.method="throw",t.arg=n.arg,t.delegate=null,u;var i=n.arg;return i?i.done?(t[e.resultName]=i.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,u):i:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,u)}function S(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function A(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function k(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(S,this),this.reset(!0)}function O(e){if(e){var t=e[o];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var r=-1,i=function t(){for(;++r0&&void 0!==arguments[0]?arguments[0]:"http://localhost:8545",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:8e3,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:A;i()(this,e),this.host=r.replace(/\\/$/,""),this.timeout=n,this.headers={},Array.isArray(o)?(o.forEach((function(e){var r=e.name,n=e.value;t.headers[r]=n})),this.headers=S({},A,{},this.headers)):this.headers=S({},A,{},o)}var t;return a()(e,[{key:"requestSendByFetch",value:function(e,t){var r=this,n=e.url,i=e.method,o=void 0===i?"POST":i,a=e.params,s=void 0===a?{}:a,f=e.signal,c="/api/".concat(n).replace(/\\/\\//g,"/"),u="".concat(this.host).concat(c).replace(),h=new Headers,l=JSON.stringify(s);return"GET"!==o.toUpperCase()&&"DELETE"!==o.toUpperCase()||(u=Object.keys(s).length>0?"".concat(u,"?").concat(Object(_.stringify)(s)):u,l=void 0),Object.keys(this.headers).forEach((function(e){h.append(e,r.headers[e])})),t(u,{method:o.toUpperCase(),headers:h,body:l,signal:f})}},{key:"sendAsyncByFetch",value:function(t){var r=k,n=this.timeout,i="function"==typeof AbortController?new AbortController:{},o=S({},t,{signal:i.signal,credentials:"omit"});return Promise.race([this.requestSendByFetch(o,r),e.timeoutPromise(n)]).then((function(t){return new Promise((function(r,o){if(1!==n)try{"timeout"===t.type?(i.abort&&i.abort(),o(t)):t.text().then((function(n){var i=e.formatResponse(n);200===t.status&&t.ok?r(i):o(i)})).catch((function(e){return o(e)}))}catch(e){o(e)}}))}))}},{key:"requestSend",value:function(e,t){var r=this,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=e.url,o=e.method,a=void 0===o?"POST":o,s=e.params,f=void 0===s?{}:s,c="/api/".concat(i).replace(/\\/\\//g,"/"),u="".concat(this.host).concat(c).replace();"GET"!==a.toUpperCase()&&"DELETE"!==a.toUpperCase()||(u=Object.keys(f).length>0?"".concat(u,"?").concat(Object(_.stringify)(f)):u),t.open(a.toUpperCase(),u,n),Object.keys(this.headers).forEach((function(e){t.setRequestHeader(e,r.headers[e])})),"GET"===a.toUpperCase()||"DELETE"===a.toUpperCase()?t.send():t.send(JSON.stringify(f))}},{key:"send",value:function(t){if(O)throw new Error("Can not get XMLHttpRequest, invalid parameter: \'sync\'");var r=new k;r.withCredentials=!1,this.requestSend(t,r);var n=r.responseText;if((n=e.formatResponse(n)).Error)throw n;return n}},{key:"sendAsync",value:function(e){return O?this.sendAsyncByFetch(e):this.sendAsyncByXMLHttp(e)}},{key:"sendAsyncByXMLHttp",value:function(t){var r=new k;return r.withCredentials=!1,r.timeout=this.timeout,this.requestSend(t,r,!0),new Promise((function(t,n){r.onreadystatechange=function(){if(4===r.readyState&&1!==r.timeout){var i=r.responseText;try{i=e.formatResponse(i),200!==r.status||i.Error?n(i):t(i)}catch(e){n(e)}}},r.onerror=function(e){n(e)},r.ontimeout=function(e){n(e)}}))}},{key:"isConnected",value:function(){try{return this.send({method:"GET",url:"blockChain/chainStatus"}),!0}catch(e){return!1}}},{key:"isConnectedAsync",value:(t=g()(b.a.mark((function e(){return b.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,this.sendAsyncByFetch({method:"GET",url:"blockChain/chainStatus"});case 3:return e.abrupt("return",e.sent);case 6:return e.prev=6,e.t0=e.catch(0),e.abrupt("return",!1);case 9:case"end":return e.stop()}}),e,this,[[0,6]])}))),function(){return t.apply(this,arguments)})}],[{key:"formatResponse",value:function(e){var t;try{t=JSON.parse(e)}catch(r){t=e}return t}},{key:"formatResponseText",value:function(e){var t;try{var r=e;t={status:r.status,error:200===r.status?0:r.status,Error:{message:e.statusText},statusText:e.statusText}}catch(r){t=e}return t}},{key:"timeoutPromise",value:function(e){return new Promise((function(t){var r=setTimeout((function(){clearTimeout(r),t({type:"timeout"})}),e)}))}}]),e}(),M=r(52),R=r(1),P=r(14),j=r(5),B=function e(){i()(this,e),this.defaultAccount=void 0};function C(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}r.d(t,"default",(function(){return L}));var N=h.a.sha256,L=function(){function e(t){i()(this,e),f()(this,"providers",{HttpProvider:I}),f()(this,"settings",new B),f()(this,"version",{api:"3.2.44"}),this._requestManager=new p(t),this.currentProvider=t,this.chain=new d.a(this._requestManager)}return a()(e,[{key:"isConnected",value:function(){return this.currentProvider&&this.currentProvider.isConnected()}},{key:"reset",value:function(e){this._requestManager.reset(e),this.settings=new B}},{key:"setProvider",value:function(e){this._requestManager.setProvider(e),this.currentProvider=e}}]),e}();f()(L,"version","3.2.44"),f()(L,"providers",{HttpProvider:I}),f()(L,"pbjs",c),f()(L,"pbUtils",P),f()(L,"wallet",M.a),f()(L,"utils",function(e){for(var t=1;t0?a-4:a;for(r=0;r>16&255,f[u++]=t>>8&255,f[u++]=255&t;2===s&&(t=i[e.charCodeAt(r)]<<2|i[e.charCodeAt(r+1)]>>4,f[u++]=255&t);1===s&&(t=i[e.charCodeAt(r)]<<10|i[e.charCodeAt(r+1)]<<4|i[e.charCodeAt(r+2)]>>2,f[u++]=t>>8&255,f[u++]=255&t);return f},t.fromByteArray=function(e){for(var t,r=e.length,i=r%3,o=[],a=0,s=r-i;as?s:a+16383));1===i?(t=e[r-1],o.push(n[t>>2]+n[t<<4&63]+"==")):2===i&&(t=(e[r-2]<<8)+e[r-1],o.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"="));return o.join("")};for(var n=[],i=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,f=a.length;s0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function u(e,t,r){for(var i,o,a=[],s=t;s>18&63]+n[o>>12&63]+n[o>>6&63]+n[63&o]);return a.join("")}i["-".charCodeAt(0)]=62,i["_".charCodeAt(0)]=63},function(e,t){\n/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */\nt.read=function(e,t,r,n,i){var o,a,s=8*i-n-1,f=(1<>1,u=-7,h=r?i-1:0,l=r?-1:1,d=e[t+h];for(h+=l,o=d&(1<<-u)-1,d>>=-u,u+=s;u>0;o=256*o+e[t+h],h+=l,u-=8);for(a=o&(1<<-u)-1,o>>=-u,u+=n;u>0;a=256*a+e[t+h],h+=l,u-=8);if(0===o)o=1-c;else{if(o===f)return a?NaN:1/0*(d?-1:1);a+=Math.pow(2,n),o-=c}return(d?-1:1)*a*Math.pow(2,o-n)},t.write=function(e,t,r,n,i,o){var a,s,f,c=8*o-i-1,u=(1<>1,l=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,d=n?0:o-1,p=n?1:-1,y=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=u):(a=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-a))<1&&(a--,f*=2),(t+=a+h>=1?l/f:l*Math.pow(2,1-h))*f>=2&&(a++,f/=2),a+h>=u?(s=0,a=u):a+h>=1?(s=(t*f-1)*Math.pow(2,i),a+=h):(s=t*Math.pow(2,h-1)*Math.pow(2,i),a=0));i>=8;e[r+d]=255&s,d+=p,s/=256,i-=8);for(a=a<0;e[r+d]=255&a,d+=p,a/=256,c-=8);e[r+d-p]|=128*y}},function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var i=Object.getOwnPropertyDescriptor(t,r);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,i)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),i=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),i(r(12),t),i(r(44),t),i(r(45),t)},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RPCMethodsUnimplemented=t.RPCMethodsBase=t.ResponseCode=void 0,function(e){e[e.ERROR_IN_PARAMS=-1]="ERROR_IN_PARAMS",e[e.UNKNOWN_METHOD=-2]="UNKNOWN_METHOD",e[e.UNIMPLEMENTED=-3]="UNIMPLEMENTED",e[e.UNAUTHENTICATED=-4]="UNAUTHENTICATED",e[e.SUCCESS=0]="SUCCESS",e[e.INTERNAL_ERROR=1]="INTERNAL_ERROR",e[e.TIMEOUT=2]="TIMEOUT",e[e.USER_DENIED=3]="USER_DENIED"}(t.ResponseCode||(t.ResponseCode={})),function(e){e.ACCOUNTS="accounts",e.REQUEST_ACCOUNTS="requestAccounts",e.DECRYPT="decrypt",e.CHAIN_ID="chainId",e.GET_PUBLIC_KEY="getEncryptionPublicKey",e.SEND_TRANSACTION="sendTransaction"}(t.RPCMethodsBase||(t.RPCMethodsBase={})),function(e){e.ADD_CHAIN="wallet_addEthereumChain",e.SWITCH_CHAIN="wallet_switchEthereumChain",e.REQUEST_PERMISSIONS="wallet_requestPermissions",e.GET_PERMISSIONS="wallet_getPermissions",e.NET_VERSION="net_version"}(t.RPCMethodsUnimplemented||(t.RPCMethodsUnimplemented={}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CentralEthereumEvents=void 0,function(e){e.CONNECTED="connected",e.MESSAGE="message",e.DISCONNECTED="disconnected",e.ACCOUNT_CHANGED="accountChanged",e.CHAIN_CHANGED="chainChanged",e.ERROR="error"}(t.CentralEthereumEvents||(t.CentralEthereumEvents={}))},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,r){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){for(var r=0;r0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,r=""+t.data;t=t.next;)r+=e+t.data;return r},e.prototype.concat=function(e){if(0===this.length)return n.alloc(0);for(var t,r,i,o=n.allocUnsafe(e>>>0),a=this.head,s=0;a;)t=a.data,r=o,i=s,t.copy(r,i),s+=a.data.length,a=a.next;return o},e}(),i&&i.inspect&&i.inspect.custom&&(e.exports.prototype[i.inspect.custom]=function(){var e=i.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,r){(function(e){var n=void 0!==e&&e||"undefined"!=typeof self&&self||window,i=Function.prototype.apply;function o(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new o(i.call(setTimeout,n,arguments),clearTimeout)},t.setInterval=function(){return new o(i.call(setInterval,n,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},o.prototype.unref=o.prototype.ref=function(){},o.prototype.close=function(){this._clearFn.call(n,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},r(34),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,r(1))},function(e,t,r){(function(e,t){!function(e,r){"use strict";if(!e.setImmediate){var n,i,o,a,s,f=1,c={},u=!1,h=e.document,l=Object.getPrototypeOf&&Object.getPrototypeOf(e);l=l&&l.setTimeout?l:e,"[object process]"==={}.toString.call(e.process)?n=function(e){t.nextTick((function(){p(e)}))}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,r=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=r,t}}()?e.MessageChannel?((o=new MessageChannel).port1.onmessage=function(e){p(e.data)},n=function(e){o.port2.postMessage(e)}):h&&"onreadystatechange"in h.createElement("script")?(i=h.documentElement,n=function(e){var t=h.createElement("script");t.onreadystatechange=function(){p(e),t.onreadystatechange=null,i.removeChild(t),t=null},i.appendChild(t)}):n=function(e){setTimeout(p,0,e)}:(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&p(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),n=function(t){e.postMessage(a+t,"*")}),l.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r */\nvar n=r(5),i=n.Buffer;function o(e,t){for(var r in e)t[r]=e[r]}function a(e,t,r){return i(e,t,r)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=n:(o(n,t),t.Buffer=a),a.prototype=Object.create(i.prototype),o(i,a),a.from=function(e,t,r){if("number"==typeof e)throw new TypeError("Argument must not be a number");return i(e,t,r)},a.alloc=function(e,t,r){if("number"!=typeof e)throw new TypeError("Argument must be a number");var n=i(e);return void 0!==t?"string"==typeof r?n.fill(t,r):n.fill(t):n.fill(0),n},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return i(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return n.SlowBuffer(e)}},function(e,t,r){"use strict";e.exports=o;var n=r(19),i=Object.create(r(4));function o(e){if(!(this instanceof o))return new o(e);n.call(this,e)}i.inherits=r(2),i.inherits(o,n),o.prototype._transform=function(e,t,r){r(null,e)}},function(e,t,r){e.exports=r(10)},function(e,t,r){e.exports=r(0)},function(e,t,r){e.exports=r(8).Transform},function(e,t,r){e.exports=r(8).PassThrough},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,r){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:t.COMMON_PRIVATE;return o.getWalletByPrivateKey(e)},t.formatFunctionName=function(e){return e.replace(e[0],e[0].toLocaleUpperCase())}},function(e,t,r){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){return(i=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function o(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=s(e);if(t){var i=s(this).constructor;r=Reflect.construct(n,arguments,i)}else r=n.apply(this,arguments);return a(this,r)}}function a(e,t){if(t&&("object"===n(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn\'t been initialised - super() hasn\'t been called");return e}(e)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function f(e,t){for(var r=0;r=o)return e;switch(e){case"%s":return String(n[r++]);case"%d":return Number(n[r++]);case"%j":try{return JSON.stringify(n[r++])}catch(e){return"[Circular]"}default:return e}})),f=n[r];r=3&&(n.depth=arguments[2]),arguments.length>=4&&(n.colors=arguments[3]),p(r)?n.showHidden=r:r&&t._extend(n,r),g(n.showHidden)&&(n.showHidden=!1),g(n.depth)&&(n.depth=2),g(n.colors)&&(n.colors=!1),g(n.customInspect)&&(n.customInspect=!0),n.colors&&(n.stylize=f),u(n,e,n.depth)}function f(e,t){var r=s.styles[t];return r?"["+s.colors[r][0]+"m"+e+"["+s.colors[r][1]+"m":e}function c(e,t){return e}function u(e,r,n){if(e.customInspect&&r&&S(r.inspect)&&r.inspect!==t.inspect&&(!r.constructor||r.constructor.prototype!==r)){var i=r.inspect(n,e);return v(i)||(i=u(e,i,n)),i}var o=function(e,t){if(g(t))return e.stylize("undefined","undefined");if(v(t)){var r="\'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/\'/g,"\\\\\'").replace(/\\\\"/g,\'"\')+"\'";return e.stylize(r,"string")}if(b(t))return e.stylize(""+t,"number");if(p(t))return e.stylize(""+t,"boolean");if(y(t))return e.stylize("null","null")}(e,r);if(o)return o;var a=Object.keys(r),s=function(e){var t={};return e.forEach((function(e,r){t[e]=!0})),t}(a);if(e.showHidden&&(a=Object.getOwnPropertyNames(r)),E(r)&&(a.indexOf("message")>=0||a.indexOf("description")>=0))return h(r);if(0===a.length){if(S(r)){var f=r.name?": "+r.name:"";return e.stylize("[Function"+f+"]","special")}if(m(r))return e.stylize(RegExp.prototype.toString.call(r),"regexp");if(_(r))return e.stylize(Date.prototype.toString.call(r),"date");if(E(r))return h(r)}var c,w="",A=!1,k=["{","}"];(d(r)&&(A=!0,k=["[","]"]),S(r))&&(w=" [Function"+(r.name?": "+r.name:"")+"]");return m(r)&&(w=" "+RegExp.prototype.toString.call(r)),_(r)&&(w=" "+Date.prototype.toUTCString.call(r)),E(r)&&(w=" "+h(r)),0!==a.length||A&&0!=r.length?n<0?m(r)?e.stylize(RegExp.prototype.toString.call(r),"regexp"):e.stylize("[Object]","special"):(e.seen.push(r),c=A?function(e,t,r,n,i){for(var o=[],a=0,s=t.length;a=0&&0,e+t.replace(/\\u001b\\[\\d\\d?m/g,"").length+1}),0)>60)return r[0]+(""===t?"":t+"\\n ")+" "+e.join(",\\n ")+" "+r[1];return r[0]+t+" "+e.join(", ")+" "+r[1]}(c,w,k)):k[0]+w+k[1]}function h(e){return"["+Error.prototype.toString.call(e)+"]"}function l(e,t,r,n,i,o){var a,s,f;if((f=Object.getOwnPropertyDescriptor(t,i)||{value:t[i]}).get?s=f.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):f.set&&(s=e.stylize("[Setter]","special")),T(n,i)||(a="["+i+"]"),s||(e.seen.indexOf(f.value)<0?(s=y(r)?u(e,f.value,null):u(e,f.value,r-1)).indexOf("\\n")>-1&&(s=o?s.split("\\n").map((function(e){return" "+e})).join("\\n").substr(2):"\\n"+s.split("\\n").map((function(e){return" "+e})).join("\\n")):s=e.stylize("[Circular]","special")),g(a)){if(o&&i.match(/^\\d+$/))return s;(a=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.substr(1,a.length-2),a=e.stylize(a,"name")):(a=a.replace(/\'/g,"\\\\\'").replace(/\\\\"/g,\'"\').replace(/(^"|"$)/g,"\'"),a=e.stylize(a,"string"))}return a+": "+s}function d(e){return Array.isArray(e)}function p(e){return"boolean"==typeof e}function y(e){return null===e}function b(e){return"number"==typeof e}function v(e){return"string"==typeof e}function g(e){return void 0===e}function m(e){return w(e)&&"[object RegExp]"===A(e)}function w(e){return"object"==typeof e&&null!==e}function _(e){return w(e)&&"[object Date]"===A(e)}function E(e){return w(e)&&("[object Error]"===A(e)||e instanceof Error)}function S(e){return"function"==typeof e}function A(e){return Object.prototype.toString.call(e)}function k(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(r){if(g(o)&&(o=e.env.NODE_DEBUG||""),r=r.toUpperCase(),!a[r])if(new RegExp("\\\\b"+r+"\\\\b","i").test(o)){var n=e.pid;a[r]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",r,n,e)}}else a[r]=function(){};return a[r]},t.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.isArray=d,t.isBoolean=p,t.isNull=y,t.isNullOrUndefined=function(e){return null==e},t.isNumber=b,t.isString=v,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=g,t.isRegExp=m,t.isObject=w,t.isDate=_,t.isError=E,t.isFunction=S,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=r(50);var O=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function x(){var e=new Date,t=[k(e.getHours()),k(e.getMinutes()),k(e.getSeconds())].join(":");return[e.getDate(),O[e.getMonth()],t].join(" ")}function T(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",x(),t.format.apply(t,arguments))},t.inherits=r(51),t._extend=function(e,t){if(!t||!w(t))return e;for(var r=Object.keys(t),n=r.length;n--;)e[r[n]]=t[r[n]];return e};var I="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function M(e,t){if(!e){var r=new Error("Promise was rejected with a falsy value");r.reason=e,e=r}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError(\'The "original" argument must be of type Function\');if(I&&e[I]){var t;if("function"!=typeof(t=e[I]))throw new TypeError(\'The "util.promisify.custom" argument must be of type Function\');return Object.defineProperty(t,I,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,r,n=new Promise((function(e,n){t=e,r=n})),i=[],o=0;o window.addEventListener('DOMContentLoaded', e, { once: !0 })); + })(), + window._metamaskSetupProvider(); + })()); + }, +]); diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index 49ae96177c..5dc3812a3a 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -125,6 +125,7 @@ "react-native-tab-view": "^3.2.1", "react-native-vector-icons": "^9.1.0", "react-native-webview": "^11.23.0", + "react-native-fs": "^2.20.0", "react-redux": "^8.0.2", "redux-flipper": "^2.0.2", "redux-persist": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index fafa09390d..a120a1c040 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9747,6 +9747,11 @@ balanced-match@^2.0.0: resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA== + base-64@^1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" @@ -23708,6 +23713,14 @@ react-native-flipper@^0.161.0: resolved "https://registry.npmmirror.com/react-native-flipper/-/react-native-flipper-0.161.0.tgz#816dd567883a30b894c5f8bc4b92f86303a550ce" integrity sha512-AouPbb40n0HODRbOokcZOJPsx42gLXZvQo7xX8RuO0zvVJGCgalgAUcdWeQgjalDIcOdjd4FWUeUvC++6cqYKA== +react-native-fs@^2.20.0: + version "2.20.0" + resolved "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6" + integrity sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ== + dependencies: + base-64 "^0.1.0" + utf8 "^3.0.0" + react-native-gesture-handler@^2.5.0: version "2.7.0" resolved "https://registry.npmmirror.com/react-native-gesture-handler/-/react-native-gesture-handler-2.7.0.tgz#53ad828add926c8e025f68ea581758c0f8893054" @@ -27679,7 +27692,7 @@ utf-8-validate@^5.0.2: dependencies: node-gyp-build "^4.3.0" -utf8@3.0.0: +utf8@3.0.0, utf8@^3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== From 451860fa6fca6ac189b1940ac1ffaf4eccc5018f Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 22 May 2023 15:00:16 +0800 Subject: [PATCH 002/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/ios/Podfile.lock | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/mobile-app-did/ios/Podfile.lock b/packages/mobile-app-did/ios/Podfile.lock index de786c97cd..f581511583 100644 --- a/packages/mobile-app-did/ios/Podfile.lock +++ b/packages/mobile-app-did/ios/Podfile.lock @@ -665,6 +665,8 @@ PODS: - RNFBApp - RNFlashList (1.4.0): - React-Core + - RNFS (2.20.0): + - React-Core - RNGestureHandler (2.7.0): - React-Core - RNGoogleSignin (8.2.2): @@ -814,6 +816,7 @@ DEPENDENCIES: - "RNFBMessaging (from `../node_modules/@react-native-firebase/messaging`)" - "RNFBPerf (from `../node_modules/@react-native-firebase/perf`)" - "RNFlashList (from `../node_modules/@shopify/flash-list`)" + - RNFS (from `../node_modules/react-native-fs`) - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)" - RNLocalize (from `../node_modules/react-native-localize`) @@ -1029,6 +1032,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-firebase/perf" RNFlashList: :path: "../node_modules/@shopify/flash-list" + RNFS: + :path: "../node_modules/react-native-fs" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNGoogleSignin: @@ -1169,6 +1174,7 @@ SPEC CHECKSUMS: RNFBMessaging: a006029b26c2d7b34c93c45ff1b8fedb5fae35d5 RNFBPerf: 789e745a88bc520bcab1fcc9e6cc80d3c9aaa6aa RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a + RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 7673697e7c0e9391adefae4faa087442bc04af33 RNGoogleSignin: 81521697b2c8f97f9a586ac7257b1a1d9b51b115 RNLocalize: 0df7970cfc60389f00eb62fd7c097dc75af3fb4f From 73014c5b36645071c367b35f417d7b84c41354be Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Thu, 25 May 2023 14:34:29 +0800 Subject: [PATCH 003/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20test=20discover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/.gitignore | 9 +- .../mobile-app-did/js/Test/Discover/index.tsx | 117 ++++++++++++++++++ .../mobile-app-did/js/navigation/index.tsx | 2 + .../mobile-app-did/js/pages/Home/index.tsx | 6 + .../js/utils/EntryScriptWeb3.ts | 6 +- .../js/utils/InpageBridgeWeb3.js | 106 ---------------- packages/mobile-app-did/package.json | 1 + yarn.lock | 15 +++ 8 files changed, 151 insertions(+), 111 deletions(-) create mode 100644 packages/mobile-app-did/js/Test/Discover/index.tsx delete mode 100644 packages/mobile-app-did/js/utils/InpageBridgeWeb3.js diff --git a/packages/mobile-app-did/.gitignore b/packages/mobile-app-did/.gitignore index ad22031db9..930dc85a14 100644 --- a/packages/mobile-app-did/.gitignore +++ b/packages/mobile-app-did/.gitignore @@ -63,8 +63,11 @@ buck-out/ /ios/Pods/ /vendor/bundle/ -#svg +# svg handleSvg.js -#env -.env \ No newline at end of file +# env +.env + +# InpageBridgeWeb3 +/js/utils/InpageBridgeWeb3.js diff --git a/packages/mobile-app-did/js/Test/Discover/index.tsx b/packages/mobile-app-did/js/Test/Discover/index.tsx new file mode 100644 index 0000000000..428f3aa442 --- /dev/null +++ b/packages/mobile-app-did/js/Test/Discover/index.tsx @@ -0,0 +1,117 @@ +import React, { useCallback, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import WebView from 'react-native-webview'; +import CustomHeader from 'components/CustomHeader'; +import SafeAreaBox from 'components/SafeAreaBox'; +import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; +import { pTd } from 'utils/unit'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { upDateRecordsItem } from '@portkey-wallet/store/store-ca/discover/slice'; +import navigationService from 'utils/navigationService'; +import { ACH_REDIRECT_URL } from 'constants/common'; +import useEffectOnce from 'hooks/useEffectOnce'; +import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; + +const safeAreaColorMap = { + white: defaultColors.bg1, + blue: defaultColors.bg5, + gray: defaultColors.bg4, + transparent: 'transparent', +}; + +export type SafeAreaColorMapKeyUnit = keyof typeof safeAreaColorMap; + +type WebViewPageType = 'default' | 'discover' | 'ach'; +EntryScriptWeb3.init(); +const Discover: React.FC = () => { + const { + title = '', + url, + webViewPageType = 'default', + injectedJavaScript, + } = useRouterParams<{ + url: string; + title?: string; + webViewPageType?: WebViewPageType; + injectedJavaScript?: string; + }>(); + + const dispatch = useAppCommonDispatch(); + const webViewRef = React.useRef(null); + const [entryScriptWeb3, setEntryScriptWeb3] = useState(); + + useEffectOnce(() => { + const getEntryScriptWeb3 = async () => { + const script = await EntryScriptWeb3.get(); + setEntryScriptWeb3(script); + }; + + getEntryScriptWeb3(); + }); + const handleNavigationStateChange = useCallback( + (navState: any) => { + if (webViewPageType === 'default') return; + if (webViewPageType === 'ach') { + if (navState.url.startsWith(ACH_REDIRECT_URL)) { + navigationService.navigate('Tab'); + } + return; + } + dispatch(upDateRecordsItem({ url, title: title ? title : navState.title })); + }, + [dispatch, title, url, webViewPageType], + ); + return ( + + + { + const data = JSON.parse(nativeEvent.data) as any; + console.log(data, '=====data'); + + const { eventName } = data || {}; + webViewRef.current?.postMessage( + JSON.stringify({ + eventName, + info: { + code: 0, + data: { + balance: 100, + }, + msg: 'balance', + }, + }), + ); + }} + /> + + ); +}; + +export default Discover; + +export const pageStyles = StyleSheet.create({ + pageWrap: { + paddingLeft: 0, + paddingRight: 0, + backgroundColor: defaultColors.bg1, + }, + svgWrap: { + marginRight: pTd(16), + }, + webViewContainer: { + flex: 1, + backgroundColor: 'red', + }, + webView: { + width: '100%', + flex: 1, + }, + noResult: {}, +}); diff --git a/packages/mobile-app-did/js/navigation/index.tsx b/packages/mobile-app-did/js/navigation/index.tsx index 11b88c849b..5560059c1f 100644 --- a/packages/mobile-app-did/js/navigation/index.tsx +++ b/packages/mobile-app-did/js/navigation/index.tsx @@ -21,6 +21,7 @@ import MyNav from 'pages/My/router'; import BuyNav from 'pages/Buy'; import DiscoverNav from 'pages/Discover/index'; import { isIos } from '@portkey-wallet/utils/mobile/device'; +import Discover from 'Test/Discover'; const Stack = createStackNavigator(); export const stackNav = [ @@ -33,6 +34,7 @@ export const stackNav = [ // FIXME: test page { name: 'Home', component: Home }, + { name: 'Discover', component: Discover }, ...GuardianNav, ...ActivityNav, diff --git a/packages/mobile-app-did/js/pages/Home/index.tsx b/packages/mobile-app-did/js/pages/Home/index.tsx index ef13fd81ff..de064f8d84 100644 --- a/packages/mobile-app-did/js/pages/Home/index.tsx +++ b/packages/mobile-app-did/js/pages/Home/index.tsx @@ -201,6 +201,12 @@ export default function HomeScreen() { console.log(ipAddress, ipAddress2, '======ipAddress'); }} /> + - ) : ( - - )} - - )} - - {permissionList?.map((account) => ( -
  • -
    -

    {account.accountName}

    -

    {shortenCharacters(account.address)}

    -
    - -
  • - ))} - - )} - - ); -} diff --git a/packages/web-extension-did/app/web/models/index.tsx b/packages/web-extension-did/app/web/models/index.tsx index 7ed67bff6b..996dc58f45 100644 --- a/packages/web-extension-did/app/web/models/index.tsx +++ b/packages/web-extension-did/app/web/models/index.tsx @@ -1,9 +1,3 @@ -import AccountConnect from './AccountConnect'; - export default function Modals() { - return ( - <> - - - ); + return <>; } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx new file mode 100644 index 0000000000..415676a2a8 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -0,0 +1,80 @@ +import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { ChainId } from '@portkey-wallet/types'; +import { handleErrorMessage } from '@portkey-wallet/utils'; +import aes from '@portkey-wallet/utils/aes'; +import { Button, message } from 'antd'; +import usePromptSearch from 'hooks/usePromptSearch'; +import { useCallback } from 'react'; +import { useUserInfo } from 'store/Provider/hooks'; +import errorHandler from 'utils/errorHandler'; +import { closePrompt } from 'utils/lib/serviceWorkerAction'; +import { callSendMethod } from 'utils/sandboxUtil/sendTransactions'; + +export default function SendTransactions() { + const detail = usePromptSearch<{ + payload: { + chainId: ChainId; + contractAddress: string; + method: string; + params: any; + }; + }>(); + const chainInfo = useCurrentChain(detail?.payload?.chainId); + const wallet = useCurrentWalletInfo(); + const { passwordSeed } = useUserInfo(); + + const sendHandler = useCallback(async () => { + try { + if (!chainInfo?.endPoint || !wallet?.caHash) { + closePrompt({ ...errorHandler(400001), data: { code: 4002, msg: 'invalid chain id' } }); + return; + } + const { payload } = detail; + const isCAManagerForwardCall = chainInfo.caContractAddress !== payload.contractAddress; + let paramsOption = payload.params?.paramsOption; + + const functionName = isCAManagerForwardCall ? 'ManagerForwardCall' : payload.method; + + paramsOption = isCAManagerForwardCall + ? { + caHash: wallet.caHash, + methodName: payload.method, + contractAddress: payload.contractAddress, + args: paramsOption, + } + : paramsOption; + const privateKey = aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed); + if (!privateKey) throw 'Invalid user information, please check'; + const result = await callSendMethod({ + rpcUrl: chainInfo.endPoint, + chainType: 'aelf', + methodName: functionName, + paramsOption, + privateKey, + address: chainInfo.caContractAddress, + sendOptions: { onMethod: 'transactionHash' }, + }); + closePrompt({ + ...errorHandler(0), + data: result.result, + }); + } catch (error) { + console.error(error, 'error===detail'); + message.error(handleErrorMessage(error)); + } + }, [chainInfo, detail, wallet, passwordSeed]); + + return ( +
    + {JSON.stringify(detail)} + + +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/components/LockPage/index.tsx b/packages/web-extension-did/app/web/pages/components/LockPage/index.tsx index f237ac07a6..ee331ac337 100644 --- a/packages/web-extension-did/app/web/pages/components/LockPage/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/LockPage/index.tsx @@ -6,13 +6,13 @@ import CustomSvg from 'components/CustomSvg'; import InternalMessage from 'messages/InternalMessage'; import InternalMessageTypes from 'messages/InternalMessageTypes'; import { ReactNode, useCallback, useState } from 'react'; -import { getLocalStorage } from 'utils/storage/chromeStorage'; import { setPasswordSeed } from 'store/reducers/user/slice'; import { useDispatch } from 'react-redux'; import './index.less'; import { useTranslation } from 'react-i18next'; import aes from '@portkey-wallet/utils/aes'; import { sleep } from '@portkey-wallet/utils'; +import { getWalletState } from 'utils/lib/SWGetReduxStore'; interface LockPageProps extends FormProps { onUnLockHandler?: (pwd: string) => void; @@ -28,18 +28,12 @@ export default function LockPage({ header, onUnLockHandler, ...props }: LockPage const onFinish = useCallback( async (values: any) => { const { password } = values; - console.log(password, 'password===walletInfo'); setIsPassword(-1); - let walletInfo = null; - try { - const walletStorage = await getLocalStorage('reduxStorageWallet'); - walletInfo = JSON.parse(JSON.parse(walletStorage).walletInfo); - } catch (e) { - walletInfo = null; - } - if (!walletInfo) return message.error(WalletError.noCreateWallet); + const wallet = await getWalletState(); + + if (!wallet.walletInfo) return message.error(WalletError.noCreateWallet); - const privateKey = aes.decrypt(walletInfo.AESEncryptPrivateKey, password); + const privateKey = aes.decrypt(wallet.walletInfo.AESEncryptPrivateKey, password); if (privateKey) { setIsPassword(1); dispatch(setPasswordSeed(password)); diff --git a/packages/web-extension-did/app/web/sandboxUtil.ts b/packages/web-extension-did/app/web/sandboxUtil.ts index 8a57d2f4eb..a95a4fea3f 100644 --- a/packages/web-extension-did/app/web/sandboxUtil.ts +++ b/packages/web-extension-did/app/web/sandboxUtil.ts @@ -232,7 +232,7 @@ class SandboxUtil { const data = event.data.data ?? {}; try { - const { rpcUrl, address, methodName, privateKey, paramsOption, chainType, isGetSignTx = 0, sendOptions } = data; + const { rpcUrl, address, methodName, privateKey, paramsOption, chainType, sendOptions } = data; console.log(data, 'sendHandler=data'); if (!rpcUrl || !address || !methodName) return callback(event, { @@ -251,8 +251,9 @@ class SandboxUtil { const account = getWallet(privateKey); const contract = await SandboxUtil._getELFSendContract(rpcUrl, address, privateKey); - const contractMethod = !isGetSignTx ? contract?.callSendMethod : contract?.encodedTx; + const contractMethod = contract?.callSendMethod; const req = await contractMethod?.(methodName, account, paramsOption, sendOptions); + console.log(req, 'req===callSendMethod'); if (req?.error) return callback(event, { code: SandboxErrorCode.error, @@ -260,7 +261,11 @@ class SandboxUtil { sid: data.sid, error: req.error, }); - return callback(event, { code: SandboxErrorCode.success, message: req?.data, sid: data.sid }); + return callback(event, { + code: SandboxErrorCode.success, + message: req?.data || req, + sid: data.sid, + }); } catch (e: any) { callback(event, { code: SandboxErrorCode.error, diff --git a/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts b/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts index 42dddd3aea..ee04da8ab8 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts @@ -19,7 +19,7 @@ export default async function getPromptConfig({ message }: PromptConfigParam) { case PromptRouteTypes.REGISTER_START_WALLET: case PromptRouteTypes.SWITCH_CHAIN: case PromptRouteTypes.CONNECT_WALLET: - case PromptRouteTypes.SIGN_MESSAGE: + case PromptRouteTypes.SEND_TRANSACTION: case PromptRouteTypes.EXPAND_FULL_SCREEN: case PromptRouteTypes.SETTING: case PromptRouteTypes.ADD_GUARDIANS: diff --git a/packages/web-extension-did/app/web/service/NotificationService/index.ts b/packages/web-extension-did/app/web/service/NotificationService/index.ts index 0586f501da..27898e1264 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/index.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/index.ts @@ -40,7 +40,7 @@ export default class NotificationService { this.openWindow = null; } if (this.closeSender?.[number]) { - this.closeSender?.[number]?.sendResponse?.(errorHandler(200010)); + this.closeSender?.[number]?.sendResponse?.(errorHandler(200003)); delete this.closeSender?.[number]; } }); @@ -50,7 +50,7 @@ export default class NotificationService { this.openTag = null; } if (this.closeSender?.[number]) { - this.closeSender?.[number]?.sendResponse?.(errorHandler(200010)); + this.closeSender?.[number]?.sendResponse?.(errorHandler(200003)); delete this.closeSender?.[number]; } }); diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 20d2f26b96..7a3fb7a56b 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -20,7 +20,7 @@ import { apis } from 'utils/BrowserApis'; import SocialLoginController from 'controllers/socialLoginController'; import OpenNewTabController from 'controllers/openNewTabController'; import { LocalStream } from 'utils/extensionStreams'; -import { RPCMethodsUnimplemented, RPCMethodsBase } from '@portkey/provider-types'; +import { MethodsUnimplemented, MethodsBase } from '@portkey/provider-types'; import { getWalletState } from 'utils/lib/SWGetReduxStore'; const notificationService = new NotificationService(); @@ -52,12 +52,12 @@ const permissionWhitelist = [ PortkeyMessageTypes.EXPAND_FULL_SCREEN, PortkeyMessageTypes.ACTIVE_LOCK_STATUS, PortkeyMessageTypes.PERMISSION_FINISH, - RPCMethodsUnimplemented.GET_WALLET_STATE, + MethodsUnimplemented.GET_WALLET_STATE, // The method that requires the dapp not to trigger the lock call - RPCMethodsBase.ACCOUNTS, - RPCMethodsBase.CHAIN_ID, - RPCMethodsBase.CHAIN_IDS, - RPCMethodsBase.CHAINS_INFO, + MethodsBase.ACCOUNTS, + MethodsBase.CHAIN_ID, + MethodsBase.CHAIN_IDS, + MethodsBase.CHAINS_INFO, ]; const initPageState = async () => { @@ -177,10 +177,6 @@ export default class ServiceWorkerInstantiate { case PortkeyMessageTypes.SOCIAL_LOGIN: this.socialLogin(sendResponse, message.payload); break; - - case WalletMessageTypes.GET_WALLET_STATE: - ServiceWorkerInstantiate.getWalletState(sendResponse); - break; case WalletMessageTypes.SET_RECAPTCHA_CODE_V2: this.getRecaptcha(sendResponse, message.payload); break; @@ -190,6 +186,7 @@ export default class ServiceWorkerInstantiate { // case PortkeyMessageTypes.FINISH_ASYNC_TASK: // this.dealNextTask(sendResponse, message.payload); // break; + default: if (this.aelfMethodController.aelfMethodList.includes(message.type)) { this.aelfMethodController.dispenseMessage(message, sendResponse); @@ -368,14 +365,6 @@ export default class ServiceWorkerInstantiate { }); } - static async getWalletState(sendResponse: SendResponseFun) { - try { - sendResponse(errorHandler(0)); - } catch (error) { - sendResponse(errorHandler(200004, error)); - } - } - /*** * Sets the seed on scope to use from decryption * @param sendResponse - Delegating response handler @@ -435,9 +424,11 @@ export default class ServiceWorkerInstantiate { static lockWallet(sendResponse?: SendResponseFun, message?: any) { try { - console.log('lockWallet', message); - seed = null; - SWEventController.lockStateChanged(true, sendResponse); + if (seed) { + console.log('lockWallet', message); + seed = null; + SWEventController.lockStateChanged(true, sendResponse); + } } catch (e) { sendResponse?.(errorHandler(500001, e)); } diff --git a/packages/web-extension-did/app/web/store/utils/CreateAccountAndConnect.tsx b/packages/web-extension-did/app/web/store/utils/CreateAccountAndConnect.tsx deleted file mode 100644 index d89ccc5b22..0000000000 --- a/packages/web-extension-did/app/web/store/utils/CreateAccountAndConnect.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { - addAccount, - addAndReplaceAccount, - importAccount, - setCurrentAccount, -} from '@portkey-wallet/store/wallet/actions'; -import { setAccountConnectModal } from 'store/reducers/modal/slice'; -import { getCurrentTab } from 'utils/platforms'; -import { getConnections } from 'utils/storage/storage.utils'; - -const promptForAccountConnection = async (dispatch: any, address?: string) => { - const currentTab = await getCurrentTab(); - const url = currentTab?.url; - if (!url) return; - const _origin = new URL(url).origin; - const connections = await getConnections(); - const permission = connections[_origin]?.permission; - if (!permission?.accountList?.length) return; - if (address && permission.accountList.some((account) => account === address)) return; - dispatch(setAccountConnectModal(true)); -}; - -export const addAccountAndConnect = (params: { password: string; accountName?: string }) => async (dispatch: any) => { - const res = dispatch(addAccount(params)); - await promptForAccountConnection(dispatch); - return res; -}; - -export const importAccountAndConnect = - (params: { password: string; privateKey: string; accountName?: string }) => async (dispatch: any) => { - const res = dispatch(importAccount(params)); - await promptForAccountConnection(dispatch); - return res; - }; - -export const addAndReplaceAccountAndConnect = - (params: { password: string; accountName?: string }) => async (dispatch: any) => { - const res = dispatch(addAndReplaceAccount(params)); - await promptForAccountConnection(dispatch); - return res; - }; - -export const setCurrentAccountAndConnect = (params: { address: string; password: string }) => async (dispatch: any) => { - const res = dispatch(setCurrentAccount(params)); - console.log(res, 'res=='); - await promptForAccountConnection(dispatch, params.address); - return res; -}; diff --git a/packages/web-extension-did/app/web/utils/errorHandler.ts b/packages/web-extension-did/app/web/utils/errorHandler.ts index 400182ea27..ff82dceb68 100644 --- a/packages/web-extension-did/app/web/utils/errorHandler.ts +++ b/packages/web-extension-did/app/web/utils/errorHandler.ts @@ -1,3 +1,5 @@ +import { ResponseMessagePreset } from '@portkey/provider-types'; + /** * 1xxxxx try catch * 2xxxxx handle @@ -15,50 +17,33 @@ // B xxxx1x, encryption and decryption related errors; xxxx0x parameter problem. // C 0, no Error const errorMap = { - 0: 'success', + 0: ResponseMessagePreset.SUCCESS, // 1xxxx The error code is the error received by the catch 100001: '', 200001: 'Payload is false.', - 200002: 'Please set permission at first.', - 200003: 'Please set permission at first.', - 200004: 'No Wallet Info.', - 200005: 'Portkey is locked!', - 200006: 'Decrypt Failed. Please unlock your wallet.', - 200007: 'No Portkey in storage.', - 200008: 'Please connect first.', - 200009: 'No permission, can not set whitelist.', - 200010: 'You closed the prompt without any action.', - 200011: 'Decrypt failed, get Portkey failed!', - 200012: 'Wallet error or damaged', - 200013: 'Decrypt keystore failed', - 200014: 'Can not find this wallet.', - 200015: 'PortKet connection failed, please check if your network is secure', - 200016: 'The user is not connected to Portkey, please connect the user first', - 200017: "Please make sure your network is consistent with Portkey's current network", - 200018: 'Chrome extension serviceWorker is invalid', + 200002: 'No Wallet Info.', + 200003: 'You closed the prompt without any action.', + 200004: 'The user is not connected to Portkey, please connect the user first', + 200005: 'Please check your chain connection is correct', + 200006: 'Chrome extension serviceWorker is invalid', // 3xxxxx Temporarily only used for internal redirects - 300000: 'Unlocked your wallet, recall your function please.', + 300000: '', // [40000, 41000) is a dynamic error // 400001 are dynamic errors // [41001, 42000) is fixed bug 400001: '', 410001: 'Forbidden', - 410002: 'Missing param account.', - 410003: 'Missing param contractAddress.', - 410004: 'Missing param sendResponse(function).', - 410005: 'Expected a single, non-array, object argument.', - 410006: `'args.method' must be a non-empty string.`, - 410007: `'args.params' must be an object or array if provided.`, + 410002: ResponseMessagePreset.ERROR_IN_PARAMS, // 5xxxxxx is generally related to the interface request, and the plug-in actively throws it out // Currently only 500001, serviceWorker.ts reports an error // 500002 NotificationService.js failed 500001: '', - 500002: '', + 500002: 'NotificationService error', // 6xxxxx is Failed to establish connection registration 600001: 'Invalid connection', 600002: 'Chrome Extension update, please refresh the page', // 7xxxxx transaction failed - 700001: '', + 700001: ResponseMessagePreset.UNIMPLEMENTED, 700002: 'The contract call failed, please check the contract address and contract name', // 8xxxxx is the error code of the event 800001: '', diff --git a/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts b/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts index 0e8c1b493f..e0014290b9 100644 --- a/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts +++ b/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts @@ -5,7 +5,8 @@ import { JOIN_AUTH_URL, RECAPTCHA_URL } from 'constants/index'; import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import { useCallback } from 'react'; -import { ReCaptchaResponseParams, SendResponseParams } from 'types'; +import { CloseParams } from 'service/NotificationService'; +import { CreatePromptType, ReCaptchaResponseParams, SendResponseParams } from 'types'; import { setLocalStorage } from 'utils/storage/chromeStorage'; export const completeRegistration = async () => { @@ -13,6 +14,13 @@ export const completeRegistration = async () => { await InternalMessage.payload(PortkeyMessageTypes.CLOSE_PROMPT, { isClose: false }).send(); }; +export const closePrompt = async (closeParams?: CloseParams, promptType?: CreatePromptType) => + InternalMessage.payload(PortkeyMessageTypes.CLOSE_PROMPT, { + promptType, + closeParams, + isClose: true, + }).send(); + export const useLockWallet = () => { return useCallback(async () => { try { diff --git a/packages/web-extension-did/app/web/utils/sandboxUtil/sendTransactions.ts b/packages/web-extension-did/app/web/utils/sandboxUtil/sendTransactions.ts new file mode 100644 index 0000000000..bd7c2ffd16 --- /dev/null +++ b/packages/web-extension-did/app/web/utils/sandboxUtil/sendTransactions.ts @@ -0,0 +1,37 @@ +import SandboxEventTypes from 'messages/SandboxEventTypes'; +import SandboxEventService, { SandboxErrorCode } from 'service/SandboxEventService'; +import { BaseSendOption } from './types'; + +export interface CallSendMethodParams extends BaseSendOption { + methodName: string; + paramsOption: any; +} + +export const callSendMethod = async ({ + rpcUrl, + chainType, + address, // contract address + privateKey, + methodName, + paramsOption, + sendOptions, +}: CallSendMethodParams) => { + const resMessage = await SandboxEventService.dispatchAndReceive(SandboxEventTypes.callSendMethod, { + rpcUrl, + chainType, + address, + privateKey, + methodName, + paramsOption, + sendOptions, + }); + + if (resMessage.code === SandboxErrorCode.error) throw resMessage.error.message; + return { + code: resMessage.code, + result: { + rpcUrl, + ...resMessage.message, + }, + }; +}; diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index fd571579e9..cdc3c5da8e 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,8 +16,8 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.8", - "@portkey/provider-utils": "0.0.1-alpha.8", + "@portkey/extension-provider": "0.0.1-alpha.10", + "@portkey/provider-utils": "0.0.1-alpha.10", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index 6bfbf3fcb7..b791d6a2f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5163,21 +5163,13 @@ "@portkey/provider-types" "^0.0.1-alpha.10" aelf-sdk "^3.2.44" -"@portkey/chain@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.8.tgz#20a6d30fe6ceb71c3d430ee898422f0a7a86665d" - integrity sha512-FLdUFprntO76rtH5N0Ef2gJ3moNfWpDwlym92WqcWq15qqjCjbBk/l86FgOkZsfLbzgZggHM1n4b8W62wvz9CQ== - dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - aelf-sdk "^3.2.44" - -"@portkey/extension-provider@0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.8.tgz#c311230f553b615378093933ee7925e1607ed169" - integrity sha512-93RqrJK2CyCKCwqlV+CFd9ixkyzGAl9yViTNoag/HjR2uNJd7dGo71HnJmDC/ijUokR+ZmnVtiJsNP+h2/DueQ== +"@portkey/extension-provider@0.0.1-alpha.10": + version "0.0.1-alpha.10" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.10.tgz#d15f8370802ba9f8cc00e02a11f566d889aa915d" + integrity sha512-xdHmmLfBccEYcNLaujQkWRJi+BLZMJeXpHBRwKXlKzNi1C7ioqyoeCZpR389CKQO99d34wht4lNjkmKOX6TBfg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/providers" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.10" + "@portkey/providers" "^0.0.1-alpha.10" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" @@ -5188,13 +5180,6 @@ dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-types@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.8.tgz#bb4996ec275d23fa1272b828b89f2043ec6a8337" - integrity sha512-qAELo9JKWCC7QWn/rCtwwxx8gokKv7ybeknn25EM5REZu80GK7Zg7KhRTgMlky+NyOQBNqyuc+1dl19S+RwaJA== - dependencies: - "@types/readable-stream" "^2.3.15" - "@portkey/provider-utils@0.0.1-alpha.10", "@portkey/provider-utils@^0.0.1-alpha.10": version "0.0.1-alpha.10" resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.10.tgz#9f3f9641c11ba1e390c97867a01e7df9393ac6ec" @@ -5202,14 +5187,7 @@ dependencies: "@portkey/provider-types" "^0.0.1-alpha.10" -"@portkey/provider-utils@0.0.1-alpha.8", "@portkey/provider-utils@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.8.tgz#f380927c43f4c718e6d7ede438cf6ad48ecaa5f8" - integrity sha512-p+coX6yBr7bEv7BSk4vKjaKIGftBHhEbWBcRMVb1NZ5b6UWwyiy3rfRgzwm4IRc5D/Qvqquyam/FLyHCfrgh6Q== - dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - -"@portkey/providers@0.0.1-alpha.10": +"@portkey/providers@0.0.1-alpha.10", "@portkey/providers@^0.0.1-alpha.10": version "0.0.1-alpha.10" resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.10.tgz#daed29a409b57838c44498d69f49cbdf820b9350" integrity sha512-srcqSS/iBivIoSkIZX13mvb1mukQMaBGE+QOpeMG0TSVkne1E49nHBxg0CVN5wi7qkTbE5/mbA5t83DypDh4kw== @@ -5222,19 +5200,6 @@ pump "^3.0.0" readable-stream "^4.4.0" -"@portkey/providers@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.8.tgz#c94228aee7f683d8297231f92dda1a28208e733c" - integrity sha512-L6wtRoURDngJfRj0ruz9TOGwj5TNP1prQTaEvY8WYdhnH/sAXef2U6uxLDPfBD03ot1kZ65d3/dg1nSc4HJP7w== - dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.8" - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/provider-utils" "^0.0.1-alpha.8" - "@types/readable-stream" "^2.3.15" - pump "^3.0.0" - readable-stream "^4.4.0" - "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From ab1b0b9100401a01cd4b1001b786055822884f42 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 2 Jun 2023 10:24:32 +0800 Subject: [PATCH 021/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20operator=20origin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts b/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts index bfd7da7206..d84f32ef33 100644 --- a/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts +++ b/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts @@ -34,7 +34,7 @@ describe('DappEventBus', () => { dappOverlay: new DappOverlay(), dappManager: new DappMobileManager({ store: store as any }), }); - console.log(`operator.origin: ${operator.origin}`); + console.log(`operator.origin: ${operator.dapp.origin}`); DappEventBus.dispatchEvent({ eventName: NotificationEvents.CONNECTED }); }); test('unregister successfully', done => { @@ -47,7 +47,7 @@ describe('DappEventBus', () => { dappOverlay: new DappOverlay(), dappManager: new DappMobileManager({ store: store as any }), }); - console.log(`operator.origin: ${operator.origin}`); + console.log(`operator.origin: ${operator.dapp.origin}`); operator.onDestroy(); DappEventBus.dispatchEvent({ eventName: NotificationEvents.CONNECTED }); }); From e4ede837021b7360e5036bb907280163c8b4bf51 Mon Sep 17 00:00:00 2001 From: "Carbon.lv" <130360750+carbon-portkey@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:03:03 +0800 Subject: [PATCH 022/893] feat: add basic UT config --- jest.config.js | 12 ++++++--- .../mobile-app-did/__tests__/App-test.tsx | 14 ----------- .../__tests__/dapp/dappEventBus.test.ts | 1 - .../__tests__/dapp/dappManager.test.ts | 25 +++++++++++++++++-- 4 files changed, 32 insertions(+), 20 deletions(-) delete mode 100644 packages/mobile-app-did/__tests__/App-test.tsx diff --git a/jest.config.js b/jest.config.js index 79630bceee..ad036e4eb2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -20,7 +20,7 @@ module.exports = { moduleNameMapper: { '\\.(css|less)$': 'identity-obj-proxy', }, - setupFilesAfterEnv: ['./packages/mobile-app-did/__tests__/setup/setupTests.js'], + setupFilesAfterEnv: ['./packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js'], projects: [ { displayName: 'hooks', @@ -69,11 +69,17 @@ module.exports = { }, { displayName: 'mobile-app-did', - rootDir: './packages/mobile-app-did', preset: 'ts-jest', + roots: ['/packages/mobile-app-did'], + transform: { + '^.+\\.(ts|tsx)$': [`ts-jest`, { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }], + }, moduleNameMapper: { '^react$': '/node_modules/react', - '^@portkey/providers$': '/node_modules/@portkey/providers', + '^utils/(.*)$': '/packages/mobile-app-did/js/utils/$1', + '^store/(.*)$': '/packages/mobile-app-did/js/store/$1', + store: '/packages/mobile-app-did/js/store/index.ts', + '^dapp/(.*)$': '/packages/mobile-app-did/js/dapp/$1', }, coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/Test/', '/utils/'], }, diff --git a/packages/mobile-app-did/__tests__/App-test.tsx b/packages/mobile-app-did/__tests__/App-test.tsx deleted file mode 100644 index 178476699b..0000000000 --- a/packages/mobile-app-did/__tests__/App-test.tsx +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @format - */ - -import 'react-native'; -import React from 'react'; -import App from '../App'; - -// Note: test renderer must be required after react-native. -import renderer from 'react-test-renderer'; - -it('renders correctly', () => { - renderer.create(); -}); diff --git a/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts b/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts index bfd7da7206..d5543b758a 100644 --- a/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts +++ b/packages/mobile-app-did/__tests__/dapp/dappEventBus.test.ts @@ -5,7 +5,6 @@ import { DappMobileManager } from 'dapp/dappManager'; import DappMobileOperator from 'dapp/dappMobileOperator'; import { DappOverlay } from 'dapp/dappOverlay'; import { store } from 'store'; - class TestOperatorStream extends DappInteractionStream { private messageCallback; constructor(callback: (message: IResponseType) => void) { diff --git a/packages/mobile-app-did/__tests__/dapp/dappManager.test.ts b/packages/mobile-app-did/__tests__/dapp/dappManager.test.ts index 6a5827f171..bba8bcfcde 100644 --- a/packages/mobile-app-did/__tests__/dapp/dappManager.test.ts +++ b/packages/mobile-app-did/__tests__/dapp/dappManager.test.ts @@ -1,11 +1,32 @@ import { DappManager } from '@portkey-wallet/utils/dapp/dappManager'; import { DappMobileManager } from 'dapp/dappManager'; +import { ChainsInfo } from '@portkey/provider-types'; +import { ChainId } from '@portkey-wallet/types'; import { store } from 'store'; -import {} from 'react-native'; describe('DappManager', () => { + let manager: DappManager; + const mockOrigin = 'you-know-who'; test('init well', () => { - const manager: DappManager = new DappMobileManager({ store: store as any }); + manager = new DappMobileManager({ store: store as any }); expect(manager).toBeTruthy(); }); + test('getState', () => { + expect(manager.getState()).toBeTruthy(); + }); + test('isLocked default:true', async () => { + expect(await manager.isLocked()).toBe(true); + }); + test('generate chainIds/chainsInfo/RpcUrl/Accounts successfully', async () => { + const chains: Array = await manager.chainId(); + expect(Array.isArray(chains) && chains[0]).toBeTruthy(); + const chainsInfo: ChainsInfo = await manager.chainsInfo(); + expect(chainsInfo.AELF || chainsInfo.tDVV || chainsInfo.tDVW).toBeTruthy(); + const rpcUrl: string | undefined = await manager.getRpcUrl(chains[0]); + expect(rpcUrl && rpcUrl?.length > 0).toBeTruthy(); + }); + test('unknown origin will be blocked', async () => { + const accounts = await manager.accounts(mockOrigin); + expect(accounts).toEqual({}); + }); }); From 01bed1dde27824d47e733d4e41e965815f84161e Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 2 Jun 2023 16:34:18 +0800 Subject: [PATCH 023/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Operator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/Test/Discover/index.tsx | 114 +++--------------- .../js/components/BrowserTab/index.tsx | 106 ++++++++++++++++ .../mobile-app-did/js/dapp/dappEventBus.ts | 26 ++-- .../js/dapp/dappMobileOperator.ts | 44 +++---- .../js/utils/EntryScriptWeb3.ts | 2 +- 5 files changed, 159 insertions(+), 133 deletions(-) create mode 100644 packages/mobile-app-did/js/components/BrowserTab/index.tsx diff --git a/packages/mobile-app-did/js/Test/Discover/index.tsx b/packages/mobile-app-did/js/Test/Discover/index.tsx index 4a29b69ba2..1a8e2394d3 100644 --- a/packages/mobile-app-did/js/Test/Discover/index.tsx +++ b/packages/mobile-app-did/js/Test/Discover/index.tsx @@ -1,25 +1,12 @@ -import React, { useCallback, useRef, useState } from 'react'; -import { StyleSheet } from 'react-native'; +import React from 'react'; import { defaultColors } from 'assets/theme'; -import WebView from 'react-native-webview'; import CustomHeader from 'components/CustomHeader'; import SafeAreaBox from 'components/SafeAreaBox'; -import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; -import useEffectOnce from 'hooks/useEffectOnce'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; -import { MobileStream } from 'dapp/mobileStream'; -import DappMobileOperator from 'dapp/dappMobileOperator'; -import { WebViewErrorEvent, WebViewNavigationEvent } from 'react-native-webview/lib/WebViewTypes'; -import URL from 'url-parse'; -import { store } from 'store'; -import { DappOverlay } from 'dapp/dappOverlay'; -import { DappMobileManager } from 'dapp/dappManager'; -import { useDapp } from '../../../../hooks/hooks-ca/dapp'; import CommonButton from 'components/CommonButton'; import DappEventBus from 'dapp/dappEventBus'; -import { useAppDispatch } from 'store/hooks'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import BrowserTab from 'components/BrowserTab'; const safeAreaColorMap = { white: defaultColors.bg1, @@ -32,82 +19,35 @@ export type SafeAreaColorMapKeyUnit = keyof typeof safeAreaColorMap; EntryScriptWeb3.init(); const Discover: React.FC = () => { - const webViewRef = useRef(null); - const operatorRef = useRef(null); - const [entryScriptWeb3, setEntryScriptWeb3] = useState(); - const dapp = useDapp(); - const dispatch = useAppDispatch(); - console.log(dapp, '=====dapp'); - useEffectOnce(() => { - const getEntryScriptWeb3 = async () => { - const script = await EntryScriptWeb3.get(); - setEntryScriptWeb3(script); - }; - - getEntryScriptWeb3(); - return () => { - operatorRef?.current?.onDestroy(); - }; - }); - - const initOperator = useCallback((origin: string) => { - operatorRef.current = new DappMobileOperator({ - origin, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - stream: new MobileStream(webViewRef.current!), - dappManager: new DappMobileManager({ store: store as any }), - dappOverlay: new DappOverlay(), - }); - }, []); - - const onLoadStart = useCallback( - ({ nativeEvent }: WebViewNavigationEvent) => { - const { origin } = new URL(nativeEvent.url); - initOperator(origin); - }, - [initOperator], - ); - const handleUpdate = useCallback(({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { - const { origin, pathname = '', query = '' } = new URL(nativeEvent.url); - const realUrl = `${origin}${pathname}${query}`; - const icon = `https://api.faviconkit.com/${getHost(realUrl)}/50`; - operatorRef.current?.updateDappInfo({ - origin, - name: nativeEvent.title, - icon, - }); - }, []); return ( - { - operatorRef.current?.handleRequestMessage(nativeEvent.data); - }} - onLoadStart={onLoadStart} - onLoad={handleUpdate} - onLoadProgress={({ nativeEvent }) => { - console.log(nativeEvent.progress, '=onLoadProgress'); + + + { + DappEventBus.dispatchEvent({ + eventName: 'accountsChanged', + data: { + AELF: ['iC1BZJsrn9jEYJ4ABgDnpqaYYbu7JB3fZuBJS5xAoZJB3yBVU'], + tDVW: ['2BhwLPoSj2z3GSTrBphmqJJ9yq3hdxtLaDQgyoJnJBHvB9cpw1'], + }, + }); }} - onLoadEnd={handleUpdate} - applicationNameForUserAgent={'WebView Portkey did Mobile'} + title="accountsChanged" /> { DappEventBus.dispatchEvent({ eventName: 'accountsChanged', + origin: 'http://localhost:3001', data: { AELF: ['iC1BZJsrn9jEYJ4ABgDnpqaYYbu7JB3fZuBJS5xAoZJB3yBVU'], tDVW: ['2BhwLPoSj2z3GSTrBphmqJJ9yq3hdxtLaDQgyoJnJBHvB9cpw1'], }, }); }} - title="accountsChanged" + title="accountsChanged origin" /> { @@ -116,30 +56,10 @@ const Discover: React.FC = () => { data: ['AELF', 'tDVV'], }); }} - title="accountsChanged2" + title="chainChanged" /> ); }; export default Discover; - -export const styles = StyleSheet.create({ - pageWrap: { - paddingLeft: 0, - paddingRight: 0, - backgroundColor: defaultColors.bg1, - }, - svgWrap: { - marginRight: pTd(16), - }, - webViewContainer: { - flex: 1, - backgroundColor: 'red', - }, - webView: { - width: '100%', - flex: 1, - }, - noResult: {}, -}); diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx new file mode 100644 index 0000000000..a7f4af4205 --- /dev/null +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -0,0 +1,106 @@ +import React, { useCallback, useRef, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import WebView from 'react-native-webview'; +import { pTd } from 'utils/unit'; +import useEffectOnce from 'hooks/useEffectOnce'; +import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; +import { MobileStream } from 'dapp/mobileStream'; +import DappMobileOperator from 'dapp/dappMobileOperator'; +import { WebViewErrorEvent, WebViewNavigationEvent } from 'react-native-webview/lib/WebViewTypes'; +import URL from 'url-parse'; +import { store } from 'store'; +import { DappOverlay } from 'dapp/dappOverlay'; +import { DappMobileManager } from 'dapp/dappManager'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; + +type BrowserTabProps = { + uri: string; +}; + +const BrowserTab: React.FC = ({ uri }) => { + const webViewRef = useRef(null); + const operatorRef = useRef(null); + const [entryScriptWeb3, setEntryScriptWeb3] = useState(); + useEffectOnce(() => { + const getEntryScriptWeb3 = async () => { + const script = await EntryScriptWeb3.get(); + setEntryScriptWeb3(script); + }; + + getEntryScriptWeb3(); + return () => { + operatorRef?.current?.onDestroy(); + }; + }); + + const initOperator = useCallback((origin: string) => { + operatorRef.current = new DappMobileOperator({ + origin, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stream: new MobileStream(webViewRef.current!), + dappManager: new DappMobileManager({ store: store as any }), + dappOverlay: new DappOverlay(), + }); + }, []); + + const onLoadStart = useCallback( + ({ nativeEvent }: WebViewNavigationEvent) => { + const { origin } = new URL(nativeEvent.url); + initOperator(origin); + }, + [initOperator], + ); + const handleUpdate = useCallback(({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { + const { origin, pathname = '', query = '' } = new URL(nativeEvent.url); + const realUrl = `${origin}${pathname}${query}`; + const icon = `https://api.faviconkit.com/${getHost(realUrl)}/50`; + operatorRef.current?.updateDappInfo({ + origin, + name: nativeEvent.title, + icon, + }); + }, []); + + return ( + { + operatorRef.current?.handleRequestMessage(nativeEvent.data); + }} + onLoadStart={onLoadStart} + onLoad={handleUpdate} + onLoadProgress={({ nativeEvent }) => { + console.log(nativeEvent.progress, '=onLoadProgress'); + }} + onLoadEnd={handleUpdate} + applicationNameForUserAgent={'WebView Portkey did Mobile'} + /> + ); +}; + +export default BrowserTab; + +export const styles = StyleSheet.create({ + pageWrap: { + paddingLeft: 0, + paddingRight: 0, + backgroundColor: defaultColors.bg1, + }, + svgWrap: { + marginRight: pTd(16), + }, + webViewContainer: { + flex: 1, + backgroundColor: 'red', + }, + webView: { + width: '100%', + flex: 1, + }, + noResult: {}, +}); diff --git a/packages/mobile-app-did/js/dapp/dappEventBus.ts b/packages/mobile-app-did/js/dapp/dappEventBus.ts index 3841a98cb9..cc14eb9db0 100644 --- a/packages/mobile-app-did/js/dapp/dappEventBus.ts +++ b/packages/mobile-app-did/js/dapp/dappEventBus.ts @@ -1,6 +1,5 @@ import { NetworkType } from '@portkey-wallet/types'; import { - IOperator, DappEvents, ResponseCode, IResponseType, @@ -9,14 +8,23 @@ import { ConnectInfo, ProviderErrorType, } from '@portkey/provider-types'; +import DappMobileOperator from './dappMobileOperator'; + +export interface DappEventPack { + eventName: T; + data?: D; + callback?: () => void; + origin?: string; + msg?: string; +} export default class DappEventBus { - private static operators: Array = []; - public static registerOperator(operator: IOperator) { + private static operators: Array = []; + public static registerOperator(operator: DappMobileOperator) { if (this.operators.includes(operator)) return; this.operators.push(operator); } - public static unregisterOperator(operator: IOperator) { + public static unregisterOperator(operator: DappMobileOperator) { this.operators = this.operators.filter(item => item !== operator); } public static dispatchEvent(params: DappEventPack): void; @@ -26,8 +34,6 @@ export default class DappEventBus { public static dispatchEvent(params: DappEventPack<'connected', ConnectInfo>): void; public static dispatchEvent(params: DappEventPack<'disconnected', ProviderErrorType>): void; public static dispatchEvent({ eventName, data, callback, origin, msg }: DappEventPack) { - console.log(eventName, data, this.operators, '==this.operators'); - const event: IResponseType = { eventName, info: { @@ -38,15 +44,9 @@ export default class DappEventBus { origin, }; this.operators.forEach(operator => { + if (origin && origin !== operator.dapp.origin) return; operator?.publishEvent?.(event as any); }); callback?.(); } } -export interface DappEventPack { - eventName: T; - data?: D; - callback?: () => void; - origin?: string; - msg?: string; -} diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 4d86e3a840..520a23b061 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -54,7 +54,7 @@ export default class DappMobileOperator extends Operator { DappEventBus.unregisterOperator(this); }; - userConfirmation = async ({ + protected userConfirmation = async ({ eventName, params, method, @@ -67,7 +67,7 @@ export default class DappMobileOperator extends Operator { if (!authorized) return this.userDenied(eventName); }; - handleViewRequest = async (request: IRequestParams): Promise => { + protected handleViewRequest = async (request: IRequestParams): Promise => { const { eventName, method } = request; switch (method) { case MethodsBase.ACCOUNTS: { @@ -106,14 +106,14 @@ export default class DappMobileOperator extends Operator { }); }; - handleRequestAccounts: SendRequest = async (eventName, params) => { + protected handleRequestAccounts: SendRequest = async (eventName, params) => { await this.dappManager.addDapp(params); return generateNormalResponse({ eventName, data: await this.dappManager.accounts(params.origin!), }); }; - handleSendTransaction: SendRequest = async (eventName, params) => { + protected handleSendTransaction: SendRequest = async (eventName, params) => { try { if (!params.params) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); @@ -127,18 +127,18 @@ export default class DappMobileOperator extends Operator { const isForward = chainInfo.caContractAddress !== params.contractAddress; - let paramsOption = (params.params as { paramsOption: object }).paramsOption; - - const functionName = isForward ? 'ManagerForwardCall' : params.method; - - paramsOption = isForward - ? { - caHash: caInfo.caHash, - methodName: params.method, - contractAddress: params.contractAddress, - args: paramsOption, - } - : paramsOption; + let paramsOption = (params.params as { paramsOption: object }).paramsOption, + functionName = params.method; + + if (isForward) { + paramsOption = { + caHash: caInfo.caHash, + methodName: params.method, + contractAddress: params.contractAddress, + args: paramsOption, + }; + functionName = 'ManagerForwardCall'; + } const data = await contract!.callSendMethod(functionName, '', paramsOption, { onMethod: 'transactionHash', @@ -151,20 +151,20 @@ export default class DappMobileOperator extends Operator { } else { return generateErrorResponse({ eventName, - code: 4007, + code: ResponseCode.CONTRACT_ERROR, msg: handleErrorMessage(data.error), }); } } catch (error) { return generateErrorResponse({ eventName, - code: 4007, + code: ResponseCode.CONTRACT_ERROR, msg: handleErrorMessage(error), }); } }; - async sendRequest({ + protected async sendRequest({ eventName, params, method, @@ -181,7 +181,7 @@ export default class DappMobileOperator extends Operator { return callBack(eventName, params); } - handleSendRequest = async (request: IRequestParams): Promise => { + protected handleSendRequest = async (request: IRequestParams): Promise => { const { method, eventName, origin } = request; if (this.dapp.origin !== origin) return generateErrorResponse({ @@ -226,13 +226,13 @@ export default class DappMobileOperator extends Operator { return this.handleViewRequest(request); }; - userDenied(eventName: string) { + protected userDenied(eventName: string) { return generateErrorResponse({ eventName, code: ResponseCode.USER_DENIED, }); } - unauthenticated(eventName: string) { + protected unauthenticated(eventName: string) { return generateErrorResponse({ eventName, code: ResponseCode.UNAUTHENTICATED, diff --git a/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts b/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts index 8d50a69412..76f4dbe235 100644 --- a/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts +++ b/packages/mobile-app-did/js/utils/EntryScriptWeb3.ts @@ -18,7 +18,7 @@ const EntryScriptWeb3 = { if (this.entryScriptWeb3) return this.entryScriptWeb3; // If for some reason it is not available, get it again - return await this.init(); + return this.init(); }, }; From 3a181c3cf40683ea6a0710e260176765830a4392 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 2 Jun 2023 16:43:14 +0800 Subject: [PATCH 024/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20connected=20stat?= =?UTF-8?q?us=20&=20my-connectedSites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/Popup/routes/index.tsx | 5 ++ .../app/web/Prompt/routes/index.tsx | 9 +++ .../app/web/assets/theme/color.less | 3 + .../app/web/hooks/useConnect.ts | 31 ++++++++++ .../app/web/models/AccountConnect/index.less | 2 +- .../Home/components/MyBalance/index.less | 6 ++ .../pages/Home/components/MyBalance/index.tsx | 6 +- .../ConnectedSites/Popup/index.less | 12 ++++ .../ConnectedSites/Popup/index.tsx | 25 ++++++++ .../ConnectedSites/Prompt/index.less | 8 +++ .../ConnectedSites/Prompt/index.tsx | 22 +++++++ .../WalletSecurity/ConnectedSites/index.tsx | 37 +++++++++++ .../web/pages/WalletSecurity/Popup/index.less | 3 +- .../pages/WalletSecurity/Prompt/index.less | 3 +- .../components/ConnectedSiteList/index.less | 58 +++++++++++++++++ .../components/ConnectedSiteList/index.tsx | 44 +++++++++++++ .../app/web/pages/WalletSecurity/index.tsx | 19 +++++- .../components/AccountConnect/index.less | 38 ++++++++++++ .../pages/components/AccountConnect/index.tsx | 32 ++++++++++ .../components/AccountConnectModal/index.less | 61 ++++++++++++++++++ .../components/AccountConnectModal/index.tsx | 62 +++++++++++++++++++ .../app/web/store/Provider/hooks.ts | 1 + 22 files changed, 481 insertions(+), 6 deletions(-) create mode 100644 packages/web-extension-did/app/web/hooks/useConnect.ts create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.less create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.less create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/components/AccountConnect/index.less create mode 100644 packages/web-extension-did/app/web/pages/components/AccountConnect/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less create mode 100644 packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx diff --git a/packages/web-extension-did/app/web/Popup/routes/index.tsx b/packages/web-extension-did/app/web/Popup/routes/index.tsx index 7ffd71a245..dc5e8fb8d6 100644 --- a/packages/web-extension-did/app/web/Popup/routes/index.tsx +++ b/packages/web-extension-did/app/web/Popup/routes/index.tsx @@ -33,6 +33,7 @@ import AutoLock from 'pages/Wallet/AutoLock'; import SwitchNetworks from 'pages/Wallet/SwitchNetwork'; import WalletName from 'pages/Wallet/WalletName'; import RecentDetail from 'pages/Send/components/RecentDetail'; +import ConnectedSites from 'pages/WalletSecurity/ConnectedSites'; export const PageRouter = () => useRoutes([ @@ -176,6 +177,10 @@ export const PageRouter = () => path: '/setting/wallet-security/manage-devices/guardian-approval', element: , }, + { + path: '/setting/wallet-security/connected-sites', + element: , + }, { path: '/unlock', element: , diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index f2886d0d78..171153599d 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -48,6 +48,7 @@ import My from 'pages/My'; import RecentDetail from 'pages/Send/components/RecentDetail'; import Permission from 'pages/Permission'; import ConnectWallet from 'pages/ConnectWallet'; +import ConnectedSites from 'pages/WalletSecurity/ConnectedSites'; export const PageRouter = () => { const { isNotLessThan768 } = useCommonState(); @@ -277,6 +278,10 @@ export const PageRouter = () => { path: '/setting/wallet-security/manage-devices/guardian-approval', element: , }, + { + path: '/setting/wallet-security/connected-sites', + element: , + }, ], }, ], @@ -371,6 +376,10 @@ export const PageRouter = () => { path: '/setting/wallet-security/manage-devices/guardian-approval', element: , }, + { + path: '/setting/wallet-security/connected-sites', + element: , + }, ]; const promptRoutes = useRoutes([...commonRoutes, ...settingPromptRoutes]); diff --git a/packages/web-extension-did/app/web/assets/theme/color.less b/packages/web-extension-did/app/web/assets/theme/color.less index 3464fb263d..da524a0a3b 100644 --- a/packages/web-extension-did/app/web/assets/theme/color.less +++ b/packages/web-extension-did/app/web/assets/theme/color.less @@ -32,6 +32,8 @@ @border-8: #515a62; @border-9: #00b9b0; @border-10: #f0f2f5; +@border-11:#00AB34; + @bg-1: #52c41a; @bg-2: #edf9e8; @@ -53,6 +55,7 @@ @bg-18: #c5cbd5; @bg-19: #515a62; @bg-20: #f2f4f6; +@bg-21: #00AB34; @hover1: @border-4; @hover2: @border-3; diff --git a/packages/web-extension-did/app/web/hooks/useConnect.ts b/packages/web-extension-did/app/web/hooks/useConnect.ts new file mode 100644 index 0000000000..63354554c8 --- /dev/null +++ b/packages/web-extension-did/app/web/hooks/useConnect.ts @@ -0,0 +1,31 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { getCurrentTab } from 'utils/platforms'; +import { useDapp, useWalletInfo } from 'store/Provider/hooks'; + +export default function useConnect() { + const [currentTab, setCurrentTab] = useState(); + const { currentNetwork } = useWalletInfo(); + const { dappMap } = useDapp(); + const currentDapps = useMemo(() => dappMap[currentNetwork] ?? [], [dappMap, currentNetwork]); + + const getCurrentTabPermission = useCallback(async () => { + const tab = await getCurrentTab(); + setCurrentTab(tab); + }, []); + + const connectedSite = useMemo(() => { + const url = currentTab?.url; + if (!url) return; + const origin = new URL(url).origin; + return origin ? currentDapps.find((dapp) => dapp.origin === origin) : undefined; + }, [currentDapps, currentTab?.url]); + + useEffect(() => { + getCurrentTabPermission(); + }, [getCurrentTabPermission]); + + return { + dapp: connectedSite, + origin: currentTab?.url, + }; +} diff --git a/packages/web-extension-did/app/web/models/AccountConnect/index.less b/packages/web-extension-did/app/web/models/AccountConnect/index.less index a4c762b84b..61c4234d4b 100644 --- a/packages/web-extension-did/app/web/models/AccountConnect/index.less +++ b/packages/web-extension-did/app/web/models/AccountConnect/index.less @@ -1,5 +1,5 @@ @import '../../assets/theme/color.less'; -.account-connect { +.account-connect-modal { .@{app-prefix}-modal-header { padding: 0; .@{app-prefix}-modal-title { diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less index bf6deee0a0..f3e161bdac 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less @@ -4,6 +4,7 @@ overflow-y: overlay; .wallet-name { + position: relative; line-height: 22px; color: @font-11; font-size: 16px; @@ -11,6 +12,11 @@ font-weight: 500; padding: 21px 24px; border-bottom: 1px solid @border-2; + .account-connect { + position: absolute; + top: 16px; + left: 8px; + } } .balance-amount { display: flex; diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index 32b733a7f6..8f772ba745 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -29,6 +29,7 @@ import { FaucetUrl } from '@portkey-wallet/constants/constants-ca/payment'; import { BalanceTab } from '@portkey-wallet/constants/constants-ca/assets'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import AccountConnect from 'pages/components/AccountConnect'; import './index.less'; export interface TransactionResult { @@ -167,7 +168,10 @@ export default function MyBalance() { return (
    -
    {walletName}
    +
    + {!isPrompt && } + {walletName} +
    {isMainNet ? ( {`$ ${accountBalanceUSD}`} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.less new file mode 100644 index 0000000000..2198361ee6 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.less @@ -0,0 +1,12 @@ +@import '../../../../assets/theme/color.less'; + +.connected-sites-popup { + .connected-sites-header { + overflow: hidden; + width: 100%; + padding-top: 16px; + background-color: @bg-13; + border-bottom: 1px solid @border-2; + z-index: 10; + } +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx new file mode 100644 index 0000000000..479ab75da0 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx @@ -0,0 +1,25 @@ +import BackHeader from 'components/BackHeader'; +import CustomSvg from 'components/CustomSvg'; +import { BaseHeaderProps } from 'types/UI'; +import ConnectedSiteList, { IConnectedSiteListProps } from 'pages/WalletSecurity/components/ConnectedSiteList'; +import './index.less'; + +export default function ConnectedSitesPopup({ + headerTitle, + goBack, + list, + onDisconnect, +}: BaseHeaderProps & IConnectedSiteListProps) { + return ( +
    +
    + } + /> +
    + +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.less new file mode 100644 index 0000000000..7f5d62f4a7 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.less @@ -0,0 +1,8 @@ +@import '.../../../../../../assets/theme/color.less'; + +.connected-sites-prompt { + width: 100%; + flex: 1; + border-left: 1px solid @border-2; + overflow-y: auto; +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx new file mode 100644 index 0000000000..834bdd6c1f --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx @@ -0,0 +1,22 @@ +import SecondPageHeader from 'pages/components/SecondPageHeader'; +import ConnectedSiteList, { IConnectedSiteListProps } from 'pages/WalletSecurity/components/ConnectedSiteList'; +import { Outlet } from 'react-router'; +import { BaseHeaderProps } from 'types/UI'; +import './index.less'; + +export default function ConnectedSitesPrompt({ + headerTitle, + goBack, + list, + onDisconnect, +}: BaseHeaderProps & IConnectedSiteListProps) { + return ( +
    +
    + + +
    + +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx new file mode 100644 index 0000000000..e91e6a8bb3 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx @@ -0,0 +1,37 @@ +import { useTranslation } from 'react-i18next'; +import { useCallback, useMemo } from 'react'; +import { useNavigate } from 'react-router'; +import { useAppDispatch, useDapp, useWalletInfo } from 'store/Provider/hooks'; +import SitesPopup from './Popup'; +import SitesPrompt from './Prompt'; +import { useCommonState } from 'store/Provider/hooks'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; + +export default function ConnectedSites() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const { currentNetwork } = useWalletInfo(); + const { dappMap } = useDapp(); + const currentDapp = useMemo(() => dappMap[currentNetwork] || [], [currentNetwork, dappMap]); + const { isNotLessThan768 } = useCommonState(); + const dispatch = useAppDispatch(); + + const handleDisConnect = useCallback( + (item: DappStoreItem) => { + dispatch(removeDapp({ networkType: currentNetwork, origin: item.origin || '' })); + }, + [currentNetwork, dispatch], + ); + + const title = t('Connected Sites'); + const handleBack = useCallback(() => { + navigate('/setting/wallet-security'); + }, [navigate]); + + return isNotLessThan768 ? ( + + ) : ( + + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/Popup/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/Popup/index.less index 0027253a07..f3bc5a6563 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/Popup/index.less +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/Popup/index.less @@ -13,7 +13,8 @@ font-size: 16px; line-height: 22px; color: @font-11; - .manage-device { + .manage-device, + .connected-sites { justify-content: space-between; padding-right: 4px; .number { diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/Prompt/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/Prompt/index.less index baad103804..ca762ff603 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/Prompt/index.less +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/Prompt/index.less @@ -15,7 +15,8 @@ font-size: 16px; line-height: 22px; color: @font-11; - .manage-device { + .manage-device, + .connected-sites { justify-content: space-between; padding-right: 4px; .number { diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less new file mode 100644 index 0000000000..530016277a --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less @@ -0,0 +1,58 @@ +@import '../../../../assets/theme/color.less'; + +.connected-site-list { + .connected-site-item { + height: 92px; + padding: 24px; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid @bg-15; + color: black; + .content { + align-items: center; + .icon { + flex-shrink: 0; + width: 32px; + border-radius: 50%; + } + .desc { + margin-left: 16px; + .text { + max-width: 169px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .name { + font-size: 16px; + line-height: 22px; + color: @font-11; + } + .origin { + margin-top: 2px; + font-size: 14px; + line-height: 20px; + color: @font-12; + } + } + } + .@{app-prefix}-btn-text { + width: 79px; + height: 24px; + font-size: 12px; + line-height: 16px; + color: @font-14; + background-color: @bg-11; + border-radius: 24px; + border: 1px solid @border-7; + text-shadow: none; + } + } +} + +.no-data { + margin-top: 160px; + font-size: 14px; + line-height: 20px; + color: @font-12; +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx new file mode 100644 index 0000000000..b923e70cae --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx @@ -0,0 +1,44 @@ +import { useTranslation } from 'react-i18next'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { Button } from 'antd'; +import { useMemo } from 'react'; +import ImgLoading from 'components/ImgLoading'; +import './index.less'; + +export interface IConnectedSiteListProps { + list: DappStoreItem[]; + onDisconnect: (item: DappStoreItem) => void; +} + +export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSiteListProps) { + const { t } = useTranslation(); + + const renderList = useMemo( + () => ( +
    + {list.map((item) => ( +
    +
    + +
    +
    {item.name}
    +
    {item.origin}
    +
    +
    + +
    + ))} +
    + ), + [list, onDisconnect, t], + ); + + return list.length === 0 ?
    {t('No Connected Sites')}
    : renderList; +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx index ea8f7a5306..47669c0401 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx @@ -6,7 +6,7 @@ import { MenuItemInfo } from 'pages/components/MenuList'; import { BaseHeaderProps } from 'types/UI'; import WalletSecurityPrompt from './Prompt'; import WalletSecurityPopup from './Popup'; -import { useCommonState } from 'store/Provider/hooks'; +import { useCommonState, useDapp, useWalletInfo } from 'store/Provider/hooks'; export interface IWalletSecurityProps extends BaseHeaderProps { menuList: MenuItemInfo[]; @@ -17,6 +17,9 @@ export default function WalletSecurity() { const navigate = useNavigate(); const { isNotLessThan768 } = useCommonState(); const { deviceAmount } = useDeviceList(); + const { currentNetwork } = useWalletInfo(); + const { dappMap } = useDapp(); + const currentDapp = useMemo(() => dappMap[currentNetwork] || [], [currentNetwork, dappMap]); const MenuListData: MenuItemInfo[] = useMemo( () => [ @@ -32,8 +35,20 @@ export default function WalletSecurity() { navigate('/setting/wallet-security/manage-devices'); }, }, + { + key: t('Connected Sites'), + element: ( +
    + {t('Connected Sites')} + {currentDapp.length} +
    + ), + click: () => { + navigate('/setting/wallet-security/connected-sites'); + }, + }, ], - [deviceAmount, navigate, t], + [currentDapp?.length, deviceAmount, navigate, t], ); const title = t('Wallet Security'); diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less b/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less new file mode 100644 index 0000000000..1ba8106930 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less @@ -0,0 +1,38 @@ +@import '../../../assets/theme/color.less'; + +.account-connect { + padding: 8px; + max-width: fit-content; + font-size: 12px; + line-height: 16px; + color: @font-13; + border-radius: 20px; + cursor: pointer; + &:hover { + background-color: @bg-13; + } + .status { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + margin-right: 6px; + border: 1px solid @border-8; + &.connected { + position: relative; + border-color: @border-11; + background-color: @bg-11; + &::after { + position: absolute; + top: 1px; + left: 1px; + content: ''; + display: inline-block; + width: 4px; + height: 4px; + border-radius: 50%; + background-color: @bg-21; + } + } + } +} diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnect/index.tsx b/packages/web-extension-did/app/web/pages/components/AccountConnect/index.tsx new file mode 100644 index 0000000000..22136eb0d6 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/AccountConnect/index.tsx @@ -0,0 +1,32 @@ +import { useCallback, useMemo, useState } from 'react'; +import clsx from 'clsx'; +import { useTranslation } from 'react-i18next'; +import AccountConnectModal from '../AccountConnectModal'; +import useConnect from 'hooks/useConnect'; +import './index.less'; + +export default function AccountConnect() { + const { t } = useTranslation(); + const { dapp } = useConnect(); + const [open, setOpen] = useState(false); + + const onClick = useCallback(() => { + setOpen(true); + }, []); + + const onCancel = useCallback(() => { + setOpen(false); + }, []); + + const modalProps = useMemo(() => ({ onCancel, open }), [onCancel, open]); + + return ( + <> +
    + + {t(dapp ? 'Connected' : 'Not Connected')} +
    + + + ); +} diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less new file mode 100644 index 0000000000..f7ec20d210 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less @@ -0,0 +1,61 @@ +@import '../../../assets/theme/color.less'; + +.account-connect-modal { + .@{app-prefix}-modal-header { + padding: 0; + .@{app-prefix}-modal-title { + padding: 24px; + font-size: 16px; + line-height: 22px; + font-weight: 500px; + color: @font-11; + } + } + .@{app-prefix}-modal-body { + padding: 0 24px 24px; + display: flex; + flex-direction: column; + justify-content: center; + .close-icon { + width: 12px; + height: 12px; + position: absolute; + top: 16px; + right: 16px; + .close2-icon { + cursor: pointer; + width: 100%; + height: 100%; + } + } + .origin { + padding-bottom: 8px; + font-size: 14px; + line-height: 20px; + color: @font-1; + } + .connect-tip { + font-size: 12px; + line-height: 16px; + color: @font-13; + } + .not-connected { + text-align: center; + } + .connected .origin { + width: 177px; + padding-bottom: 0; + } + .@{app-prefix}-btn-text { + width: 79px; + height: 24px; + font-size: 12px; + line-height: 16px; + color: @font-14; + background-color: @bg-11; + border-radius: 24px; + border: 1px solid @border-7; + text-shadow: none; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx new file mode 100644 index 0000000000..3656f38dd3 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx @@ -0,0 +1,62 @@ +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { Button } from 'antd'; +import CommonModal from 'components/CommonModal'; +import CustomSvg from 'components/CustomSvg'; +import useConnect from 'hooks/useConnect'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; +import './index.less'; + +export interface IAccountConnectModalProps { + open: boolean; + onCancel: () => void; +} + +export default function AccountConnectModal({ open, onCancel }: IAccountConnectModalProps) { + const { t } = useTranslation(); + const { dapp, origin } = useConnect(); + const { currentNetwork } = useWalletInfo(); + const dispatch = useAppDispatch(); + + const handleDisConnect = useCallback(() => { + if (!dapp) return; + dispatch(removeDapp({ networkType: currentNetwork, origin: dapp.origin || '' })); + onCancel(); + }, [currentNetwork, dapp, dispatch, onCancel]); + + const renderBody = useMemo( + () => + dapp ? ( +
    +
    {dapp.origin}
    + +
    + ) : ( +
    +
    {origin}
    +
    {t('To connect, locate the connect button on their site.')}
    +
    + ), + [dapp, handleDisConnect, origin, t], + ); + + return ( + + {renderBody} +
    + +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/store/Provider/hooks.ts b/packages/web-extension-did/app/web/store/Provider/hooks.ts index f00a45696c..2f76ef1902 100644 --- a/packages/web-extension-did/app/web/store/Provider/hooks.ts +++ b/packages/web-extension-did/app/web/store/Provider/hooks.ts @@ -21,6 +21,7 @@ export const useCustomModal = () => useAppSelector((state) => state.modal); export const useMiscState = () => useAppSelector((state) => state.misc); export const useCommonState = () => useAppSelector((state) => state.common); export const usePayment = () => useAppSelector((state) => state.payment); +export const useDapp = () => useAppSelector((state) => state.dapp); export const useLoading = () => { const { loadingInfo } = useAppSelector((state) => state.userInfo); const dispatch = useAppDispatch(); From 01d9bf893009c7724ae3300c7d29af7f14057b73 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 2 Jun 2023 18:20:13 +0800 Subject: [PATCH 025/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 7 ++++--- .../ViewOnWebView/hooks/useHandleAchSell.ts | 5 +++-- .../js/pages/Activity/ViewOnWebView/index.tsx | 2 +- .../Buy/BuyHome/components/SellForm/index.tsx | 1 + .../js/pages/Buy/BuyPreview/index.tsx | 7 +++---- packages/mobile-app-did/js/pages/Buy/hooks.tsx | 14 ++++++++++++-- packages/socket/socket-sell/index.ts | 9 ++++++++- 7 files changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 4568dc543c..d0783aa748 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -44,16 +44,17 @@ export const useSellTransfer = () => { if (!isMainnet || merchantName !== ACH_MERCHANT_NAME) return; const clientId = randomId(); - signalrSell.doOpen({ + await signalrSell.doOpen({ url: `${request.defaultConfig.baseURL}/ca`, clientId, }); - await signalrSell.requestAchTxAddress(clientId, orderId); - const achTxAddressReceived = await new Promise(resolve => () => { + + const achTxAddressReceived = await new Promise(resolve => { const { remove } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, data => { resolve(data); remove(); }); + signalrSell.requestAchTxAddress(clientId, orderId); }); const result = await paymentSellTransfer(achTxAddressReceived); if (result.error) { diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index cb9242c420..09c7ab7742 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -55,10 +55,10 @@ export const useHandleAchSell = () => { const amount = timesDecimals(params.cryptoAmount, decimals).toNumber(); return await sameChainTransfer({ contract, - tokenInfo: aelfToken, + tokenInfo: { ...aelfToken, address: aelfToken.tokenContractAddress || '' }, caHash: caHash, amount, - toAddress: params.address, + toAddress: `ELF_${params.address}_AELF`, }); }, [aelfToken, chainInfo, pin, wallet], @@ -74,6 +74,7 @@ export const useHandleAchSell = () => { paymentSellTransfer, }); } catch (error) { + console.log('error', error); CommonToast.fail('Transfer Error'); } finally { Loading.hide(); diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx index 94b55ffb09..0dae9fd212 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx @@ -74,7 +74,7 @@ const ViewOnWebView: React.FC = () => { navigationService.navigate('Tab'); const { orderNo } = (params as AchSellParams) || {}; if (!orderNo) { - // TODO: + // TODO: add Toast return; } diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index 7a55004cdc..6b500e7ffb 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -177,6 +177,7 @@ export default function SellForm() { const balance = await getELFChainBalance(tokenContract, symbol, wallet?.[chainId]?.caAddress || ''); if (divDecimals(balance, decimals).isLessThan(amount)) { + // TODO: add Toast return; } diff --git a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx index 2d5ad8c2e1..e5a29e9267 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx @@ -59,7 +59,6 @@ export default function BuyPreview() { async (isNoEmail = false) => { const appId = buyConfig?.ach?.appId; const baseUrl = buyConfig?.ach?.baseUrl; - if (!amount || !receiveAmount || !fiat || !token || !appId || !baseUrl) return; Loading.show(); try { @@ -121,10 +120,10 @@ export default function BuyPreview() { injectedJavaScript, params: type === TypeEnum.BUY - ? { + ? undefined + : { orderNo, - } - : undefined, + }, }); } catch (error) { CommonToast.fail(`There is a network error, please try again.`); diff --git a/packages/mobile-app-did/js/pages/Buy/hooks.tsx b/packages/mobile-app-did/js/pages/Buy/hooks.tsx index 4f151de6c6..0f8ca198ce 100644 --- a/packages/mobile-app-did/js/pages/Buy/hooks.tsx +++ b/packages/mobile-app-did/js/pages/Buy/hooks.tsx @@ -10,6 +10,7 @@ import { ErrorType } from 'types/common'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import isEqual from 'lodash/isEqual'; import { useIsFocused } from '@react-navigation/native'; +import { ZERO } from '@portkey-wallet/constants/misc'; export const useReceive = ( type: TypeEnum, @@ -82,7 +83,9 @@ export const useReceive = ( if (amountNum < min || amountNum > max) { setAmountError({ ...INIT_HAS_ERROR, - errorMsg: `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${fiat?.currency}`, + errorMsg: `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${ + type === TypeEnum.BUY ? fiat?.currency : token.crypto + }`, }); setRate(''); setReceiveAmount(''); @@ -123,7 +126,14 @@ export const useReceive = ( if (isRefreshReceiveValid) isRefreshReceiveValid.current = true; const _rate = Number(rst.cryptoPrice).toFixed(2) + ''; - const _receiveAmount = formatAmountShow((type === TypeEnum.BUY ? rst.cryptoQuantity : rst.fiatQuantity) || '', 4); + let _receiveAmount = ''; + if (type === TypeEnum.BUY) { + _receiveAmount = formatAmountShow(rst.cryptoQuantity || '', 4); + } else { + const fiatQuantity = ZERO.plus(rst.fiatQuantity || 0).minus(rst.rampFee || 0); + _receiveAmount = formatAmountShow(fiatQuantity.valueOf(), 4); + } + setRate(_rate); setReceiveAmount(_receiveAmount); return { diff --git a/packages/socket/socket-sell/index.ts b/packages/socket/socket-sell/index.ts index f67c19d851..6f942f7198 100644 --- a/packages/socket/socket-sell/index.ts +++ b/packages/socket/socket-sell/index.ts @@ -4,7 +4,11 @@ import { AchTxAddressReceivedType } from '@portkey-wallet/types/types-ca/payment export class SignalrSell extends Signalr { public requestAchTxAddress(clientId: string, orderId: string) { - this.invoke('RequestAchTxAddress', clientId, orderId); + console.log('invoke RequestAchTxAddress'); + this.invoke('RequestAchTxAddress', { + TargetClientId: clientId, + OrderId: orderId, + }); } public onAchTxAddressReceived( @@ -12,8 +16,11 @@ export class SignalrSell extends Signalr { callback: (data: AchTxAddressReceivedType) => void, ) { return this.listen('onAchTxAddressReceived', (data: { body: AchTxAddressReceivedType }) => { + console.log('onAchTxAddressReceived: data', data); if (data?.body?.orderId === orderId) { callback(data.body); + } else { + throw new Error('onAchTxAddressReceived error'); } }); } From 586b7daf31f38cd1a889ac7ca48b2011dd363c54 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Mon, 5 Jun 2023 10:55:23 +0800 Subject: [PATCH 026/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20sentry=20error?= =?UTF-8?q?=20level?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/store/Provider/Updater.tsx | 2 ++ .../app/web/store/Provider/index.tsx | 5 +++- .../utils/errorHandler/ExceptionHandler.ts | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 packages/web-extension-did/app/web/utils/errorHandler/ExceptionHandler.ts diff --git a/packages/web-extension-did/app/web/store/Provider/Updater.tsx b/packages/web-extension-did/app/web/store/Provider/Updater.tsx index a4c0945824..81404a4e3e 100644 --- a/packages/web-extension-did/app/web/store/Provider/Updater.tsx +++ b/packages/web-extension-did/app/web/store/Provider/Updater.tsx @@ -17,8 +17,10 @@ import { useCheckUpdate } from 'hooks/useCheckUpdate'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; import { useLocation } from 'react-router'; import { useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; keepAliveOnPages({}); +request.setExceptionManager(exceptionManager); export default function Updater() { const onLocking = useLocking(); diff --git a/packages/web-extension-did/app/web/store/Provider/index.tsx b/packages/web-extension-did/app/web/store/Provider/index.tsx index a38dbbb806..58e8adb918 100644 --- a/packages/web-extension-did/app/web/store/Provider/index.tsx +++ b/packages/web-extension-did/app/web/store/Provider/index.tsx @@ -12,6 +12,7 @@ import ReduxProvider from './ReduxProvider'; import Updater from './Updater'; import * as Sentry from '@sentry/react'; import { Integrations } from '@sentry/tracing'; +import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; const bodyRootWrapper = document.body; Sentry.init({ @@ -22,8 +23,10 @@ Sentry.init({ // We recommend adjusting this value in production tracesSampleRate: 1.0, // release: 'v1.0.0', - // environment: process.env.NODE_ENV, + environment: process.env.NODE_ENV, }); +exceptionManager.setSentryInstance(Sentry); + ConfigProvider.config({ prefixCls, }); diff --git a/packages/web-extension-did/app/web/utils/errorHandler/ExceptionHandler.ts b/packages/web-extension-did/app/web/utils/errorHandler/ExceptionHandler.ts new file mode 100644 index 0000000000..f0b23bddd3 --- /dev/null +++ b/packages/web-extension-did/app/web/utils/errorHandler/ExceptionHandler.ts @@ -0,0 +1,29 @@ +import * as Sentry from '@sentry/react'; +import { ExceptionManager, Severity } from '@portkey-wallet/utils/ExceptionManager'; + +class SentryExceptionManager extends ExceptionManager { + initGlobalJSErrorHandler() { + window.onerror = (_msg, _url, _line, _column, error) => { + this.reportError(error, Severity.Fatal); + Sentry.captureException(error, { level: Severity.Fatal }); + }; + } + initGlobalUnHandledPromiseErr() { + // require('promise/setimmediate/rejection-tracking').enable({ + // allRejections: true, + // onUnhandled: (id: any, error: any) => { + // const {message, stack} = error; + // const warning = + // `Possible Unhandled Promise Rejection (id: ${id}):\n` + + // (message == null ? '' : `${message}\n`) + + // (stack == null ? '' : stack); + // console.warn(warning); + // this.reportError(error, Severity.Warning); + // }, + // }); + } +} + +const exceptionManager = new SentryExceptionManager(Sentry); + +export { exceptionManager }; From 9f18ad9714a147607c2a438bc8eb1b12f0f2f052 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 5 Jun 2023 11:02:24 +0800 Subject: [PATCH 027/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/dapp/MobileStream.ts | 4 +- .../mobile-app-did/js/dapp/dappEventBus.ts | 12 +-- .../js/dapp/dappMobileOperator.ts | 10 +- packages/mobile-app-did/package.json | 6 +- packages/types/package.json | 2 +- packages/utils/package.json | 6 +- yarn.lock | 92 +++++++------------ 7 files changed, 56 insertions(+), 76 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/MobileStream.ts b/packages/mobile-app-did/js/dapp/MobileStream.ts index c24139a2ac..2a7b2531be 100644 --- a/packages/mobile-app-did/js/dapp/MobileStream.ts +++ b/packages/mobile-app-did/js/dapp/MobileStream.ts @@ -1,5 +1,5 @@ -import { DappInteractionStream } from '@portkey/providers'; -import WebView from 'react-native-webview'; +import { DappInteractionStream } from '@portkey/providers/dist/DappStream'; +import type WebView from 'react-native-webview'; export class MobileStream extends DappInteractionStream { private _webViewRef: WebView; public constructor(webViewRef: WebView) { diff --git a/packages/mobile-app-did/js/dapp/dappEventBus.ts b/packages/mobile-app-did/js/dapp/dappEventBus.ts index cc14eb9db0..f245d00b19 100644 --- a/packages/mobile-app-did/js/dapp/dappEventBus.ts +++ b/packages/mobile-app-did/js/dapp/dappEventBus.ts @@ -7,6 +7,7 @@ import { ChainIds, ConnectInfo, ProviderErrorType, + NotificationEvents, } from '@portkey/provider-types'; import DappMobileOperator from './dappMobileOperator'; @@ -27,12 +28,11 @@ export default class DappEventBus { public static unregisterOperator(operator: DappMobileOperator) { this.operators = this.operators.filter(item => item !== operator); } - public static dispatchEvent(params: DappEventPack): void; - public static dispatchEvent(params: DappEventPack<'chainChanged', ChainIds>): void; - public static dispatchEvent(params: DappEventPack<'accountsChanged', Accounts>): void; - public static dispatchEvent(params: DappEventPack<'networkChanged', NetworkType>): void; - public static dispatchEvent(params: DappEventPack<'connected', ConnectInfo>): void; - public static dispatchEvent(params: DappEventPack<'disconnected', ProviderErrorType>): void; + public static dispatchEvent(params: DappEventPack): void; + public static dispatchEvent(params: DappEventPack): void; + public static dispatchEvent(params: DappEventPack): void; + public static dispatchEvent(params: DappEventPack): void; + public static dispatchEvent(params: DappEventPack): void; public static dispatchEvent({ eventName, data, callback, origin, msg }: DappEventPack) { const event: IResponseType = { eventName, diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 520a23b061..68d2d12cc2 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -7,12 +7,13 @@ import { MethodsBase, MethodsUnimplemented, SendTransactionParams, + NotificationEvents, } from '@portkey/provider-types'; import DappEventBus from './dappEventBus'; import { generateNormalResponse, generateErrorResponse } from '@portkey/provider-utils'; import { IDappManager } from '@portkey-wallet/types/types-ca/dapp'; import { IDappOverlay } from './dappOverlay'; -import { Operator } from '@portkey/providers'; +import { Operator } from '@portkey/providers/dist/Operator'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount, getPin } from 'utils/redux'; @@ -108,6 +109,13 @@ export default class DappMobileOperator extends Operator { protected handleRequestAccounts: SendRequest = async (eventName, params) => { await this.dappManager.addDapp(params); + // connected + DappEventBus.dispatchEvent({ + eventName: NotificationEvents.CONNECTED, + data: { + chainIds: await this.dappManager.chainIds(), + }, + }); return generateNormalResponse({ eventName, data: await this.dappManager.accounts(params.origin!), diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index c8f3121d6a..92346484c4 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -49,9 +49,9 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/provider-types": "^0.0.1-alpha.11", - "@portkey/provider-utils": "^0.0.1-alpha.11", - "@portkey/providers": "^0.0.1-alpha.11", + "@portkey/provider-types": "^0.0.1-alpha.13", + "@portkey/provider-utils": "^0.0.1-alpha.13", + "@portkey/providers": "^0.0.1-alpha.13", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index af4d3c0c93..ba4d641488 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "version": "0.0.1", "type": "commonjs", "dependencies": { - "@portkey/provider-types": "^0.0.1-alpha.11" + "@portkey/provider-types": "^0.0.1-alpha.13" }, "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/utils/package.json b/packages/utils/package.json index a488e09f73..94d5d76c52 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,8 +23,8 @@ "expo-local-authentication": "^12.3.0", "expo-secure-store": "^11.3.0", "@react-native-async-storage/async-storage": "^1.17.6", - "@portkey/provider-types": "^0.0.1-alpha.11", - "@portkey/provider-utils": "^0.0.1-alpha.11", - "@portkey/providers": "^0.0.1-alpha.11" + "@portkey/provider-types": "^0.0.1-alpha.13", + "@portkey/provider-utils": "^0.0.1-alpha.13", + "@portkey/providers": "^0.0.1-alpha.13" } } diff --git a/yarn.lock b/yarn.lock index 4ebae60c2b..0e85512350 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5016,82 +5016,54 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/chain@^0.0.1-alpha.11": - version "0.0.1-alpha.11" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.11.tgz#047c4ea665d6aea61e5ba53714f2e7b002086a09" - integrity sha512-Gp/1AngQxA1pwGCOV5f6irUC7b70M3douAlFs/0r6A6VMUj545YXVazQTRZ0Dcp4sO4GBaZebbSdIyLhWUGYMw== +"@portkey/chain@^0.0.1-alpha.13": + version "0.0.1-alpha.13" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.13.tgz#5aa17f13693c8c2c9606c394dc32904b02a6e53f" + integrity sha512-gqK8PdKleMC4aEYanw/hn/z9vCYPEtv5AXN6cytZQKaL/Wl8/x2HVnELywRPGHUAEmokyLtbF51KIAi+oAl0mg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.11" + "@portkey/provider-types" "^0.0.1-alpha.13" aelf-sdk "^3.2.44" -"@portkey/chain@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.8.tgz#20a6d30fe6ceb71c3d430ee898422f0a7a86665d" - integrity sha512-FLdUFprntO76rtH5N0Ef2gJ3moNfWpDwlym92WqcWq15qqjCjbBk/l86FgOkZsfLbzgZggHM1n4b8W62wvz9CQ== +"@portkey/extension-provider@0.0.1-alpha.10": + version "0.0.1-alpha.10" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.10.tgz#d15f8370802ba9f8cc00e02a11f566d889aa915d" + integrity sha512-xdHmmLfBccEYcNLaujQkWRJi+BLZMJeXpHBRwKXlKzNi1C7ioqyoeCZpR389CKQO99d34wht4lNjkmKOX6TBfg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - aelf-sdk "^3.2.44" - -"@portkey/extension-provider@0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.8.tgz#c311230f553b615378093933ee7925e1607ed169" - integrity sha512-93RqrJK2CyCKCwqlV+CFd9ixkyzGAl9yViTNoag/HjR2uNJd7dGo71HnJmDC/ijUokR+ZmnVtiJsNP+h2/DueQ== - dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/providers" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.10" + "@portkey/providers" "^0.0.1-alpha.10" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/provider-types@^0.0.1-alpha.11": - version "0.0.1-alpha.11" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.11.tgz#c9a8a4ed006385eb1527d23ec1aab0d11695d1ff" - integrity sha512-EhgThMje7hn7v5tj3koxW37/qKOyF8Z9pozXe3uKE+Iy37J6VvveSbKxtda/j2TEh/XU71bGCCeNKbgx/uOdPw== - dependencies: - "@types/readable-stream" "^2.3.15" - -"@portkey/provider-types@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.8.tgz#bb4996ec275d23fa1272b828b89f2043ec6a8337" - integrity sha512-qAELo9JKWCC7QWn/rCtwwxx8gokKv7ybeknn25EM5REZu80GK7Zg7KhRTgMlky+NyOQBNqyuc+1dl19S+RwaJA== +"@portkey/provider-types@^0.0.1-alpha.10", "@portkey/provider-types@^0.0.1-alpha.13": + version "0.0.1-alpha.13" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.13.tgz#70d831d0917ac902e72de5f85c5b304b0fe3b147" + integrity sha512-p6vJqsAVaCzz1pGLlzr6NH+5o1GTKxQH3vfG/m7hLuNxTFo6gVp3fklBfgs6F6A7ePYBTtOgcZO4QFiSfPO2tg== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.8", "@portkey/provider-utils@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.8.tgz#f380927c43f4c718e6d7ede438cf6ad48ecaa5f8" - integrity sha512-p+coX6yBr7bEv7BSk4vKjaKIGftBHhEbWBcRMVb1NZ5b6UWwyiy3rfRgzwm4IRc5D/Qvqquyam/FLyHCfrgh6Q== +"@portkey/provider-utils@0.0.1-alpha.10": + version "0.0.1-alpha.10" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.10.tgz#9f3f9641c11ba1e390c97867a01e7df9393ac6ec" + integrity sha512-fiDrTeWNzmiLUp/g+aI8g+GzpHMKUATD7/pdIr33SMdGLcNBlfit7gXijt5Zzdyax8GOM/8GwCCyEOSLx+jA/Q== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.10" -"@portkey/provider-utils@^0.0.1-alpha.11": - version "0.0.1-alpha.11" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.11.tgz#d14563aaaa5cdd88cf647eab903942396d6a9457" - integrity sha512-Cb2sohKPISKp2racIIlpK1NPTC75t5lbIq9K+xt4KhCcETU64zBHE9ASuI2PRMlko8ksNxS+19ZUuy9OLSWNrg== +"@portkey/provider-utils@^0.0.1-alpha.13": + version "0.0.1-alpha.13" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.13.tgz#a94b5e4118c7f31c2c92af2a2c96d9517e39ba83" + integrity sha512-A2rr1iW6lsNdNo0ZmLeDvpixntTNH/xzYq44m6oxwJJgJPWQJ3hQ4m1piDLY4ZhO9mkEoJEFqEC0s3dDXmambQ== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.11" - -"@portkey/providers@^0.0.1-alpha.11": - version "0.0.1-alpha.11" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.11.tgz#2deaf83e9aeed9dfed36209103ad22be0e325f8c" - integrity sha512-t54NiSBoYJW2YPhBXSaJN8Yn9eVbS8AkSPih2/M+2ejlZTCT8FzyAwIKjAyV1CTjK2k6mw53BLni9/xROSaWWA== - dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.11" - "@portkey/provider-types" "^0.0.1-alpha.11" - "@portkey/provider-utils" "^0.0.1-alpha.11" - "@types/readable-stream" "^2.3.15" - pump "^3.0.0" - readable-stream "^4.4.0" + "@portkey/provider-types" "^0.0.1-alpha.13" -"@portkey/providers@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.8.tgz#c94228aee7f683d8297231f92dda1a28208e733c" - integrity sha512-L6wtRoURDngJfRj0ruz9TOGwj5TNP1prQTaEvY8WYdhnH/sAXef2U6uxLDPfBD03ot1kZ65d3/dg1nSc4HJP7w== +"@portkey/providers@^0.0.1-alpha.10", "@portkey/providers@^0.0.1-alpha.13": + version "0.0.1-alpha.13" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.13.tgz#c1c3ddf90e372cda0b79f5877afa44eaaf9760bc" + integrity sha512-re+J1XJqIRelqXqoV2qzC+s+OorA6xyjiQTos5/duvEuH9jT2pPQfaDa27rJETWwAyF8tNiZti4OGF0q+ue9Ug== dependencies: "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.8" - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/provider-utils" "^0.0.1-alpha.8" + "@portkey/chain" "^0.0.1-alpha.13" + "@portkey/provider-types" "^0.0.1-alpha.13" + "@portkey/provider-utils" "^0.0.1-alpha.13" "@types/readable-stream" "^2.3.15" pump "^3.0.0" readable-stream "^4.4.0" From 61c02d1f88169acfab9f44d2a76c3acf125dd467 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 5 Jun 2023 11:34:31 +0800 Subject: [PATCH 028/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20postinstall.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/.gitignore | 2 +- packages/mobile-app-did/package.json | 5 ++++- packages/mobile-app-did/scripts/postinstall.sh | 4 ++++ yarn.lock | 5 +++++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 packages/mobile-app-did/scripts/postinstall.sh diff --git a/packages/mobile-app-did/.gitignore b/packages/mobile-app-did/.gitignore index 930dc85a14..8358f39c53 100644 --- a/packages/mobile-app-did/.gitignore +++ b/packages/mobile-app-did/.gitignore @@ -70,4 +70,4 @@ handleSvg.js .env # InpageBridgeWeb3 -/js/utils/InpageBridgeWeb3.js +**/InpageBridgeWeb3.js diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index 92346484c4..f5c41c9e30 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -3,6 +3,7 @@ "version": "1.1.5", "private": true, "scripts": { + "postinstall": "bash scripts/postinstall.sh", "handle-network": "yarn workspace @portkey-wallet/constants run handle-network", "android": "react-native run-android", "ios": "react-native run-ios --simulator='iPhone 13 Pro'", @@ -37,7 +38,8 @@ "base-64", "buffer", "rn-teaset", - "@shopify/flash-list" + "@shopify/flash-list", + "@portkey/mobile-provider" ] }, "dependencies": { @@ -49,6 +51,7 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", + "@portkey/mobile-provider": "^0.0.1-alpha.13", "@portkey/provider-types": "^0.0.1-alpha.13", "@portkey/provider-utils": "^0.0.1-alpha.13", "@portkey/providers": "^0.0.1-alpha.13", diff --git a/packages/mobile-app-did/scripts/postinstall.sh b/packages/mobile-app-did/scripts/postinstall.sh new file mode 100644 index 0000000000..49f4c92b47 --- /dev/null +++ b/packages/mobile-app-did/scripts/postinstall.sh @@ -0,0 +1,4 @@ +# Import provider +cp -rf node_modules/@portkey/mobile-provider/dist/index.js js/utils/InpageBridgeWeb3.js +cp -rf js/utils/InpageBridgeWeb3.js android/app/src/main/assets/. +echo "cp InpageBridgeWeb3 success" diff --git a/yarn.lock b/yarn.lock index 0e85512350..ebef4a7321 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5034,6 +5034,11 @@ "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" +"@portkey/mobile-provider@^0.0.1-alpha.13": + version "0.0.1-alpha.13" + resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.13.tgz#93feae05af7fff42c30ce5e63902b12e4b8c2236" + integrity sha512-Lbra5xle1pP8ktCjwE7p/OwdlJjMeAgNe5BL9ze/uzNF/CdgReVbukO+YCmAfbbm6dQtz8Adl4f4eAM0wZvEuw== + "@portkey/provider-types@^0.0.1-alpha.10", "@portkey/provider-types@^0.0.1-alpha.13": version "0.0.1-alpha.13" resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.13.tgz#70d831d0917ac902e72de5f85c5b304b0fe3b147" From 71a106cd097d230ff1cabd8b4812b29a2354e2ae Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 5 Jun 2023 18:26:48 +0800 Subject: [PATCH 029/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dapp=20middle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/dapp/MobileStream.ts | 2 +- .../mobile-app-did/js/dapp/dappEventBus.ts | 36 +++++++++++++++++++ .../js/dapp/dappMobileOperator.ts | 11 +++--- .../mobile-app-did/js/dapp/mobileStream.ts | 18 ++++++++++ packages/mobile-app-did/js/store/index.ts | 5 ++- packages/utils/dapp/dappManager.ts | 27 +++++--------- packages/utils/dapp/index.ts | 27 ++++++++++++++ packages/utils/dapp/middle.ts | 28 +++++++++++++++ 8 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 packages/mobile-app-did/js/dapp/mobileStream.ts create mode 100644 packages/utils/dapp/index.ts create mode 100644 packages/utils/dapp/middle.ts diff --git a/packages/mobile-app-did/js/dapp/MobileStream.ts b/packages/mobile-app-did/js/dapp/MobileStream.ts index 2a7b2531be..0ef4ce664f 100644 --- a/packages/mobile-app-did/js/dapp/MobileStream.ts +++ b/packages/mobile-app-did/js/dapp/MobileStream.ts @@ -1,4 +1,4 @@ -import { DappInteractionStream } from '@portkey/providers/dist/DappStream'; +import { DappInteractionStream } from '@portkey/providers/dist/dappStream'; import type WebView from 'react-native-webview'; export class MobileStream extends DappInteractionStream { private _webViewRef: WebView; diff --git a/packages/mobile-app-did/js/dapp/dappEventBus.ts b/packages/mobile-app-did/js/dapp/dappEventBus.ts index f245d00b19..f8c3e7427f 100644 --- a/packages/mobile-app-did/js/dapp/dappEventBus.ts +++ b/packages/mobile-app-did/js/dapp/dappEventBus.ts @@ -10,6 +10,11 @@ import { NotificationEvents, } from '@portkey/provider-types'; import DappMobileOperator from './dappMobileOperator'; +import { DappMiddle } from '@portkey-wallet/utils/dapp/middle'; +import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; +import { getWallet } from 'utils/redux'; +import { handleAccounts, handleChainIds } from '@portkey-wallet/utils/dapp'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; export interface DappEventPack { eventName: T; @@ -28,6 +33,34 @@ export default class DappEventBus { public static unregisterOperator(operator: DappMobileOperator) { this.operators = this.operators.filter(item => item !== operator); } + public static emit(action: string, payload: any) { + switch (action) { + case changeNetworkType.toString(): { + const { currentNetwork } = getWallet(); + DappEventBus.dispatchEvent({ eventName: NotificationEvents.NETWORK_CHANGED, data: currentNetwork }); + break; + } + case setCAInfo.toString(): { + const wallet = getWallet(); + DappEventBus.dispatchEvent({ eventName: NotificationEvents.ACCOUNTS_CHANGED, data: handleAccounts(wallet) }); + DappEventBus.dispatchEvent({ eventName: NotificationEvents.CHAIN_CHANGED, data: handleChainIds(wallet) }); + break; + } + case removeDapp.toString(): { + if (payload.origin) + DappEventBus.dispatchEvent({ + eventName: NotificationEvents.DISCONNECTED, + origin: payload.origin, + data: { + message: 'user disconnected', + code: ResponseCode.USER_DENIED, + }, + }); + break; + } + } + } + public static dispatchEvent(params: DappEventPack): void; public static dispatchEvent(params: DappEventPack): void; public static dispatchEvent(params: DappEventPack): void; @@ -50,3 +83,6 @@ export default class DappEventBus { callback?.(); } } + +// register event +DappMiddle.registerEvent(DappEventBus); diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 68d2d12cc2..6f32af5030 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -13,7 +13,7 @@ import DappEventBus from './dappEventBus'; import { generateNormalResponse, generateErrorResponse } from '@portkey/provider-utils'; import { IDappManager } from '@portkey-wallet/types/types-ca/dapp'; import { IDappOverlay } from './dappOverlay'; -import { Operator } from '@portkey/providers/dist/Operator'; +import { Operator } from '@portkey/providers/dist/operator'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount, getPin } from 'utils/redux'; @@ -109,7 +109,7 @@ export default class DappMobileOperator extends Operator { protected handleRequestAccounts: SendRequest = async (eventName, params) => { await this.dappManager.addDapp(params); - // connected + // Notification connected DappEventBus.dispatchEvent({ eventName: NotificationEvents.CONNECTED, data: { @@ -123,7 +123,8 @@ export default class DappMobileOperator extends Operator { }; protected handleSendTransaction: SendRequest = async (eventName, params) => { try { - if (!params.params) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); + if (!params || !params.params || !params.method || !params.contractAddress || !params.chainId) + return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); const chainInfo = await this.dappManager.getChainInfo(params.chainId); const caInfo = await this.dappManager.getCaInfo(params.chainId); @@ -148,9 +149,7 @@ export default class DappMobileOperator extends Operator { functionName = 'ManagerForwardCall'; } - const data = await contract!.callSendMethod(functionName, '', paramsOption, { - onMethod: 'transactionHash', - }); + const data = await contract!.callSendMethod(functionName, '', paramsOption, { onMethod: 'transactionHash' }); if (!data?.error) { return generateNormalResponse({ eventName, diff --git a/packages/mobile-app-did/js/dapp/mobileStream.ts b/packages/mobile-app-did/js/dapp/mobileStream.ts new file mode 100644 index 0000000000..0ef4ce664f --- /dev/null +++ b/packages/mobile-app-did/js/dapp/mobileStream.ts @@ -0,0 +1,18 @@ +import { DappInteractionStream } from '@portkey/providers/dist/dappStream'; +import type WebView from 'react-native-webview'; +export class MobileStream extends DappInteractionStream { + private _webViewRef: WebView; + public constructor(webViewRef: WebView) { + super(); + this._webViewRef = webViewRef; + } + + public _write(chunk: any, _encoding: BufferEncoding, callback: (error?: Error | null | undefined) => void): void { + const text = chunk.toString(); + if (__DEV__) { + console.log('MobileStream _write', text); + } + this._webViewRef.postMessage(text); + callback(); + } +} diff --git a/packages/mobile-app-did/js/store/index.ts b/packages/mobile-app-did/js/store/index.ts index 4326b56ea9..99d8fffd35 100644 --- a/packages/mobile-app-did/js/store/index.ts +++ b/packages/mobile-app-did/js/store/index.ts @@ -3,7 +3,7 @@ import { persistReducer } from 'redux-persist'; import storeConfig from './config'; import rootReducer from './rootReducer'; import { rateApi } from '@portkey-wallet/store/rate/api'; - +import { DappMiddle } from '@portkey-wallet/utils/dapp/middle'; const persistedReducer = persistReducer(storeConfig.reduxPersistConfig, rootReducer); const middlewareList: any[] = []; @@ -14,6 +14,9 @@ if (__DEV__) { } middlewareList.push(rateApi.middleware); +// dapp middle +middlewareList.push(DappMiddle.middle); + export const store = configureStore({ reducer: persistedReducer, middleware: getDefaultMiddleware => getDefaultMiddleware(storeConfig.defaultMiddlewareOptions).concat(middlewareList), diff --git a/packages/utils/dapp/dappManager.ts b/packages/utils/dapp/dappManager.ts index 3eee8ef916..286037c382 100644 --- a/packages/utils/dapp/dappManager.ts +++ b/packages/utils/dapp/dappManager.ts @@ -4,7 +4,8 @@ import { ChainItemType } from '@portkey-wallet/store/store-ca/wallet/type'; import { DappManagerOptions, IDappManager, IDappManagerStore } from '@portkey-wallet/types/types-ca/dapp'; import { CACommonState } from '@portkey-wallet/types/types-ca/store'; import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; -import { Accounts, ChainId, ChainIds, ChainsInfo } from '@portkey/provider-types'; +import { ChainId, ChainsInfo } from '@portkey/provider-types'; +import { handleAccounts, handleChainIds, handleCurrentCAInfo } from './index'; import { isEqDapp } from './browser'; export abstract class BaseDappManager { @@ -34,8 +35,8 @@ export abstract class DappManager return !!(await this.getOriginInfo(origin)); } async getCurrentCAInfo() { - const { walletInfo, currentNetwork } = await this.getWallet(); - return walletInfo?.caInfo[currentNetwork]; + const wallet = await this.getWallet(); + return handleCurrentCAInfo(wallet); } async getCaInfo(chainId: ChainId): Promise { return (await this.getCurrentCAInfo())?.[chainId]; @@ -65,27 +66,17 @@ export abstract class DappManager return (await this.originIsAuthorized(origin)) && (await this.isLogged()); } async accounts(origin: string) { - const { walletInfo, currentNetwork } = await this.getWallet(); - if (!(await this.isActive(origin)) || !walletInfo?.caInfo) return {}; - const accounts: Accounts = {}; - Object.entries(walletInfo.caInfo[currentNetwork] || {}).forEach(([key, value]) => { - if ((value as CAInfo)?.caAddress) accounts[key as ChainId] = [(value as CAInfo).caAddress]; - }); - return accounts; + const wallet = await this.getWallet(); + if (!(await this.isActive(origin)) || !wallet.walletInfo?.caInfo) return {}; + return handleAccounts(wallet); } async chainId() { return this.chainIds(); } async chainIds() { if (!this.isLogged()) return []; - const currentCAInfo = await this.getCurrentCAInfo(); - const list = Object.entries(currentCAInfo || {}) - .map(([key, value]) => { - if ((value as CAInfo)?.caAddress) return key; - return undefined; - }) - .filter(i => !!i); - return list as ChainIds; + const wallet = await this.getWallet(); + return handleChainIds(wallet); } async chainsInfo() { const chainsInfo: ChainsInfo = {}; diff --git a/packages/utils/dapp/index.ts b/packages/utils/dapp/index.ts new file mode 100644 index 0000000000..b5291fc816 --- /dev/null +++ b/packages/utils/dapp/index.ts @@ -0,0 +1,27 @@ +import { WalletState } from '@portkey-wallet/store/store-ca/wallet/type'; +import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; +import { Accounts, ChainId, ChainIds } from '@portkey/provider-types'; +export function handleCurrentCAInfo(wallet: WalletState) { + const { walletInfo, currentNetwork } = wallet; + return walletInfo?.caInfo[currentNetwork]; +} + +export function handleChainIds(wallet: WalletState) { + const currentCAInfo = handleCurrentCAInfo(wallet); + const list = Object.entries(currentCAInfo || {}) + .map(([key, value]) => { + if ((value as CAInfo)?.caAddress) return key; + return undefined; + }) + .filter(i => !!i); + return list as ChainIds; +} + +export function handleAccounts(wallet: WalletState) { + const currentCAInfo = handleCurrentCAInfo(wallet); + const accounts: Accounts = {}; + Object.entries(currentCAInfo || {}).forEach(([key, value]) => { + if ((value as CAInfo)?.caAddress) accounts[key as ChainId] = [(value as CAInfo).caAddress]; + }); + return accounts; +} diff --git a/packages/utils/dapp/middle.ts b/packages/utils/dapp/middle.ts new file mode 100644 index 0000000000..621c0fdabe --- /dev/null +++ b/packages/utils/dapp/middle.ts @@ -0,0 +1,28 @@ +// Middleware<{}, RootState, ThunkDispatch> +import { CACommonState } from '@portkey-wallet/types/types-ca/store'; +import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; + +export interface IMiddlewareAPI { + getState(): Promise; + dispatch: any; +} + +export interface IEvent { + emit: (action: string, payload: any) => void; +} + +const ActionList = [setCAInfo.toString(), changeNetworkType.toString(), removeDapp.toString()]; + +export class DappMiddle { + public static event?: IEvent; + public static middle = () => (next: any) => (action: { type: string; payload: any }) => { + if (ActionList.includes(action.type)) { + DappMiddle.event?.emit(action.type, action.payload); + } + return next(action); + }; + public static registerEvent(event: IEvent) { + DappMiddle.event = event; + } +} From 47e9b30aafb4a1d86fecdf03d865b1dafa99f55f Mon Sep 17 00:00:00 2001 From: portkey-yellow <120541606+portkey-yellow@users.noreply.github.com> Date: Mon, 5 Jun 2023 18:42:52 +0800 Subject: [PATCH 030/893] Delete MobileStream.ts --- .../mobile-app-did/js/dapp/MobileStream.ts | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 packages/mobile-app-did/js/dapp/MobileStream.ts diff --git a/packages/mobile-app-did/js/dapp/MobileStream.ts b/packages/mobile-app-did/js/dapp/MobileStream.ts deleted file mode 100644 index 0ef4ce664f..0000000000 --- a/packages/mobile-app-did/js/dapp/MobileStream.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DappInteractionStream } from '@portkey/providers/dist/dappStream'; -import type WebView from 'react-native-webview'; -export class MobileStream extends DappInteractionStream { - private _webViewRef: WebView; - public constructor(webViewRef: WebView) { - super(); - this._webViewRef = webViewRef; - } - - public _write(chunk: any, _encoding: BufferEncoding, callback: (error?: Error | null | undefined) => void): void { - const text = chunk.toString(); - if (__DEV__) { - console.log('MobileStream _write', text); - } - this._webViewRef.postMessage(text); - callback(); - } -} From 2ce09676157c69920cd2c927ac4e635beb43e638 Mon Sep 17 00:00:00 2001 From: "Carbon.lv" <130360750+carbon-portkey@users.noreply.github.com> Date: Tue, 6 Jun 2023 10:15:39 +0800 Subject: [PATCH 031/893] feat: add UT config --- jest.config.js | 7 ++- .../__tests__/setup/mockAsyncStorage.js | 4 ++ packages/mobile-app-did/package.json | 7 +++ yarn.lock | 54 +++++-------------- 4 files changed, 29 insertions(+), 43 deletions(-) diff --git a/jest.config.js b/jest.config.js index ad036e4eb2..890910929c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -69,10 +69,13 @@ module.exports = { }, { displayName: 'mobile-app-did', - preset: 'ts-jest', + preset: 'react-native', roots: ['/packages/mobile-app-did'], transform: { - '^.+\\.(ts|tsx)$': [`ts-jest`, { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }], + '^.+\\.(ts|tsx)$': [ + `react-native`, + { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }, + ], }, moduleNameMapper: { '^react$': '/node_modules/react', diff --git a/packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js b/packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js index cc7b74ebd8..d3c97de03b 100644 --- a/packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js +++ b/packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js @@ -1,3 +1,7 @@ import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock'; jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage); + +test('mockAsyncStorage', () => { + expect(true).toBeTruthy(); +}); diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index c8f3121d6a..fab978c289 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -193,6 +193,13 @@ "json", "node" ], + "moduleNameMapper": { + "^react$": "/../../node_modules/react", + "^utils/(.*)$": "/js/utils/$1", + "^store/(.*)$": "/js/store/$1", + "store": "/js/store/index.ts", + "^dapp/(.*)$": "/js/dapp/$1" + }, "setupFilesAfterEnv": [ "./__tests__/setup/mockAsyncStorage.js" ] diff --git a/yarn.lock b/yarn.lock index 4ebae60c2b..01f2345281 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5024,44 +5024,29 @@ "@portkey/provider-types" "^0.0.1-alpha.11" aelf-sdk "^3.2.44" -"@portkey/chain@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.8.tgz#20a6d30fe6ceb71c3d430ee898422f0a7a86665d" - integrity sha512-FLdUFprntO76rtH5N0Ef2gJ3moNfWpDwlym92WqcWq15qqjCjbBk/l86FgOkZsfLbzgZggHM1n4b8W62wvz9CQ== +"@portkey/extension-provider@0.0.1-alpha.10": + version "0.0.1-alpha.10" + resolved "https://registry.yarnpkg.com/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.10.tgz#d15f8370802ba9f8cc00e02a11f566d889aa915d" + integrity sha512-xdHmmLfBccEYcNLaujQkWRJi+BLZMJeXpHBRwKXlKzNi1C7ioqyoeCZpR389CKQO99d34wht4lNjkmKOX6TBfg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - aelf-sdk "^3.2.44" - -"@portkey/extension-provider@0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.8.tgz#c311230f553b615378093933ee7925e1607ed169" - integrity sha512-93RqrJK2CyCKCwqlV+CFd9ixkyzGAl9yViTNoag/HjR2uNJd7dGo71HnJmDC/ijUokR+ZmnVtiJsNP+h2/DueQ== - dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/providers" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.10" + "@portkey/providers" "^0.0.1-alpha.10" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/provider-types@^0.0.1-alpha.11": +"@portkey/provider-types@^0.0.1-alpha.10", "@portkey/provider-types@^0.0.1-alpha.11": version "0.0.1-alpha.11" resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.11.tgz#c9a8a4ed006385eb1527d23ec1aab0d11695d1ff" integrity sha512-EhgThMje7hn7v5tj3koxW37/qKOyF8Z9pozXe3uKE+Iy37J6VvveSbKxtda/j2TEh/XU71bGCCeNKbgx/uOdPw== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-types@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.8.tgz#bb4996ec275d23fa1272b828b89f2043ec6a8337" - integrity sha512-qAELo9JKWCC7QWn/rCtwwxx8gokKv7ybeknn25EM5REZu80GK7Zg7KhRTgMlky+NyOQBNqyuc+1dl19S+RwaJA== - dependencies: - "@types/readable-stream" "^2.3.15" - -"@portkey/provider-utils@0.0.1-alpha.8", "@portkey/provider-utils@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.8.tgz#f380927c43f4c718e6d7ede438cf6ad48ecaa5f8" - integrity sha512-p+coX6yBr7bEv7BSk4vKjaKIGftBHhEbWBcRMVb1NZ5b6UWwyiy3rfRgzwm4IRc5D/Qvqquyam/FLyHCfrgh6Q== +"@portkey/provider-utils@0.0.1-alpha.10": + version "0.0.1-alpha.10" + resolved "https://registry.yarnpkg.com/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.10.tgz#9f3f9641c11ba1e390c97867a01e7df9393ac6ec" + integrity sha512-fiDrTeWNzmiLUp/g+aI8g+GzpHMKUATD7/pdIr33SMdGLcNBlfit7gXijt5Zzdyax8GOM/8GwCCyEOSLx+jA/Q== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.10" "@portkey/provider-utils@^0.0.1-alpha.11": version "0.0.1-alpha.11" @@ -5070,7 +5055,7 @@ dependencies: "@portkey/provider-types" "^0.0.1-alpha.11" -"@portkey/providers@^0.0.1-alpha.11": +"@portkey/providers@^0.0.1-alpha.10", "@portkey/providers@^0.0.1-alpha.11": version "0.0.1-alpha.11" resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.11.tgz#2deaf83e9aeed9dfed36209103ad22be0e325f8c" integrity sha512-t54NiSBoYJW2YPhBXSaJN8Yn9eVbS8AkSPih2/M+2ejlZTCT8FzyAwIKjAyV1CTjK2k6mw53BLni9/xROSaWWA== @@ -5083,19 +5068,6 @@ pump "^3.0.0" readable-stream "^4.4.0" -"@portkey/providers@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.8.tgz#c94228aee7f683d8297231f92dda1a28208e733c" - integrity sha512-L6wtRoURDngJfRj0ruz9TOGwj5TNP1prQTaEvY8WYdhnH/sAXef2U6uxLDPfBD03ot1kZ65d3/dg1nSc4HJP7w== - dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.8" - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/provider-utils" "^0.0.1-alpha.8" - "@types/readable-stream" "^2.3.15" - pump "^3.0.0" - readable-stream "^4.4.0" - "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From c4ab3bb6c1da55bd8f26a4dfd111eebfca891da0 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 6 Jun 2023 13:42:29 +0800 Subject: [PATCH 032/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20provide?= =?UTF-8?q?r=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/package.json | 8 ++--- yarn.lock | 44 +++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index f5c41c9e30..8d3d6cec30 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -51,10 +51,10 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/mobile-provider": "^0.0.1-alpha.13", - "@portkey/provider-types": "^0.0.1-alpha.13", - "@portkey/provider-utils": "^0.0.1-alpha.13", - "@portkey/providers": "^0.0.1-alpha.13", + "@portkey/mobile-provider": "^0.0.1-alpha.14", + "@portkey/provider-types": "^0.0.1-alpha.14", + "@portkey/provider-utils": "^0.0.1-alpha.14", + "@portkey/providers": "^0.0.1-alpha.14", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/yarn.lock b/yarn.lock index ebef4a7321..a9218312ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5024,6 +5024,14 @@ "@portkey/provider-types" "^0.0.1-alpha.13" aelf-sdk "^3.2.44" +"@portkey/chain@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.14.tgz#5b92d50818f38d8b5b3da442fb720cdd4e879970" + integrity sha512-+hhSd0Qu1Jm/LbSrQ5/zA+Qogyx/dEsercvdCW3ogvfh05C5gDzrhKFX3Q0WvvWYwKZugHFbReeMFrXqyX8quQ== + dependencies: + "@portkey/provider-types" "^0.0.1-alpha.14" + aelf-sdk "^3.2.44" + "@portkey/extension-provider@0.0.1-alpha.10": version "0.0.1-alpha.10" resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.10.tgz#d15f8370802ba9f8cc00e02a11f566d889aa915d" @@ -5034,10 +5042,10 @@ "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/mobile-provider@^0.0.1-alpha.13": - version "0.0.1-alpha.13" - resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.13.tgz#93feae05af7fff42c30ce5e63902b12e4b8c2236" - integrity sha512-Lbra5xle1pP8ktCjwE7p/OwdlJjMeAgNe5BL9ze/uzNF/CdgReVbukO+YCmAfbbm6dQtz8Adl4f4eAM0wZvEuw== +"@portkey/mobile-provider@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.14.tgz#2fec1ad012739ace1c35e9e9907fc8b9ea410dda" + integrity sha512-VnZuHzabedRZgaGEV+vseJR6v4c2QSLY8E4SFov1htdNz5Agn7rybkcPpd50P8IF9hKeLwEADqf7nrZCtxLoUA== "@portkey/provider-types@^0.0.1-alpha.10", "@portkey/provider-types@^0.0.1-alpha.13": version "0.0.1-alpha.13" @@ -5046,6 +5054,13 @@ dependencies: "@types/readable-stream" "^2.3.15" +"@portkey/provider-types@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.14.tgz#7608c7f8825595a70e3db416db627939d9aed0e3" + integrity sha512-7OELNO9uyvvi92lU0dFtR6sQjSevMTii1kw1xos86MRhMndNBKAL315rkM29D1XQnL/0rdxjzEquKl3Kpre0mw== + dependencies: + "@types/readable-stream" "^2.3.15" + "@portkey/provider-utils@0.0.1-alpha.10": version "0.0.1-alpha.10" resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.10.tgz#9f3f9641c11ba1e390c97867a01e7df9393ac6ec" @@ -5060,6 +5075,13 @@ dependencies: "@portkey/provider-types" "^0.0.1-alpha.13" +"@portkey/provider-utils@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.14.tgz#9bf6de253c43e3e06ce487a8bc02f1119d755bdc" + integrity sha512-NyMlvnpABc8cINxXf72quVrXtic3YpSG/U5T4PlFJRjGqLGFiVZ8xyIcONqxpA0DilqNZEMonmp72LZfEEZh9g== + dependencies: + "@portkey/provider-types" "^0.0.1-alpha.14" + "@portkey/providers@^0.0.1-alpha.10", "@portkey/providers@^0.0.1-alpha.13": version "0.0.1-alpha.13" resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.13.tgz#c1c3ddf90e372cda0b79f5877afa44eaaf9760bc" @@ -5073,6 +5095,20 @@ pump "^3.0.0" readable-stream "^4.4.0" +"@portkey/providers@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.14.tgz#4c15939c24610a1d99920750f3b6fdc933408869" + integrity sha512-wvjVsvj4D9Xjgh4/HoN8sKWx5B5ePYRsyu69zOMDxFvL0K7+9xlFedQCBcXpKwjdZ6JvG9Kbc1b9XGrk6OR2lw== + dependencies: + "@metamask/object-multiplex" "^1.1.0" + "@portkey/chain" "^0.0.1-alpha.14" + "@portkey/provider-types" "^0.0.1-alpha.14" + "@portkey/provider-utils" "^0.0.1-alpha.14" + "@types/readable-stream" "^2.3.15" + lodash "^4.17.21" + pump "^3.0.0" + readable-stream "^4.4.0" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From 295c61949240d86e0df7b0689d4ee60ce5fe6f67 Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Tue, 6 Jun 2023 13:50:02 +0800 Subject: [PATCH 033/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/dapp/type.ts | 2 + packages/web-extension-did/app/web/content.ts | 30 +++- .../app/web/controllers/SWController.ts | 60 ------- .../app/web/controllers/SWEventController.ts | 166 ++++++++++-------- .../app/web/messages/InternalMessageTypes.ts | 11 +- .../app/web/messages/utils.ts | 6 + .../serviceWorker/ServiceWorkerInstantiate.ts | 40 +++-- .../app/web/serviceWorker/connectListener.ts | 4 +- .../serviceWorker/serviceWorkerListener.ts | 11 +- .../web-extension-did/app/web/types/SW.ts | 4 +- .../app/web/types/storage.ts | 6 - packages/web-extension-did/package.json | 6 +- yarn.lock | 73 ++++---- 13 files changed, 191 insertions(+), 228 deletions(-) delete mode 100644 packages/web-extension-did/app/web/controllers/SWController.ts create mode 100644 packages/web-extension-did/app/web/messages/utils.ts diff --git a/packages/store/store-ca/dapp/type.ts b/packages/store/store-ca/dapp/type.ts index f888c6deee..c5286e1f30 100644 --- a/packages/store/store-ca/dapp/type.ts +++ b/packages/store/store-ca/dapp/type.ts @@ -4,6 +4,8 @@ export type DappStoreItem = { origin: string; name?: string; icon?: string; + // Record the tabId of the dapp after opening the link + tadIds?: string[]; }; export interface IDappStoreState { diff --git a/packages/web-extension-did/app/web/content.ts b/packages/web-extension-did/app/web/content.ts index 4524fdc093..6e194ed69e 100644 --- a/packages/web-extension-did/app/web/content.ts +++ b/packages/web-extension-did/app/web/content.ts @@ -4,8 +4,18 @@ import { getUrl } from './utils/getHostname'; import { runWorkerKeepAliveInterval } from 'utils/keepAlive'; import { checkForError } from 'utils'; import { ContentPostStream } from '@portkey/extension-provider'; -import { MethodsUnimplemented, IRequestParams } from '@portkey/provider-types'; +import { + MethodsUnimplemented, + IRequestParams, + MethodsType, + ProviderError, + ResponseMessagePreset, + ResponseCode, +} from '@portkey/provider-types'; import { generateErrorResponse, generateNormalResponse } from '@portkey/provider-utils'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import { isMethodsBase, isMethodsUnimplemented } from '@portkey/providers'; +import { isMethodsWalletMessage } from 'messages/utils'; /** * Don't run the keep-worker-alive logic for JSON-RPC methods called on initial load. * This is to prevent the service worker from being kept alive when accounts are not @@ -139,11 +149,19 @@ class Content { }; } + methodCheck = (method: string): method is MethodsType => { + return isMethodsBase(method) || isMethodsUnimplemented(method) || isMethodsWalletMessage(method); + }; + contentListener(input: IRequestParams) { + const URL = getUrl(); + const icon = `https://api.faviconkit.com/${getHost(URL.href)}/50`; + const message = Object.assign({}, input, { - hostname: getUrl().hostname, - origin: getUrl().origin, - href: getUrl().href, + hostname: URL.hostname, + origin: URL.origin, + href: URL.href, + icon, }); const method = message.method; @@ -152,7 +170,9 @@ class Content { if (!IGNORE_INIT_METHODS_FOR_KEEP_ALIVE.includes(method)) { runWorkerKeepAliveInterval(); } - + if (!this.methodCheck(method)) { + return this.respond(new ProviderError(ResponseMessagePreset['UNKNOWN_METHOD'], ResponseCode.UNKNOWN_METHOD)); + } this.internalCommunicate(method, message); } diff --git a/packages/web-extension-did/app/web/controllers/SWController.ts b/packages/web-extension-did/app/web/controllers/SWController.ts deleted file mode 100644 index 8e0ceba648..0000000000 --- a/packages/web-extension-did/app/web/controllers/SWController.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file - * connectWebAppByTab, disconnectWebAppByTab - */ -import errorHandler from 'utils/errorHandler'; -import { setLocalStorage } from 'utils/storage/chromeStorage'; -import { getConnections } from 'utils/storage/storage.utils'; - -export default class SWController { - /** - * Establish a connection and cache data when the page is initialized - * Distinguish multi-window corresponding to unified URL by tabId - */ - static async connectWebAppByTab(sender: chrome.runtime.MessageSender) { - if (!sender.url || !sender?.tab?.id) throw errorHandler(600001); - const _origin = new URL(sender.url).origin; - const key = _origin; - const connections = await getConnections(); - if (!connections[key]) { - connections[key] = { - tabs: [], - permission: {}, - } as any; - } - const tabs = connections[key].tabs; - const isHasTab = tabs.includes(sender.tab.id); - if (isHasTab) return; - tabs.push(sender.tab.id); - - connections[key] = { - ...connections[key], - id: sender.id, - origin: sender.origin, - permission: connections[key].permission, - tabs, - }; - return setLocalStorage({ - connections, - }); - } - - /** - * When the tab is closed and there is no authorization information, remove the corresponding data in the cache - */ - static async disconnectWebAppByTab(tabId?: number) { - if (!tabId) return; - const connections = await getConnections(); - if (!connections) { - return; - } - Object.entries(connections).forEach(([key, v]) => { - connections[key].tabs = v.tabs.filter((id) => tabId !== id); - if (connections[key].tabs.length === 0 && Object.keys(connections[key].permission).length === 0) - delete connections[key]; - }); - return setLocalStorage({ - connections, - }); - } -} diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.ts b/packages/web-extension-did/app/web/controllers/SWEventController.ts index 46beebe875..00ba67bf7f 100644 --- a/packages/web-extension-did/app/web/controllers/SWEventController.ts +++ b/packages/web-extension-did/app/web/controllers/SWEventController.ts @@ -1,99 +1,109 @@ /** * @file * The controller that handles the event - * chainChanged, accountsChanged, lockStateChanged + * chainChanged, accountsChanged, networkChanged, disconnected */ -import { BaseChainType } from '@portkey-wallet/types/chain'; -import { AccountType } from '@portkey-wallet/types/wallet'; -import { NOTIFICATION_NAMES } from 'messages/InternalMessageTypes'; import { SendResponseFun } from 'types'; +import { apis } from 'utils/BrowserApis'; +import { getConnections } from 'utils/storage/storage.utils'; +import { + ChainIds, + Accounts, + DappEvents, + ConnectInfo, + IResponseType, + ResponseCode, + ProviderErrorType, +} from '@portkey/provider-types'; +import { NetworkType } from '@portkey-wallet/types'; +import { isNotificationEvents } from '@portkey/providers'; import errorHandler from 'utils/errorHandler'; import { setLocalStorage } from 'utils/storage/chromeStorage'; +import { ConnectionsItem } from 'types/storage'; -let _isLocked: boolean; +export interface DappEventPack { + eventName: T; + data?: D; + callback?: SendResponseFun; + origin?: string; +} export default class SWEventController { - static async chainChanged(payload: BaseChainType, sendResponse: SendResponseFun) { - sendResponse({ - ...errorHandler(0), - data: payload, - }); + public static async registerOperator(sender: chrome.runtime.MessageSender) { + if (!sender.url || !sender?.tab?.id) throw errorHandler(600001); + const key = new URL(sender.url).origin; + const connections = await getConnections(); + if (!connections[key]) { + connections[key] = { + tabs: [], + }; + } + const tabs = connections[key].tabs; + const isHasTab = tabs.includes(sender.tab.id); + if (isHasTab) return; + tabs.push(sender.tab.id); - const data = { - chainId: payload.chainId, - chainType: payload.chainType, - rpcUrl: payload.rpcUrl, - blockExplorerURL: payload.blockExplorerURL, - nativeCurrency: payload.nativeCurrency, + connections[key] = { + ...connections[key], + id: sender.id, + origin: sender.origin, + tabs, }; - this._sendMessage('CHAIN_CHANGED', data); - } - - static async accountsChanged(payload: AccountType | undefined, sendResponse: SendResponseFun) { - sendResponse({ - ...errorHandler(0), - data: payload, + return setLocalStorage({ + connections, }); - const data = payload - ? [ - { - accountName: payload.accountName, - address: payload.address, - }, - ] - : []; - this._sendMessage('ACCOUNTS_CHANGED', data); } - // TODO - static async onDisconnect(data: any, sendResponse?: SendResponseFun) { - this._sendMessage('DISCONNECT', data, sendResponse); - } - - static async lockStateChanged(isLocked: boolean, sendResponse?: SendResponseFun) { - if (_isLocked === isLocked) return; - setLocalStorage({ - locked: isLocked, - }); - sendResponse?.({ - ...errorHandler(0), - data: { - isLocked, - }, + public static async unregisterOperator(tabId?: number) { + if (!tabId) return; + const connections = await getConnections(); + if (!connections) { + return; + } + Object.entries(connections).forEach(([key, v]) => { + connections[key].tabs = v.tabs.filter((id) => tabId !== id); + if (connections[key].tabs.length === 0) delete connections[key]; }); - this._sendMessage('UNLOCK_STATE_CHANGED', { - isLocked, + return setLocalStorage({ + connections, }); } - static async _sendMessage( - method: keyof typeof NOTIFICATION_NAMES, - data: any, - responseCallback?: (response: any) => void, - ) { - // TODO - console.log('_sendMessage', method, data); - responseCallback; - // const connections: ConnectionsType = (await getLocalStorage('connections')) ?? {}; - // Object.values(connections).forEach((connection) => { - // // const tabId = sender?.tab?.id; - // const tabs = connection.tabs; - // tabs?.forEach((tabId) => { - // if (!tabId) return; - // apis.tabs.sendMessage( - // tabId, - // { - // method: NOTIFICATION_NAMES[method], - // data, - // tabId, - // }, - // (res) => { - // const { lastError } = apis.runtime; - // responseCallback?.(lastError ? lastError : res); - // // lastError && console.log(lastError, tabId, '_sendMessage=='); - // }, - // ); - // }); - // }); + public static checkEventMethod(eventName: string): boolean { + return isNotificationEvents(eventName); + } + public static dispatchEvent(params: DappEventPack): void; + public static dispatchEvent(params: DappEventPack<'chainChanged', ChainIds>): void; + public static dispatchEvent(params: DappEventPack<'accountsChanged', Accounts>): void; + public static dispatchEvent(params: DappEventPack<'networkChanged', NetworkType>): void; + public static dispatchEvent(params: DappEventPack<'connected', ConnectInfo>): void; + public static dispatchEvent(params: DappEventPack<'disconnected', ProviderErrorType>): void; + static async dispatchEvent({ eventName, data, callback, origin }: DappEventPack) { + const connections = await getConnections(); + let connectionList: ConnectionsItem[]; + if (origin && origin !== '*' && connections[origin]) { + connectionList = [connections[origin]]; + } else { + connectionList = Object.values(connections); + } + if (!connectionList) return; + connectionList.forEach((connection) => { + const tabs = connection.tabs; + tabs?.forEach((tabId) => { + if (!tabId) return; + const event: IResponseType = { + eventName, + info: { + code: ResponseCode.SUCCESS, + data, + }, + origin, + }; + apis.tabs.sendMessage(tabId, event, (res) => { + const { lastError } = apis.runtime; + callback?.(lastError ? lastError : res); + }); + }); + }); } } diff --git a/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts b/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts index 7d9cf46dac..e3b8961e2b 100644 --- a/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts +++ b/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts @@ -1,14 +1,5 @@ import walletMessage from './walletMessage'; -export const NOTIFICATION_NAMES = { - ACCOUNTS_CHANGED: 'portkey_accountsChanged', - UNLOCK_STATE_CHANGED: 'portkey_unlockStateChanged', - CHAIN_CHANGED: 'portkey_chainChanged', - DISCONNECT: 'portkey_disconnect', - // TODO - MESSAGE: 'portkey_message', -}; - export const PromptRouteTypes = { UNLOCK_WALLET: 'UNLOCK_WALLET', BLANK_PAGE: 'BLANK_PAGE', @@ -31,6 +22,8 @@ export const PromptRouteTypes = { } as const; export const WalletMessageTypes = walletMessage; +export type WalletMessageType = typeof walletMessage[keyof typeof walletMessage]; + export const MethodMessageTypes = { GET_WALLET_STATE: 'wallet_getState', }; diff --git a/packages/web-extension-did/app/web/messages/utils.ts b/packages/web-extension-did/app/web/messages/utils.ts new file mode 100644 index 0000000000..08cbb1d3f6 --- /dev/null +++ b/packages/web-extension-did/app/web/messages/utils.ts @@ -0,0 +1,6 @@ +import { WalletMessageType } from './InternalMessageTypes'; +import walletMessage from './walletMessage'; + +export function isMethodsWalletMessage(method: string): method is WalletMessageType { + return Object.values(walletMessage).indexOf(method as any) !== -1; +} diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 7a3fb7a56b..aaf8c03d88 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -1,7 +1,7 @@ -import { getAllStorageLocalData, getLocalStorage } from 'utils/storage/chromeStorage'; +import { getAllStorageLocalData, getLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; import storage from 'utils/storage/storage'; import { AutoLockDataKey, AutoLockDataType, DefaultLock } from 'constants/lock'; -import SWEventController from 'controllers/SWEventController'; +import SWEventController, { DappEventPack } from 'controllers/SWEventController'; import PermissionController from 'controllers/PermissionController'; import NotificationService, { CloseParams } from 'service/NotificationService'; import ApprovalController from 'controllers/approval/ApprovalController'; @@ -130,7 +130,11 @@ export default class ServiceWorkerInstantiate { */ dispenseMessage(sendResponse: SendResponseFun, message: InternalMessageData) { console.log('dispenseMessage: ', message); - + if (SWEventController.checkEventMethod(message.type)) { + const data: InternalMessageData> = message; + SWEventController.dispatchEvent({ ...data.payload, callback: sendResponse }); + return; + } switch (message.type) { case PortkeyMessageTypes.GET_SEED: ServiceWorkerInstantiate.getSeed(sendResponse); @@ -393,20 +397,6 @@ export default class ServiceWorkerInstantiate { return; } if (seed) { - // const lastTime = await getLocalStorage('lastMessageTime'); - // const timeLock = moment().isSameOrAfter(lastTime); - // setLocalStorage({ - // [storage.lastMessageTime]: moment().add(pageState.lockTime, 'm').format(), - // }); - // console.log( - // timeLock, - // lastTime, - // pageState.lockTime, - // moment().format(), - // moment().add(pageState.lockTime, 'm').format(), - // 'timeLock==', - // ); - // lastTime && timeLock && ServiceWorkerInstantiate.lockWallet(sendResponse, 'timingLock'); // MV2 -> MV3 setTimeout -> alarms.create apis.alarms.create('timingLock', { delayInMinutes: pageState.lockTime ?? AutoLockDataType.OneHour, @@ -427,7 +417,17 @@ export default class ServiceWorkerInstantiate { if (seed) { console.log('lockWallet', message); seed = null; - SWEventController.lockStateChanged(true, sendResponse); + SWEventController.dispatchEvent({ + eventName: 'disconnected', + data: { + code: 1000, + message: 'locked', + }, + callback: sendResponse, + }); + setLocalStorage({ + locked: true, + }); } } catch (e) { sendResponse?.(errorHandler(500001, e)); @@ -436,7 +436,9 @@ export default class ServiceWorkerInstantiate { static unlockWallet(sendResponse: SendResponseFun, _seed: string | null) { if (!_seed) return sendResponse(errorHandler(500001, 'unlockWallet error')); - SWEventController.lockStateChanged(false, sendResponse); + setLocalStorage({ + locked: false, + }); } static async checkRegisterStatus() { diff --git a/packages/web-extension-did/app/web/serviceWorker/connectListener.ts b/packages/web-extension-did/app/web/serviceWorker/connectListener.ts index 5c9d1b3c53..63fe82fb04 100644 --- a/packages/web-extension-did/app/web/serviceWorker/connectListener.ts +++ b/packages/web-extension-did/app/web/serviceWorker/connectListener.ts @@ -4,7 +4,7 @@ */ import { ENVIRONMENT_TYPE_SERVICE_WORKER } from 'constants/envType'; import { ACK_KEEP_ALIVE_MESSAGE, WORKER_KEEP_ALIVE_MESSAGE } from 'constants/index'; -import SWController from 'controllers/SWController'; +import SWEventController from 'controllers/SWEventController'; import { getEnvironmentType } from 'utils'; import { apis } from 'utils/BrowserApis'; import errorHandler from 'utils/errorHandler'; @@ -43,7 +43,7 @@ export default function connectListener() { } const isContentConnect = portType === ENVIRONMENT_TYPE_SERVICE_WORKER; if (isContentConnect) { - SWController.connectWebAppByTab(port.sender); + SWEventController.registerOperator(port.sender); // console.log(port, 'connectListener===port'); // if (port.name !== WORKER_KEEP_ALIVE_MESSAGE) return; diff --git a/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts b/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts index a479c27a5e..f46cab4d5e 100644 --- a/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts +++ b/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts @@ -3,10 +3,10 @@ * Listen for onInstalled, storage.onChanged, tabs.onRemoved, runtime.onConnect */ import { AutoLockDataKey, AutoLockDataType } from 'constants/lock'; -import SWController from 'controllers/SWController'; import { apis } from 'utils/BrowserApis'; import storage from 'utils/storage/storage'; import connectListener from './connectListener'; +import SWEventController from 'controllers/SWEventController'; interface ListenerHandler { pageStateChange: (pageStateChanges: any) => void; @@ -19,9 +19,6 @@ const serviceWorkerListener = ({ pageStateChange, checkRegisterStatus, checkTimi apis.runtime.onInstalled.addListener(async ({ reason }) => { checkRegisterStatus(); console.log('reason', reason); - // if (reason === chrome.runtime.OnInstalledReason.UPDATE) { - // SWEventController.onDisconnect(errorHandler(600002) ); - // } }); // eslint-disable-next-line @typescript-eslint/no-unused-vars apis.storage.onChanged.addListener((changes) => { @@ -36,16 +33,12 @@ const serviceWorkerListener = ({ pageStateChange, checkRegisterStatus, checkTimi pageStateChange({ lockTime: AutoLockDataType[changes[storage.lockTime].newValue as AutoLockDataKey], }); - } else if (storage.reduxStorageWallet in changes) { - // const { newValue = '{}', oldValue = '{}' } = changes[storage.reduxStorageWallet] ?? {}; - // const { wallet: newWallet = '{}' } = JSON.parse(newValue); - // walletInfo = JSON.parse(JSON.parse(walletStorage).walletInfo); } }); connectListener(); apis.tabs.onRemoved.addListener((tabId) => { - SWController.disconnectWebAppByTab(tabId); + SWEventController.unregisterOperator(tabId); }); }; export default serviceWorkerListener; diff --git a/packages/web-extension-did/app/web/types/SW.ts b/packages/web-extension-did/app/web/types/SW.ts index f5be132798..82cbbb896c 100644 --- a/packages/web-extension-did/app/web/types/SW.ts +++ b/packages/web-extension-did/app/web/types/SW.ts @@ -20,9 +20,9 @@ export interface InternalMessagePayload extends BaseInternalMessagePayload { params: any; } -export interface InternalMessageData { +export interface InternalMessageData { type: string; - payload: any; + payload: T; } export interface BaseRequestMessagePayload { diff --git a/packages/web-extension-did/app/web/types/storage.ts b/packages/web-extension-did/app/web/types/storage.ts index 25b4daf2dd..4b29b01f7e 100644 --- a/packages/web-extension-did/app/web/types/storage.ts +++ b/packages/web-extension-did/app/web/types/storage.ts @@ -13,11 +13,6 @@ export interface ContractsItem { }; } -export interface ConnectPermissionType { - accountList?: string[]; - contracts?: ContractsItem; -} - type origin = string; export interface ConnectTabItem { @@ -29,7 +24,6 @@ export interface ConnectTabItem { export interface ConnectionsItem { id?: string; origin?: string; - permission: ConnectPermissionType; tabs: number[]; } diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index cdc3c5da8e..a0bd71b034 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,8 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.10", - "@portkey/provider-utils": "0.0.1-alpha.10", + "@portkey/extension-provider": "0.0.1-alpha.14", + "@portkey/provider-utils": "0.0.1-alpha.14", + "@portkey/provider-types": "0.0.1-alpha.14", + "@portkey/providers": "0.0.1-alpha.14", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index 4ebae60c2b..e12d4104e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5024,24 +5024,31 @@ "@portkey/provider-types" "^0.0.1-alpha.11" aelf-sdk "^3.2.44" -"@portkey/chain@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.8.tgz#20a6d30fe6ceb71c3d430ee898422f0a7a86665d" - integrity sha512-FLdUFprntO76rtH5N0Ef2gJ3moNfWpDwlym92WqcWq15qqjCjbBk/l86FgOkZsfLbzgZggHM1n4b8W62wvz9CQ== +"@portkey/chain@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.14.tgz#5b92d50818f38d8b5b3da442fb720cdd4e879970" + integrity sha512-+hhSd0Qu1Jm/LbSrQ5/zA+Qogyx/dEsercvdCW3ogvfh05C5gDzrhKFX3Q0WvvWYwKZugHFbReeMFrXqyX8quQ== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.14" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.8.tgz#c311230f553b615378093933ee7925e1607ed169" - integrity sha512-93RqrJK2CyCKCwqlV+CFd9ixkyzGAl9yViTNoag/HjR2uNJd7dGo71HnJmDC/ijUokR+ZmnVtiJsNP+h2/DueQ== +"@portkey/extension-provider@0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.14.tgz#bf839b2b33d300e7815dca07575861f870c8d4df" + integrity sha512-3ht27XYWfUxGve4vrPGw1tTQ0T99Y/zgZAhDd9uQs7h+ctrwibZ1wX8NA4zZGFvrD4DTja3kfUIeZkj1SnBK3g== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/providers" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.14" + "@portkey/providers" "^0.0.1-alpha.14" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" +"@portkey/provider-types@0.0.1-alpha.14", "@portkey/provider-types@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.14.tgz#7608c7f8825595a70e3db416db627939d9aed0e3" + integrity sha512-7OELNO9uyvvi92lU0dFtR6sQjSevMTii1kw1xos86MRhMndNBKAL315rkM29D1XQnL/0rdxjzEquKl3Kpre0mw== + dependencies: + "@types/readable-stream" "^2.3.15" + "@portkey/provider-types@^0.0.1-alpha.11": version "0.0.1-alpha.11" resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.11.tgz#c9a8a4ed006385eb1527d23ec1aab0d11695d1ff" @@ -5049,19 +5056,12 @@ dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-types@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.8.tgz#bb4996ec275d23fa1272b828b89f2043ec6a8337" - integrity sha512-qAELo9JKWCC7QWn/rCtwwxx8gokKv7ybeknn25EM5REZu80GK7Zg7KhRTgMlky+NyOQBNqyuc+1dl19S+RwaJA== - dependencies: - "@types/readable-stream" "^2.3.15" - -"@portkey/provider-utils@0.0.1-alpha.8", "@portkey/provider-utils@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.8.tgz#f380927c43f4c718e6d7ede438cf6ad48ecaa5f8" - integrity sha512-p+coX6yBr7bEv7BSk4vKjaKIGftBHhEbWBcRMVb1NZ5b6UWwyiy3rfRgzwm4IRc5D/Qvqquyam/FLyHCfrgh6Q== +"@portkey/provider-utils@0.0.1-alpha.14", "@portkey/provider-utils@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.14.tgz#9bf6de253c43e3e06ce487a8bc02f1119d755bdc" + integrity sha512-NyMlvnpABc8cINxXf72quVrXtic3YpSG/U5T4PlFJRjGqLGFiVZ8xyIcONqxpA0DilqNZEMonmp72LZfEEZh9g== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.8" + "@portkey/provider-types" "^0.0.1-alpha.14" "@portkey/provider-utils@^0.0.1-alpha.11": version "0.0.1-alpha.11" @@ -5070,6 +5070,20 @@ dependencies: "@portkey/provider-types" "^0.0.1-alpha.11" +"@portkey/providers@0.0.1-alpha.14", "@portkey/providers@^0.0.1-alpha.14": + version "0.0.1-alpha.14" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.14.tgz#4c15939c24610a1d99920750f3b6fdc933408869" + integrity sha512-wvjVsvj4D9Xjgh4/HoN8sKWx5B5ePYRsyu69zOMDxFvL0K7+9xlFedQCBcXpKwjdZ6JvG9Kbc1b9XGrk6OR2lw== + dependencies: + "@metamask/object-multiplex" "^1.1.0" + "@portkey/chain" "^0.0.1-alpha.14" + "@portkey/provider-types" "^0.0.1-alpha.14" + "@portkey/provider-utils" "^0.0.1-alpha.14" + "@types/readable-stream" "^2.3.15" + lodash "^4.17.21" + pump "^3.0.0" + readable-stream "^4.4.0" + "@portkey/providers@^0.0.1-alpha.11": version "0.0.1-alpha.11" resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.11.tgz#2deaf83e9aeed9dfed36209103ad22be0e325f8c" @@ -5083,19 +5097,6 @@ pump "^3.0.0" readable-stream "^4.4.0" -"@portkey/providers@^0.0.1-alpha.8": - version "0.0.1-alpha.8" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.8.tgz#c94228aee7f683d8297231f92dda1a28208e733c" - integrity sha512-L6wtRoURDngJfRj0ruz9TOGwj5TNP1prQTaEvY8WYdhnH/sAXef2U6uxLDPfBD03ot1kZ65d3/dg1nSc4HJP7w== - dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.8" - "@portkey/provider-types" "^0.0.1-alpha.8" - "@portkey/provider-utils" "^0.0.1-alpha.8" - "@types/readable-stream" "^2.3.15" - pump "^3.0.0" - readable-stream "^4.4.0" - "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From 75c21c3994de03e1507db347c66a525658695dcf Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Tue, 6 Jun 2023 18:16:41 +0800 Subject: [PATCH 034/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dispatch=20event?= =?UTF-8?q?=20to=20service=20worker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/dapp/type.ts | 2 - .../app/web/controllers/SWEventController.ts | 67 ++++++++++++++++--- .../app/web/messages/InternalMessage.ts | 2 +- .../serviceWorker/ServiceWorkerInstantiate.ts | 8 ++- .../app/web/serviceWorker/connectListener.ts | 25 +------ .../app/web/store/Provider/store.ts | 4 ++ 6 files changed, 69 insertions(+), 39 deletions(-) diff --git a/packages/store/store-ca/dapp/type.ts b/packages/store/store-ca/dapp/type.ts index c5286e1f30..f888c6deee 100644 --- a/packages/store/store-ca/dapp/type.ts +++ b/packages/store/store-ca/dapp/type.ts @@ -4,8 +4,6 @@ export type DappStoreItem = { origin: string; name?: string; icon?: string; - // Record the tabId of the dapp after opening the link - tadIds?: string[]; }; export interface IDappStoreState { diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.ts b/packages/web-extension-did/app/web/controllers/SWEventController.ts index 00ba67bf7f..45483030bf 100644 --- a/packages/web-extension-did/app/web/controllers/SWEventController.ts +++ b/packages/web-extension-did/app/web/controllers/SWEventController.ts @@ -1,5 +1,5 @@ /** - * @file + * @remarks * The controller that handles the event * chainChanged, accountsChanged, networkChanged, disconnected */ @@ -14,12 +14,18 @@ import { IResponseType, ResponseCode, ProviderErrorType, + NotificationEvents, } from '@portkey/provider-types'; import { NetworkType } from '@portkey-wallet/types'; import { isNotificationEvents } from '@portkey/providers'; import errorHandler from 'utils/errorHandler'; import { setLocalStorage } from 'utils/storage/chromeStorage'; import { ConnectionsItem } from 'types/storage'; +import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; +import { getDappState, getWalletState } from 'utils/lib/SWGetReduxStore'; +import InternalMessage from 'messages/InternalMessage'; +import { handleAccounts, handleChainIds } from '@portkey-wallet/utils/dapp'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; export interface DappEventPack { eventName: T; @@ -29,6 +35,7 @@ export interface DappEventPack { } export default class SWEventController { + /** Add dapps tadId */ public static async registerOperator(sender: chrome.runtime.MessageSender) { if (!sender.url || !sender?.tab?.id) throw errorHandler(600001); const key = new URL(sender.url).origin; @@ -39,8 +46,7 @@ export default class SWEventController { }; } const tabs = connections[key].tabs; - const isHasTab = tabs.includes(sender.tab.id); - if (isHasTab) return; + if (tabs.includes(sender.tab.id)) return; tabs.push(sender.tab.id); connections[key] = { @@ -53,13 +59,11 @@ export default class SWEventController { connections, }); } - + /** Remove dapps tadId */ public static async unregisterOperator(tabId?: number) { if (!tabId) return; const connections = await getConnections(); - if (!connections) { - return; - } + if (!connections) return; Object.entries(connections).forEach(([key, v]) => { connections[key].tabs = v.tabs.filter((id) => tabId !== id); if (connections[key].tabs.length === 0) delete connections[key]; @@ -72,6 +76,16 @@ export default class SWEventController { public static checkEventMethod(eventName: string): boolean { return isNotificationEvents(eventName); } + + public static checkDispatchEventParams(data: DappEventPack): boolean { + if (!data) return false; + return true; + } + + public static check(eventName: string, data: any): boolean { + return SWEventController.checkEventMethod(eventName) && SWEventController.checkDispatchEventParams(data); + } + public static dispatchEvent(params: DappEventPack): void; public static dispatchEvent(params: DappEventPack<'chainChanged', ChainIds>): void; public static dispatchEvent(params: DappEventPack<'accountsChanged', Accounts>): void; @@ -80,9 +94,18 @@ export default class SWEventController { public static dispatchEvent(params: DappEventPack<'disconnected', ProviderErrorType>): void; static async dispatchEvent({ eventName, data, callback, origin }: DappEventPack) { const connections = await getConnections(); - let connectionList: ConnectionsItem[]; + let connectionList: ConnectionsItem[] = []; if (origin && origin !== '*' && connections[origin]) { - connectionList = [connections[origin]]; + /** Send an event to the specified origin */ + connectionList.push(connections[origin]); + } else if (eventName === 'accountsChanged') { + /** Only send events to connected dapps */ + const { currentNetwork } = await getWalletState(); + const { dappMap } = await getDappState(); + const connectDappList = dappMap[currentNetwork]; + connectDappList?.forEach((dapp) => { + if (connections[dapp.origin]) connectionList.push(connections[dapp.origin]); + }); } else { connectionList = Object.values(connections); } @@ -101,9 +124,35 @@ export default class SWEventController { }; apis.tabs.sendMessage(tabId, event, (res) => { const { lastError } = apis.runtime; + if (lastError) SWEventController.unregisterOperator(tabId); callback?.(lastError ? lastError : res); }); }); }); } + // Trigger events based on user operations to notify service workers + public static async emit(action: string, payload: any) { + switch (action) { + case changeNetworkType.toString(): { + const { currentNetwork } = await getWalletState(); + InternalMessage.payload(NotificationEvents.NETWORK_CHANGED, { data: currentNetwork }).send(); + break; + } + case setCAInfo.toString(): { + const wallet = await getWalletState(); + await InternalMessage.payload(NotificationEvents.ACCOUNTS_CHANGED, { data: handleAccounts(wallet) }).send(); + await InternalMessage.payload(NotificationEvents.CHAIN_CHANGED, { data: handleChainIds(wallet) }).send(); + break; + } + case removeDapp.toString(): { + if (payload.origin) { + await InternalMessage.payload(NotificationEvents.DISCONNECTED, { + data: { message: 'user disconnected', code: ResponseCode.USER_DENIED }, + origin: payload.origin, + }).send(); + } + break; + } + } + } } diff --git a/packages/web-extension-did/app/web/messages/InternalMessage.ts b/packages/web-extension-did/app/web/messages/InternalMessage.ts index fb36981e2c..7e82af77f4 100644 --- a/packages/web-extension-did/app/web/messages/InternalMessage.ts +++ b/packages/web-extension-did/app/web/messages/InternalMessage.ts @@ -27,7 +27,7 @@ export default class InternalMessage { return Object.assign(this.placeholder(), json); } - static payload(type: string | number, payload?: any) { + static payload(type: string | number, payload?: T) { const p = this.placeholder(); p.type = type; p.payload = payload; diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index d232c1c48a..b8bd78e0f2 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -131,9 +131,11 @@ export default class ServiceWorkerInstantiate { dispenseMessage(sendResponse: SendResponseFun, message: InternalMessageData) { console.log('dispenseMessage: ', message); // process events - if (SWEventController.checkEventMethod(message.type)) { - const data: InternalMessageData> = message; - SWEventController.dispatchEvent({ ...data.payload, callback: sendResponse }); + if (SWEventController.check(message.type, message.payload?.data)) { + const payload = message.payload; + const data: Omit = payload.data; + const origin = payload.origin; + SWEventController.dispatchEvent({ ...data, origin, callback: sendResponse }); return; } switch (message.type) { diff --git a/packages/web-extension-did/app/web/serviceWorker/connectListener.ts b/packages/web-extension-did/app/web/serviceWorker/connectListener.ts index 63fe82fb04..86e257ea78 100644 --- a/packages/web-extension-did/app/web/serviceWorker/connectListener.ts +++ b/packages/web-extension-did/app/web/serviceWorker/connectListener.ts @@ -11,22 +11,6 @@ import errorHandler from 'utils/errorHandler'; import popupHandler from 'utils/popupHandler'; import { getLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; -// function onMessage(msg: any, port: any) { -// console.log('received', msg, 'from', port.sender); -// } - -// function deleteTimer(port: any) { -// if (port._timer) { -// clearTimeout(port._timer); -// delete port._timer; -// } -// } -// function forceReconnect(port: any) { -// deleteTimer(port); -// console.log('keepSWActive=forceReconnect==='); -// port.disconnect(); -// } - export default function connectListener() { apis.runtime.onConnect.addListener(async (port: any) => { const portType = getEnvironmentType(port.sender.url); @@ -44,21 +28,14 @@ export default function connectListener() { const isContentConnect = portType === ENVIRONMENT_TYPE_SERVICE_WORKER; if (isContentConnect) { SWEventController.registerOperator(port.sender); - // console.log(port, 'connectListener===port'); - // if (port.name !== WORKER_KEEP_ALIVE_MESSAGE) return; - - // port.onMessage.addListener(onMessage); - // port.onDisconnect.addListener(deleteTimer); - // port._timer = setTimeout(forceReconnect, TIME_45_MIN_IN_MS, port); } - // sendReadyMessageToTabs(); // use setInterval if (!isContentConnect) port.onMessage.addListener((message: chrome.runtime.Port) => { // If we get a WORKER_KEEP_ALIVE message, we respond with an ACK if (message.name === WORKER_KEEP_ALIVE_MESSAGE) { - // To test un-comment this line and wait for 1 minute. An error should be shown on MetaMask UI. + // To test un-comment this line and wait for 1 minute. An error should be reload extension port.postMessage({ name: ACK_KEEP_ALIVE_MESSAGE }); } }); diff --git a/packages/web-extension-did/app/web/store/Provider/store.ts b/packages/web-extension-did/app/web/store/Provider/store.ts index b69f4e501d..f134986b7c 100644 --- a/packages/web-extension-did/app/web/store/Provider/store.ts +++ b/packages/web-extension-did/app/web/store/Provider/store.ts @@ -3,6 +3,7 @@ import { persistReducer } from 'redux-persist'; import storeConfig from './config'; import rootReducer from './rootReducer'; import { rateApi } from '@portkey-wallet/store/rate/api'; +import { DappMiddle } from '@portkey-wallet/utils/dapp/middle'; export const persistedReducer = persistReducer(storeConfig.reduxPersistConfig as any, rootReducer); @@ -10,6 +11,9 @@ const middlewareList: any[] = []; middlewareList.push(rateApi.middleware); +// dapp middle +middlewareList.push(DappMiddle.middle); + export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => From bca45aea7b99119617473c87eedf13b3dff3d659 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 6 Jun 2023 19:00:33 +0800 Subject: [PATCH 035/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20connect=20&=20tr?= =?UTF-8?q?ansfer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../methodController/AELFMethodController.ts | 6 +- .../app/web/pages/ConnectWallet/index.less | 84 ++++++++++ .../app/web/pages/ConnectWallet/index.tsx | 81 +++++++++- .../app/web/pages/SendTransactions/index.less | 148 ++++++++++++++++++ .../app/web/pages/SendTransactions/index.tsx | 119 ++++++++++++-- 5 files changed, 420 insertions(+), 18 deletions(-) create mode 100644 packages/web-extension-did/app/web/pages/ConnectWallet/index.less create mode 100644 packages/web-extension-did/app/web/pages/SendTransactions/index.less diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 65b2e9b49c..4fb8775509 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -131,11 +131,7 @@ export default class AELFMethodController { requestAccounts: RequestCommonHandler = async (sendResponse, message) => { const isActive = await this.dappManager.isActive(message.origin); if (isActive) return sendResponse({ ...errorHandler(0), data: await this.dappManager.accounts(message.origin) }); - const result = await this.approvalController.authorizedToConnect({ - appName: 'appName', - appLogo: 'appName', - origin, - }); + const result = await this.approvalController.authorizedToConnect(message); if (result.error === 200003) return sendResponse({ ...errorHandler(200003), diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less new file mode 100644 index 0000000000..fc90143e5d --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less @@ -0,0 +1,84 @@ +@import '../../assets/theme/color.less'; + +.connect-wallet { + margin: 40px auto; + width: 328px; + flex-direction: column; + align-items: center; + .site { + margin-bottom: 24px; + width: fit-content; + padding: 8px 16px; + border: 1px solid @border-2; + border-radius: 24px; + font-size: 14px; + line-height: 20px; + color: @font-13; + .icon { + width: 24px; + border-radius: 50%; + margin-right: 8px; + } + } + .title { + margin-bottom: 16px; + font-size: 24px; + line-height: 28px; + font-weight: 700; + color: @font-11; + } + .allow { + width: 100%; + margin-bottom: 40px; + font-size: 12px; + line-height: 16px; + color: @font-13; + border: 1px solid @border-2; + border-radius: 6px; + .item { + padding-top: 16px; + margin: 0 16px 16px; + border-top: 1px solid @border-2; + .allow-title { + align-items: center; + font-size: 14px; + line-height: 20px; + font-weight: 500; + color: @font-11; + .tickfilled-icon { + width: 14px; + height: 14px; + margin-left: 1px; + margin-right: 9px; + } + } + .allow-text { + margin-top: 4px; + margin-left: 24px; + } + &:first-child { + border-top: none; + } + } + } + .btn{ + width: 100%; + .@{app-prefix}-btn { + width: 156px; + height: 48px; + font-size: 14px; + line-height: 22px; + text-align: center; + border-radius: 24px; + } + .@{app-prefix}-btn-text { + border: 1px solid @border-3; + color: @font-9; + background-color: @bg-11; + } + .@{app-prefix}-btn-primary { + color: @font-5; + background-color: @bg-7; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index f717816e28..b6898c7521 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -1,6 +1,85 @@ +import { addDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { Button } from 'antd'; +import CustomSvg from 'components/CustomSvg'; +import ImgLoading from 'components/ImgLoading'; import usePromptSearch from 'hooks/usePromptSearch'; +import { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; +import errorHandler from 'utils/errorHandler'; +import { closePrompt } from 'utils/lib/serviceWorkerAction'; +import './index.less'; + +const allowItem = [ + 'viewing wallet balance and activity Allow', + 'sending requests for transactions', + 'moving funds without your permission', +]; export default function ConnectWallet() { const detail = usePromptSearch(); - return
    {JSON.stringify(detail)}
    ; + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const { currentNetwork } = useWalletInfo(); + + const renderSite = useMemo( + () => + detail && ( +
    + + {detail.appHref} +
    + ), + [detail], + ); + + const renderAllow = useMemo( + () => ( +
    + {allowItem.map((item) => ( +
    +
    + + {t('Allow')} +
    +
    {item}
    +
    + ))} +
    + ), + [t], + ); + + const handleSign = useCallback(() => { + dispatch( + addDapp({ + networkType: currentNetwork, + dapp: detail, + }), + ); + closePrompt({ + ...errorHandler(0), + data: {}, + }); + }, [currentNetwork, detail, dispatch]); + + return ( +
    + {renderSite} +
    {t('Connect with Portkey')}
    + {renderAllow} +
    + + +
    +
    + ); } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.less b/packages/web-extension-did/app/web/pages/SendTransactions/index.less new file mode 100644 index 0000000000..8444784d14 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.less @@ -0,0 +1,148 @@ +@import '../../assets/theme/color.less'; + +.send-transaction { + width: 328px; + margin: 40px auto; + flex-direction: column; + font-size: 12px; + line-height: 16px; + color: @font-11; + .title { + font-size: 14px; + line-height: 20px; + font-weight: 500; + color: @font-11; + } + .chain { + align-self: center; + margin-bottom: 24px; + padding: 8px 16px; + border-radius: 24px; + border: 1px solid @border-2; + .aelf-icon { + width: 24px; + height: 24px; + margin-right: 8px; + } + } + .account { + position: relative; + margin: 0 auto 16px; + padding: 17px 15px; + width: 328px; + border: 1px solid @border-2; + border-radius: 6px; + align-items: center; + gap: 44px; + .address, + .name { + flex: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .oval-icon { + position: absolute; + width: 20px; + height: 20px; + top: 50%; + left: 50%; + transform: translateX(-10px) translateY(-10px); + z-index: 10; + } + .line { + position: absolute; + left: 50%; + height: 100%; + border-left: 1px solid @border-2; + } + } + .method { + margin-bottom: 16px; + color: @font-13; + .title { + margin-bottom: 4px; + } + } + .detail { + color: @font-13; + .title { + margin-bottom: 4px; + } + .amount { + padding: 16px; + margin-bottom: 18px; + border: 1px solid @border-2; + border-radius: 6px; + } + .fee, .total { + margin-bottom: 12px; + .elf { + font-size: 14px; + line-height: 20px; + font-weight: 500; + color: @font-11; + } + .dollar { + margin-left: 4px; + } + } + } + .message-wrapper { + color: @font-13; + margin-bottom: 42px; + .title { + margin-bottom: 4px; + } + .message { + width: 328px; + padding: 16px; + margin-bottom: 18px; + border: 1px solid @border-2; + border-radius: 6px; + .title { + margin-bottom: 4px; + } + .content { + margin-bottom: 16px; + width: 100%; + line-break: anywhere; + white-space: pre-wrap; + &:last-child { + margin-bottom: 0; + } + } + } + .fee { + .elf { + font-size: 14px; + line-height: 20px; + font-weight: 500; + color: @font-11; + } + .dollar { + margin-left: 4px; + } + } + } + .btn { + width: 100%; + .@{app-prefix}-btn { + width: 156px; + height: 48px; + font-size: 14px; + line-height: 22px; + text-align: center; + border-radius: 24px; + } + .@{app-prefix}-btn-text { + border: 1px solid @border-3; + color: @font-9; + background-color: @bg-11; + } + .@{app-prefix}-btn-primary { + color: @font-5; + background-color: @bg-7; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 415676a2a8..411b46cace 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -1,15 +1,22 @@ import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { ChainId } from '@portkey-wallet/types'; -import { handleErrorMessage } from '@portkey-wallet/utils'; +import { ZERO } from '@portkey-wallet/constants/misc'; +import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import { formatAmountShow } from '@portkey-wallet/utils/converter'; +import { formatChainInfoToShow, handleErrorMessage } from '@portkey-wallet/utils'; import aes from '@portkey-wallet/utils/aes'; import { Button, message } from 'antd'; +import CustomSvg from 'components/CustomSvg'; +import { useTranslation } from 'react-i18next'; import usePromptSearch from 'hooks/usePromptSearch'; -import { useCallback } from 'react'; -import { useUserInfo } from 'store/Provider/hooks'; +import { useCallback, useEffect, useMemo } from 'react'; +import { useUserInfo, useWalletInfo } from 'store/Provider/hooks'; import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; import { callSendMethod } from 'utils/sandboxUtil/sendTransactions'; +import { useAmountInUsdShow } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; +import './index.less'; export default function SendTransactions() { const detail = usePromptSearch<{ @@ -17,12 +24,78 @@ export default function SendTransactions() { chainId: ChainId; contractAddress: string; method: string; + isGetSignTx?: 0 | 1; // 0 message 1 detail params: any; }; }>(); const chainInfo = useCurrentChain(detail?.payload?.chainId); const wallet = useCurrentWalletInfo(); + const { walletName } = useWalletInfo(); + const { currentNetwork } = useWalletInfo(); + const isMainnet = useIsMainnet(); + const { t } = useTranslation(); + const { + payload: { params }, + } = detail; const { passwordSeed } = useUserInfo(); + const amountInUsdShow = useAmountInUsdShow(); + + useEffect(() => { + // TODO fee + }, []); + + const renderDetail = useMemo(() => { + const { symbol, amount, decimals } = params.paramsOption; + return ( +
    +
    Details
    +
    +
    Amount
    +
    +
    {`${formatAmountShow(amount)} ELF`}
    + {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    } +
    +
    +
    +
    Transaction Fee
    +
    +
    {`${formatAmountShow(params.paramsOption.fee)} ELF`}
    + {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    } +
    +
    +
    +
    Total (Amount + Transaction Fee)
    +
    +
    {`${formatAmountShow(ZERO.plus(amount).plus(params.paramsOption.fee))} ELF`}
    + {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    } +
    +
    +
    + ); + }, [amountInUsdShow, isMainnet, params.paramsOption]); + const renderMessage = useMemo(() => { + const { messageDetail } = params.paramsOption; + return ( +
    +
    Message
    +
    +
    String to be sign
    +
    {messageDetail.sign}
    +
    Method
    +
    {messageDetail.method}
    +
    Signature
    +
    {messageDetail.signature}
    +
    +
    +
    Transaction Fee
    +
    +
    {`${formatAmountShow(params.paramsOption.fee)} ELF`}
    + {isMainnet &&
    $0.12
    } +
    +
    +
    + ); + }, [isMainnet, params.paramsOption]); const sendHandler = useCallback(async () => { try { @@ -66,15 +139,37 @@ export default function SendTransactions() { }, [chainInfo, detail, wallet, passwordSeed]); return ( -
    - {JSON.stringify(detail)} - - +
    +
    + + {formatChainInfoToShow(detail.payload.chainId, currentNetwork)} +
    +
    +
    {walletName}
    + +
    {`${detail.payload.contractAddress.slice( + 0, + 10, + )}...${detail.payload.contractAddress.slice(-4)}`}
    +
    +
    +
    +
    Method
    +
    {detail.payload.method}
    +
    + {detail.payload.isGetSignTx ? renderMessage : renderDetail} +
    + + +
    ); } From cc8e8daf4092ac8377a5b7e23c09a2d03a55b8d7 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 6 Jun 2023 19:48:36 +0800 Subject: [PATCH 036/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20getSignt?= =?UTF-8?q?x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web-extension-did/app/web/pages/SendTransactions/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 411b46cace..3778d7c761 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -24,7 +24,6 @@ export default function SendTransactions() { chainId: ChainId; contractAddress: string; method: string; - isGetSignTx?: 0 | 1; // 0 message 1 detail params: any; }; }>(); @@ -157,7 +156,7 @@ export default function SendTransactions() {
    Method
    {detail.payload.method}
    - {detail.payload.isGetSignTx ? renderMessage : renderDetail} + {detail.payload.method.toLowerCase() === 'transfer' ? renderDetail : renderMessage}
    +
    + + +
    +
    + + +
    ); } diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx index 01685fa3d9..cf5d11525e 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx @@ -214,6 +214,7 @@ export default function RegisterStart() { message.error(`LoginType:${type} is not support`); } } catch (error) { + setLoading(false); console.log(error, 'error===onSocialSignFinish'); const msg = handleErrorMessage(error); message.error(msg); diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index c842a812c5..4fbeff6fea 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -53,6 +53,8 @@ const permissionWhitelist = [ PortkeyMessageTypes.ACTIVE_LOCK_STATUS, PortkeyMessageTypes.PERMISSION_FINISH, PortkeyMessageTypes.SOCIAL_LOGIN, + PortkeyMessageTypes.OPEN_RECAPTCHA_PAGE, + WalletMessageTypes.SET_RECAPTCHA_CODE_V2, WalletMessageTypes.SOCIAL_LOGIN, MethodsUnimplemented.GET_WALLET_STATE, // The method that requires the dapp not to trigger the lock call diff --git a/packages/web-extension-did/app/web/store/Provider/store.ts b/packages/web-extension-did/app/web/store/Provider/store.ts index 8443afbbbd..0d05965b26 100644 --- a/packages/web-extension-did/app/web/store/Provider/store.ts +++ b/packages/web-extension-did/app/web/store/Provider/store.ts @@ -11,10 +11,9 @@ export const persistedReducer = persistReducer(storeConfig.reduxPersistConfig as const middlewareList: any[] = []; middlewareList.push(rateApi.middleware); - // dapp middle -middlewareList.push(DappMiddle.middle); DappMiddle.registerEvent(SWEventController); +middlewareList.push(DappMiddle.middle); export const store = configureStore({ reducer: persistedReducer, diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index a0bd71b034..094bac3860 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.14", - "@portkey/provider-utils": "0.0.1-alpha.14", - "@portkey/provider-types": "0.0.1-alpha.14", - "@portkey/providers": "0.0.1-alpha.14", + "@portkey/extension-provider": "0.0.1-alpha.17", + "@portkey/provider-utils": "0.0.1-alpha.17", + "@portkey/provider-types": "0.0.1-alpha.17", + "@portkey/providers": "0.0.1-alpha.17", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index e3afc3ae86..20b2b99cc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5024,13 +5024,21 @@ "@portkey/provider-types" "^0.0.1-alpha.14" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.14": - version "0.0.1-alpha.14" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.14.tgz#bf839b2b33d300e7815dca07575861f870c8d4df" - integrity sha512-3ht27XYWfUxGve4vrPGw1tTQ0T99Y/zgZAhDd9uQs7h+ctrwibZ1wX8NA4zZGFvrD4DTja3kfUIeZkj1SnBK3g== +"@portkey/chain@^0.0.1-alpha.17": + version "0.0.1-alpha.17" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.17.tgz#bbb62c56d3ec60fa8571a22a091a7e9c440315a1" + integrity sha512-4eK3JrjsWwRPebG+3Mchq3UmR7QnVpqUj/bsl5h2waxbloLeQIzvM9w7m4rdxMoqRPnu8PpwFmDC1WCPtvQWbQ== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.14" - "@portkey/providers" "^0.0.1-alpha.14" + "@portkey/provider-types" "^0.0.1-alpha.17" + aelf-sdk "^3.2.44" + +"@portkey/extension-provider@0.0.1-alpha.17": + version "0.0.1-alpha.17" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.17.tgz#7bc8bf6446e3a050855be05d345e5ccea94d02e9" + integrity sha512-GVdkjUt1cb2qX7zLyLhwNwG/du2y1ih2pQjmE/3CG36gw+js1AQggufSIXL0dMsTTnssyRs0cvu2f+95tntsqw== + dependencies: + "@portkey/provider-types" "^0.0.1-alpha.17" + "@portkey/providers" "^0.0.1-alpha.17" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" @@ -5039,21 +5047,49 @@ resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.14.tgz#2fec1ad012739ace1c35e9e9907fc8b9ea410dda" integrity sha512-VnZuHzabedRZgaGEV+vseJR6v4c2QSLY8E4SFov1htdNz5Agn7rybkcPpd50P8IF9hKeLwEADqf7nrZCtxLoUA== -"@portkey/provider-types@0.0.1-alpha.14", "@portkey/provider-types@^0.0.1-alpha.13", "@portkey/provider-types@^0.0.1-alpha.14": +"@portkey/provider-types@0.0.1-alpha.17", "@portkey/provider-types@^0.0.1-alpha.17": + version "0.0.1-alpha.17" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.17.tgz#118ff7e138276a2fb12cbfd379fbf923615a1a73" + integrity sha512-KAQT2s5OEGpAy8A/LWBdETGuWFeefBNyrbcvPQKFm2QhPG6zIcC9Sgjj/g/PA1/VIsFDn7B8Q7S+ZM6TRlKT4g== + dependencies: + "@types/readable-stream" "^2.3.15" + +"@portkey/provider-types@^0.0.1-alpha.13", "@portkey/provider-types@^0.0.1-alpha.14": version "0.0.1-alpha.14" resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.14.tgz#7608c7f8825595a70e3db416db627939d9aed0e3" integrity sha512-7OELNO9uyvvi92lU0dFtR6sQjSevMTii1kw1xos86MRhMndNBKAL315rkM29D1XQnL/0rdxjzEquKl3Kpre0mw== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.14", "@portkey/provider-utils@^0.0.1-alpha.13", "@portkey/provider-utils@^0.0.1-alpha.14": +"@portkey/provider-utils@0.0.1-alpha.17", "@portkey/provider-utils@^0.0.1-alpha.17": + version "0.0.1-alpha.17" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.17.tgz#77c985546784da27fffa63a3e1af5729697ca56e" + integrity sha512-LYdFUhd/iHkJ3cbajZBZf6U/ANDo3JePAyzu+1g7EyJa0atuU5LE0mLqc8bIBF/z5KQj39hPziq0VvtjJnazXQ== + dependencies: + "@portkey/provider-types" "^0.0.1-alpha.17" + +"@portkey/provider-utils@^0.0.1-alpha.13", "@portkey/provider-utils@^0.0.1-alpha.14": version "0.0.1-alpha.14" resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.14.tgz#9bf6de253c43e3e06ce487a8bc02f1119d755bdc" integrity sha512-NyMlvnpABc8cINxXf72quVrXtic3YpSG/U5T4PlFJRjGqLGFiVZ8xyIcONqxpA0DilqNZEMonmp72LZfEEZh9g== dependencies: "@portkey/provider-types" "^0.0.1-alpha.14" -"@portkey/providers@0.0.1-alpha.14", "@portkey/providers@^0.0.1-alpha.13", "@portkey/providers@^0.0.1-alpha.14": +"@portkey/providers@0.0.1-alpha.17", "@portkey/providers@^0.0.1-alpha.17": + version "0.0.1-alpha.17" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.17.tgz#ed8c2a7e471b18e3a379c2b0676bdcc693626822" + integrity sha512-D9VqXws68YEb4YAOMJAAxUHtz3wt21cqugjDXVELkVeKygmQaVbIizbrsej+H96G102UvINagcoGx0yZmeiJzg== + dependencies: + "@metamask/object-multiplex" "^1.1.0" + "@portkey/chain" "^0.0.1-alpha.17" + "@portkey/provider-types" "^0.0.1-alpha.17" + "@portkey/provider-utils" "^0.0.1-alpha.17" + "@types/readable-stream" "^2.3.15" + lodash "^4.17.21" + pump "^3.0.0" + readable-stream "^4.4.0" + +"@portkey/providers@^0.0.1-alpha.13": version "0.0.1-alpha.14" resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.14.tgz#4c15939c24610a1d99920750f3b6fdc933408869" integrity sha512-wvjVsvj4D9Xjgh4/HoN8sKWx5B5ePYRsyu69zOMDxFvL0K7+9xlFedQCBcXpKwjdZ6JvG9Kbc1b9XGrk6OR2lw== From 15d3a0ac8d8a6d113c86c6adf9856727bde58673 Mon Sep 17 00:00:00 2001 From: "Carbon.lv" <130360750+carbon-portkey@users.noreply.github.com> Date: Wed, 7 Jun 2023 15:35:26 +0800 Subject: [PATCH 041/893] feat: solve jest config issues --- jest.config.js | 17 +- package.json | 1 + packages/mobile-app-did/babel.config.js | 2 +- .../mockAsyncStorage.ts} | 4 - packages/mobile-app-did/jest.config.js | 20 ++ packages/mobile-app-did/package.json | 22 +-- yarn.lock | 179 ++++++++++-------- 7 files changed, 135 insertions(+), 110 deletions(-) rename packages/mobile-app-did/{__tests__/setup/mockAsyncStorage.js => jest-setup/mockAsyncStorage.ts} (72%) create mode 100644 packages/mobile-app-did/jest.config.js diff --git a/jest.config.js b/jest.config.js index 890910929c..768467c10f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -20,7 +20,6 @@ module.exports = { moduleNameMapper: { '\\.(css|less)$': 'identity-obj-proxy', }, - setupFilesAfterEnv: ['./packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js'], projects: [ { displayName: 'hooks', @@ -69,19 +68,23 @@ module.exports = { }, { displayName: 'mobile-app-did', - preset: 'react-native', roots: ['/packages/mobile-app-did'], + preset: 'react-native', transform: { - '^.+\\.(ts|tsx)$': [ - `react-native`, - { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }, - ], + '^.+\\.(ts|tsx)$': [`ts-jest`, { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }], + }, + transformIgnorePatterns: ['/node_modules/(?!((jest-)?react-native|@react-native(-community)?)/)'], + testEnvironment: 'react-native', + globals: { + __DEV__: true, }, + setupFilesAfterEnv: ['./packages/mobile-app-did/jest-setup/mockAsyncStorage.ts'], moduleNameMapper: { '^react$': '/node_modules/react', '^utils/(.*)$': '/packages/mobile-app-did/js/utils/$1', '^store/(.*)$': '/packages/mobile-app-did/js/store/$1', - store: '/packages/mobile-app-did/js/store/index.ts', + '^@portkey-wallet/store/(.*)$': '/packages/store/$1', + store: '/packages/mobile-app-did/js/store/index', '^dapp/(.*)$': '/packages/mobile-app-did/js/dapp/$1', }, coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/Test/', '/utils/'], diff --git a/package.json b/package.json index 9b8c6b5d02..fd7f10c5ab 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "test-badges": "jest --coverage && istanbul-badges-readme" }, "devDependencies": { + "react-native": "0.69.7", "@babel/core": "^7.19.1", "@babel/plugin-proposal-decorators": "^7.19.1", "@babel/plugin-transform-modules-commonjs": "^7.21.2", diff --git a/packages/mobile-app-did/babel.config.js b/packages/mobile-app-did/babel.config.js index ce2ab07d05..4be3c57690 100644 --- a/packages/mobile-app-did/babel.config.js +++ b/packages/mobile-app-did/babel.config.js @@ -11,7 +11,7 @@ function getAliasesFromTsConfig() { return alias; } module.exports = { - presets: ['module:metro-react-native-babel-preset'], + presets: ['module:metro-react-native-babel-preset', ['@babel/preset-env', { loose: true }]], plugins: [ [ 'module-resolver', diff --git a/packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js b/packages/mobile-app-did/jest-setup/mockAsyncStorage.ts similarity index 72% rename from packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js rename to packages/mobile-app-did/jest-setup/mockAsyncStorage.ts index d3c97de03b..cc7b74ebd8 100644 --- a/packages/mobile-app-did/__tests__/setup/mockAsyncStorage.js +++ b/packages/mobile-app-did/jest-setup/mockAsyncStorage.ts @@ -1,7 +1,3 @@ import mockAsyncStorage from '@react-native-async-storage/async-storage/jest/async-storage-mock'; jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage); - -test('mockAsyncStorage', () => { - expect(true).toBeTruthy(); -}); diff --git a/packages/mobile-app-did/jest.config.js b/packages/mobile-app-did/jest.config.js new file mode 100644 index 0000000000..114ccb89de --- /dev/null +++ b/packages/mobile-app-did/jest.config.js @@ -0,0 +1,20 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'react-native', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + globals: { + __DEV__: true, + }, + transform: { + '^.+\\.(ts|tsx)$': [`ts-jest`, { isolatedModules: true, tsconfig: './tsconfig.json' }], + }, + moduleNameMapper: { + '^utils/(.*)$': '/js/utils/$1', + '^store/(.*)$': '/js/store/$1', + '^@portkey-wallet/store/(.*)$': '/../store/$1', + store: '/js/store/index', + '^dapp/(.*)$': '/js/dapp/$1', + }, + testEnvironment: 'react-native', + setupFilesAfterEnv: ['./jest-setup/mockAsyncStorage.ts'], +}; diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index da3288cb9b..cb30902173 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -146,6 +146,7 @@ "web3-utils": "^1.8.0" }, "devDependencies": { + "react-native": "0.69.7", "@babel/core": "^7.12.9", "@babel/runtime": "^7.12.5", "@commitlint/cli": "^17.0.0", @@ -186,27 +187,6 @@ "resolutions": { "@types/react": "^18.0.20" }, - "jest": { - "preset": "react-native", - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "jsx", - "json", - "node" - ], - "moduleNameMapper": { - "^react$": "/../../node_modules/react", - "^utils/(.*)$": "/js/utils/$1", - "^store/(.*)$": "/js/store/$1", - "store": "/js/store/index.ts", - "^dapp/(.*)$": "/js/dapp/$1" - }, - "setupFilesAfterEnv": [ - "./__tests__/setup/mockAsyncStorage.js" - ] - }, "lint-staged": { "*.{js,jsx,ts,tsx}": "eslint --cache --fix" } diff --git a/yarn.lock b/yarn.lock index f40c932e4e..a985c43bed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1466,6 +1466,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.21.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" + integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" @@ -2836,17 +2843,17 @@ resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.8.1.tgz#5fbb3808677aa6b88bf79968237d3bb14595b1ba" integrity sha512-CJW8vxt6bJaBeco2VnlJjmCmAkrrtIdf0GGKvpAB4J5gw8Gi0rHb+qsgKp6LsyS5W6ALPLawLs7phZmw02dvLw== -"@floating-ui/core@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.0.2.tgz#d06a66d3ad8214186eda2432ac8b8d81868a571f" - integrity sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg== +"@floating-ui/core@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.6.tgz#d21ace437cc919cdd8f1640302fa8851e65e75c0" + integrity sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg== -"@floating-ui/dom@^1.0.6": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.0.7.tgz#9e8e6615ce03e5ebc6a4ae879640a199a366f86d" - integrity sha512-6RsqvCYe0AYWtsGvuWqCm7mZytnXAZCjWtsWu1Kg8dI3INvj/DbKlDsZO+mKSaQdPT12uxIW9W2dAWJkPx4Y5g== +"@floating-ui/dom@^1.2.6": + version "1.2.9" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.9.tgz#b9ed1c15d30963419a6736f1b7feb350dd49c603" + integrity sha512-sosQxsqgxMNkV3C+3UqTS6LxP7isRLwX8WMepp843Rb3/b0Wz8+MdUkxJksByip3C2WwLugLHN1b4ibn//zKwQ== dependencies: - "@floating-ui/core" "^1.0.2" + "@floating-ui/core" "^1.2.6" "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" @@ -5044,7 +5051,7 @@ resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.13.tgz#70d831d0917ac902e72de5f85c5b304b0fe3b147" integrity sha512-p6vJqsAVaCzz1pGLlzr6NH+5o1GTKxQH3vfG/m7hLuNxTFo6gVp3fklBfgs6F6A7ePYBTtOgcZO4QFiSfPO2tg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.10" + "@types/readable-stream" "^2.3.15" "@portkey/provider-utils@0.0.1-alpha.10": version "0.0.1-alpha.10" @@ -5126,6 +5133,13 @@ resolved "https://registry.npmmirror.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@rc-component/mini-decimal@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.0.1.tgz#e5dbc20a6a5b0e234d279bc71ce730ab865d3910" + integrity sha512-9N8nRk0oKj1qJzANKl+n9eNSMUGsZtjwNuDCiZ/KA+dt1fE3zq5x2XxclRcAbOIXnZcJ53ozP2Pa60gyELXagA== + dependencies: + "@babel/runtime" "^7.18.0" + "@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.2": version "1.0.3" resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.0.3.tgz#3aa2c229a7a20ac2412d864e8977e6377973416e" @@ -5471,51 +5485,51 @@ resolved "https://registry.npmjs.org/@react-oauth/google/-/google-0.8.1.tgz#6fd62daa2a2c91e642d93a11cc00b8a80a060bda" integrity sha512-pPzZ9ScbTq3FaRU3/uC0d50FgWSGCBBYnW3+aocV1A6YzOMKqyBi5KLCBjh4Gy/yhkHBlKdlN0RIRWapaAGj5g== -"@react-spring/animated@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.5.5.tgz#d3bfd0f62ed13a337463a55d2c93bb23c15bbf3e" - integrity sha512-glzViz7syQ3CE6BQOwAyr75cgh0qsihm5lkaf24I0DfU63cMm/3+br299UEYkuaHNmfDfM414uktiPlZCNJbQA== +"@react-spring/animated@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.6.1.tgz#ccc626d847cbe346f5f8815d0928183c647eb425" + integrity sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ== dependencies: - "@react-spring/shared" "~9.5.5" - "@react-spring/types" "~9.5.5" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" -"@react-spring/core@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.5.5.tgz#1d8a4c64630ee26b2295361e1eedfd716a85b4ae" - integrity sha512-shaJYb3iX18Au6gkk8ahaF0qx0LpS0Yd+ajb4asBaAQf6WPGuEdJsbsNSgei1/O13JyEATsJl20lkjeslJPMYA== +"@react-spring/core@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.6.1.tgz#ebe07c20682b360b06af116ea24e2b609e778c10" + integrity sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ== dependencies: - "@react-spring/animated" "~9.5.5" - "@react-spring/rafz" "~9.5.5" - "@react-spring/shared" "~9.5.5" - "@react-spring/types" "~9.5.5" + "@react-spring/animated" "~9.6.1" + "@react-spring/rafz" "~9.6.1" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" -"@react-spring/rafz@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.5.5.tgz#62a49c5e294104b79db2a8afdf4f3a274c7f44ca" - integrity sha512-F/CLwB0d10jL6My5vgzRQxCNY2RNyDJZedRBK7FsngdCmzoq3V4OqqNc/9voJb9qRC2wd55oGXUeXv2eIaFmsw== +"@react-spring/rafz@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.6.1.tgz#d71aafb92b78b24e4ff84639f52745afc285c38d" + integrity sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ== -"@react-spring/shared@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.5.5.tgz#9be0b391d546e3e184a24ecbaf40acbaeab7fc73" - integrity sha512-YwW70Pa/YXPOwTutExHZmMQSHcNC90kJOnNR4G4mCDNV99hE98jWkIPDOsgqbYx3amIglcFPiYKMaQuGdr8dyQ== +"@react-spring/shared@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.6.1.tgz#4e2e4296910656c02bd9fd54c559702bc836ac4e" + integrity sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw== dependencies: - "@react-spring/rafz" "~9.5.5" - "@react-spring/types" "~9.5.5" + "@react-spring/rafz" "~9.6.1" + "@react-spring/types" "~9.6.1" -"@react-spring/types@~9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.5.5.tgz#c8e94f1b9232ca7cb9d860ea67762ec401b1de14" - integrity sha512-7I/qY8H7Enwasxr4jU6WmtNK+RZ4Z/XvSlDvjXFVe7ii1x0MoSlkw6pD7xuac8qrHQRm9BTcbZNyeeKApYsvCg== +"@react-spring/types@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.6.1.tgz#913d3a68c5cbc1124fdb18eff919432f7b6abdde" + integrity sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q== -"@react-spring/web@^9.5.5": - version "9.5.5" - resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.5.5.tgz#d416abc591aaed930401f0c98a991a8c5b90c382" - integrity sha512-+moT8aDX/ho/XAhU+HRY9m0LVV9y9CK6NjSRaI+30Re150pB3iEip6QfnF4qnhSCQ5drpMF0XRXHgOTY/xbtFw== +"@react-spring/web@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.6.1.tgz#3e4c03b724d2b545dc2fa2649eb6109318ab9178" + integrity sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw== dependencies: - "@react-spring/animated" "~9.5.5" - "@react-spring/core" "~9.5.5" - "@react-spring/shared" "~9.5.5" - "@react-spring/types" "~9.5.5" + "@react-spring/animated" "~9.6.1" + "@react-spring/core" "~9.6.1" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" "@reduxjs/toolkit@^1.8.5": version "1.8.6" @@ -7759,11 +7773,12 @@ ahooks-v3-count@^1.0.0: resolved "https://registry.yarnpkg.com/ahooks-v3-count/-/ahooks-v3-count-1.0.0.tgz#ddeb392e009ad6e748905b3cbf63a9fd8262ca80" integrity sha512-V7uUvAwnimu6eh/PED4mCDjE7tokeZQLKlxg9lCTMPhN+NjsSbtdacByVlR1oluXQzD3MOw55wylDmQo4+S9ZQ== -ahooks@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/ahooks/-/ahooks-3.7.2.tgz#0afa42625e77ae1cc4b60b19c45cf12a8cf29b56" - integrity sha512-nJPsQJcmJnGaNXiqgZdfO7UMs+o926LQg6VyDYt2vzKhXU8Ze/U87NsA/FeIvlIZB0rQr/j7uotFb1bGPp627A== +ahooks@^3.7.6: + version "3.7.7" + resolved "https://registry.yarnpkg.com/ahooks/-/ahooks-3.7.7.tgz#b046f8a23a6e6d6695a37ba44f5f4331cf09f1eb" + integrity sha512-5e5WlPq81Y84UnTLOKIQeq2cJw4aa7yj8fR2Nb/oMmXPrWMjIMCbPS1o+fpxSfCaNA3AzOnnMc8AehWRZltkJQ== dependencies: + "@babel/runtime" "^7.21.0" "@types/js-cookie" "^2.x.x" ahooks-v3-count "^1.0.0" dayjs "^1.9.1" @@ -7772,6 +7787,7 @@ ahooks@^3.7.2: lodash "^4.17.21" resize-observer-polyfill "^1.5.1" screenfull "^5.0.0" + tslib "^2.4.1" ajv-formats@^2.1.1: version "2.1.1" @@ -7958,25 +7974,26 @@ antd-mobile-v5-count@^1.0.1: integrity sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA== antd-mobile@^5.26.0: - version "5.26.0" - resolved "https://registry.yarnpkg.com/antd-mobile/-/antd-mobile-5.26.0.tgz#338282126c2054fa8b84777f81db112238939261" - integrity sha512-syhaSpgiR+SG+s868R0nS+vj3pORVrM2fp9ZPLQD5yHlpxdXZ9/OY82+ttNtuazz2r37PxOOQ/0ggQds5t9LrA== + version "5.30.0" + resolved "https://registry.yarnpkg.com/antd-mobile/-/antd-mobile-5.30.0.tgz#e7540617bbaf9cbe0a1ab69b9811a1906b8a85b8" + integrity sha512-M42VrDvFNzTr1OovLa9HezkD2oXmkDERolha9292FNNTvF1QikIcLZA/o1PDP9+0oWvuz59PW1ZfhjTNx6rUyg== dependencies: - "@floating-ui/dom" "^1.0.6" - "@react-spring/web" "^9.5.5" + "@floating-ui/dom" "^1.2.6" + "@rc-component/mini-decimal" "^1.0.1" + "@react-spring/web" "~9.6.1" "@use-gesture/react" "10.2.20" - ahooks "^3.7.2" + ahooks "^3.7.6" antd-mobile-icons "^0.3.0" antd-mobile-v5-count "^1.0.1" - big.js "^6.2.1" classnames "^2.3.2" - dayjs "^1.11.6" + dayjs "^1.11.7" lodash "^4.17.21" - rc-field-form "~1.27.3" - react-is "^17.0.2" - runes "^0.4.3" + rc-field-form "~1.27.4" + rc-util "^5.30.0" + react-is "^18.2.0" + runes2 "^1.1.2" staged-components "^1.1.3" - tslib "^2.4.1" + tslib "^2.5.0" use-sync-external-store "^1.2.0" antd@^4.21.7: @@ -9737,11 +9754,6 @@ big.js@^5.2.2: resolved "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -big.js@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f" - integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ== - bigint-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" @@ -11980,6 +11992,11 @@ dayjs@^1.11.6, dayjs@^1.9.1: resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb" integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ== +dayjs@^1.11.7: + version "1.11.8" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea" + integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ== + debounce@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" @@ -22599,10 +22616,10 @@ rc-field-form@~1.27.0: async-validator "^4.1.0" rc-util "^5.8.0" -rc-field-form@~1.27.3: - version "1.27.3" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.3.tgz#e5262796b91c80848a42a3e7a669bf459f08d63d" - integrity sha512-HGqxHnmGQgkPApEcikV4qTg3BLPC82uB/cwBDftDt1pYaqitJfSl5TFTTUMKVEJVT5RqJ2Zi68ME1HmIMX2HAw== +rc-field-form@~1.27.4: + version "1.27.4" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.4.tgz#53600714af5b28c226c70d34867a8c52ccd64d44" + integrity sha512-PQColQnZimGKArnOh8V2907+VzDCXcqtFvHgevDLtqWc/P7YASb/FqntSmdS8q3VND5SHX3Y1vgMIzY22/f/0Q== dependencies: "@babel/runtime" "^7.18.0" async-validator "^4.1.0" @@ -23030,6 +23047,14 @@ rc-util@^5.16.0, rc-util@^5.24.4: react-is "^16.12.0" shallowequal "^1.1.0" +rc-util@^5.30.0: + version "5.33.0" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.33.0.tgz#8e673700e467b24c722014f2fe2f2f77aa6c2c07" + integrity sha512-mq2NkEAnHklq4fgU/JqjiE0PS8+8u33gEWw2bDUNDPck3OroPpSgw/8oEyuFrvPgaZEmt9BgQdh59JfQt2cU+w== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^16.12.0" + rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.8: version "3.4.8" resolved "https://registry.npmmirror.com/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz#c24c10c6940546b7e2a5e9809402c6716adfd26c" @@ -23090,12 +23115,12 @@ react-is@^16.12.0, react-is@^16.13.0, react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0: +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-is@^17.0.1, react-is@^17.0.2: +react-is@^17.0.1: version "17.0.2" resolved "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -24401,10 +24426,10 @@ run-parallel@^1.1.2, run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -runes@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/runes/-/runes-0.4.3.tgz#32f7738844bc767b65cc68171528e3373c7bb355" - integrity sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg== +runes2@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/runes2/-/runes2-1.1.2.tgz#408a4cdd31077ca62f52a9aa5bd22d6d16ae6a41" + integrity sha512-v6XIdRpUKdFLNhgF2AC9XvntZsDzxyTpVlpQ8HD592XD6vHiW8jEcHFnTV5ztUjWJC5cGOcdi9YKIwxWVh0f9w== rx-lite-aggregates@^4.0.8: version "4.0.8" From 3b012f872da4fe4e6661fad781d9f975d4b83438 Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Wed, 7 Jun 2023 16:05:53 +0800 Subject: [PATCH 042/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20deal=20social=20l?= =?UTF-8?q?ogin=20and=20getRecaptcha=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/controllers/SWEventController.ts | 12 +++- .../serviceWorker/ServiceWorkerInstantiate.ts | 4 +- packages/web-extension-did/package.json | 8 +-- yarn.lock | 65 +++++++++---------- 4 files changed, 45 insertions(+), 44 deletions(-) diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.ts b/packages/web-extension-did/app/web/controllers/SWEventController.ts index b4d555833b..c5b53d371d 100644 --- a/packages/web-extension-did/app/web/controllers/SWEventController.ts +++ b/packages/web-extension-did/app/web/controllers/SWEventController.ts @@ -1,7 +1,7 @@ /** * @remarks * The controller that handles the event - * chainChanged, accountsChanged, networkChanged, disconnected + * chainChanged, accountsChanged, networkChanged, disconnected, connected */ import { SendResponseFun } from 'types'; import { apis } from 'utils/BrowserApis'; @@ -25,7 +25,7 @@ import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wal import { getDappState, getWalletState } from 'utils/lib/SWGetReduxStore'; import InternalMessage from 'messages/InternalMessage'; import { handleAccounts, handleChainIds } from '@portkey-wallet/utils/dapp'; -import { addDapp, removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { addDapp, removeDapp, resetDapp, resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; export interface DappEventPack { eventName: T; @@ -165,6 +165,14 @@ export default class SWEventController { origin: payload.dapp.origin, }).send(); } + break; + } + case resetDapp.toString(): + case resetDappList.toString(): { + await InternalMessage.payload(NotificationEvents.DISCONNECTED, { + data: { message: 'user logout', code: ResponseCode.USER_DENIED }, + }).send(); + break; } } } diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 4fbeff6fea..d4797018fe 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -235,12 +235,12 @@ export default class ServiceWorkerInstantiate { } async getRecaptcha(sendResponse: SendResponseFun, message: any) { - this.notificationServiceClose(sendResponse, { closeParams: message.params, promptType: 'windows' }); + this.notificationServiceClose(sendResponse, { closeParams: message.payload, promptType: 'windows' }); } async getSocialLogin(sendResponse: SendResponseFun, message: any) { socialLoginService.finishSocialLogin(); - this.notificationServiceClose(sendResponse, { closeParams: message.params, promptType: 'windows' }); + this.notificationServiceClose(sendResponse, { closeParams: message.payload, promptType: 'windows' }); } // async dealNextTask(sendResponse: SendResponseFun, message: any) { diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 094bac3860..06c6ec85d5 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.17", - "@portkey/provider-utils": "0.0.1-alpha.17", - "@portkey/provider-types": "0.0.1-alpha.17", - "@portkey/providers": "0.0.1-alpha.17", + "@portkey/extension-provider": "0.0.1-alpha.18", + "@portkey/provider-utils": "0.0.1-alpha.18", + "@portkey/provider-types": "0.0.1-alpha.18", + "@portkey/providers": "0.0.1-alpha.18", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index 8ac9202a0a..98a390f73b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5032,13 +5032,13 @@ "@portkey/provider-types" "^0.0.1-alpha.18" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.10": - version "0.0.1-alpha.10" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.10.tgz#d15f8370802ba9f8cc00e02a11f566d889aa915d" - integrity sha512-xdHmmLfBccEYcNLaujQkWRJi+BLZMJeXpHBRwKXlKzNi1C7ioqyoeCZpR389CKQO99d34wht4lNjkmKOX6TBfg== +"@portkey/extension-provider@0.0.1-alpha.18": + version "0.0.1-alpha.18" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.18.tgz#eb082c62f6a24f56e7f195570e0e7e3d53c48525" + integrity sha512-MFYxpKIkcL21aLz3+2g1OANj2ORqaqPRwa4EO45zhCgQhXhgcaQ+S4pchQMh14GiAH6TxDNOokAp0Shyhb9tTw== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.10" - "@portkey/providers" "^0.0.1-alpha.10" + "@portkey/provider-types" "^0.0.1-alpha.18" + "@portkey/providers" "^0.0.1-alpha.18" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" @@ -5047,55 +5047,35 @@ resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.18.tgz#8d7a5fae3e272176fe8fbd9927bf9dec4ee5514a" integrity sha512-Nv+Ci8xhCo1KJ4TdhR2v2onqU4T3cJSzEujXfPHoAqqNqSRk9tZPhYtS0/2/6CAhq3yOMukPp4TXvh3JOFTngA== -"@portkey/provider-types@^0.0.1-alpha.10", "@portkey/provider-types@^0.0.1-alpha.13": - version "0.0.1-alpha.13" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.13.tgz#70d831d0917ac902e72de5f85c5b304b0fe3b147" - integrity sha512-p6vJqsAVaCzz1pGLlzr6NH+5o1GTKxQH3vfG/m7hLuNxTFo6gVp3fklBfgs6F6A7ePYBTtOgcZO4QFiSfPO2tg== - dependencies: - "@types/readable-stream" "^2.3.15" - -"@portkey/provider-types@^0.0.1-alpha.18": +"@portkey/provider-types@0.0.1-alpha.18", "@portkey/provider-types@^0.0.1-alpha.18": version "0.0.1-alpha.18" resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.18.tgz#494f755d589cbe589538af6ef5ecb0d3df566160" integrity sha512-nn2fjfopQvtu4Z+8ipNFA5ThsytONXSGbdsbQJhh1DfvOwjCPdtB16Dp6D/qEakQSLHL1e7K01A+dgz3A5oxew== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.10": - version "0.0.1-alpha.10" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.10.tgz#9f3f9641c11ba1e390c97867a01e7df9393ac6ec" - integrity sha512-fiDrTeWNzmiLUp/g+aI8g+GzpHMKUATD7/pdIr33SMdGLcNBlfit7gXijt5Zzdyax8GOM/8GwCCyEOSLx+jA/Q== - dependencies: - "@portkey/provider-types" "^0.0.1-alpha.10" - -"@portkey/provider-utils@^0.0.1-alpha.13": +"@portkey/provider-types@^0.0.1-alpha.13": version "0.0.1-alpha.13" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.13.tgz#a94b5e4118c7f31c2c92af2a2c96d9517e39ba83" - integrity sha512-A2rr1iW6lsNdNo0ZmLeDvpixntTNH/xzYq44m6oxwJJgJPWQJ3hQ4m1piDLY4ZhO9mkEoJEFqEC0s3dDXmambQ== + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.13.tgz#70d831d0917ac902e72de5f85c5b304b0fe3b147" + integrity sha512-p6vJqsAVaCzz1pGLlzr6NH+5o1GTKxQH3vfG/m7hLuNxTFo6gVp3fklBfgs6F6A7ePYBTtOgcZO4QFiSfPO2tg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.13" + "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@^0.0.1-alpha.18": +"@portkey/provider-utils@0.0.1-alpha.18", "@portkey/provider-utils@^0.0.1-alpha.18": version "0.0.1-alpha.18" resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.18.tgz#e2af68cb2e2615d80787681df2596875935b1938" integrity sha512-dxHYuc2f+fZim15a7cMpySAgrp7TClCqib0iM7QW01CEt4OfQ9wnJxVrzhA5HEALWt3OLV8VAqksGp2cq8Azfg== dependencies: "@portkey/provider-types" "^0.0.1-alpha.18" -"@portkey/providers@^0.0.1-alpha.10", "@portkey/providers@^0.0.1-alpha.13": +"@portkey/provider-utils@^0.0.1-alpha.13": version "0.0.1-alpha.13" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.13.tgz#c1c3ddf90e372cda0b79f5877afa44eaaf9760bc" - integrity sha512-re+J1XJqIRelqXqoV2qzC+s+OorA6xyjiQTos5/duvEuH9jT2pPQfaDa27rJETWwAyF8tNiZti4OGF0q+ue9Ug== + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.13.tgz#a94b5e4118c7f31c2c92af2a2c96d9517e39ba83" + integrity sha512-A2rr1iW6lsNdNo0ZmLeDvpixntTNH/xzYq44m6oxwJJgJPWQJ3hQ4m1piDLY4ZhO9mkEoJEFqEC0s3dDXmambQ== dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.13" "@portkey/provider-types" "^0.0.1-alpha.13" - "@portkey/provider-utils" "^0.0.1-alpha.13" - "@types/readable-stream" "^2.3.15" - pump "^3.0.0" - readable-stream "^4.4.0" -"@portkey/providers@^0.0.1-alpha.18": +"@portkey/providers@0.0.1-alpha.18", "@portkey/providers@^0.0.1-alpha.18": version "0.0.1-alpha.18" resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.18.tgz#318ca6b9c24bd7bf887abd4706afba3692ea31df" integrity sha512-BfgQWC53ehbA8D01c8gcR/H9zQFkpuFuOFYqDc8QXrMTv4TXtey2j0LJO5LdJraa8hIoGHOrvG3rQvsZzILcaA== @@ -5109,6 +5089,19 @@ pump "^3.0.0" readable-stream "^4.4.0" +"@portkey/providers@^0.0.1-alpha.13": + version "0.0.1-alpha.13" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.13.tgz#c1c3ddf90e372cda0b79f5877afa44eaaf9760bc" + integrity sha512-re+J1XJqIRelqXqoV2qzC+s+OorA6xyjiQTos5/duvEuH9jT2pPQfaDa27rJETWwAyF8tNiZti4OGF0q+ue9Ug== + dependencies: + "@metamask/object-multiplex" "^1.1.0" + "@portkey/chain" "^0.0.1-alpha.13" + "@portkey/provider-types" "^0.0.1-alpha.13" + "@portkey/provider-utils" "^0.0.1-alpha.13" + "@types/readable-stream" "^2.3.15" + pump "^3.0.0" + readable-stream "^4.4.0" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From 0e896ad4f4ed36ecd55c8f3ef809d9251bbfe035 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 7 Jun 2023 19:41:51 +0800 Subject: [PATCH 043/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20dapp=20utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/dapp/browser.ts | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/utils/dapp/browser.ts b/packages/utils/dapp/browser.ts index adf8078069..578a25ba9b 100644 --- a/packages/utils/dapp/browser.ts +++ b/packages/utils/dapp/browser.ts @@ -43,6 +43,48 @@ export function getHost(url: string, defaultProtocol = 'https://') { return result; } +/** + * getFaviconUrl + * @param url + * @param size + * @returns string + */ +export function getFaviconUrl(url: string, size: number = 50): string { + return `https://api.faviconkit.com/${getHost(url)}/${size}`; +} + +/** + * Returns URL prefixed with protocol, which could be a search engine url if + * a keyword is detected instead of a url + * + * @param input - String corresponding to url input + * @param searchEngine - Protocol string to append to URLs that have none + * @param defaultProtocol - Protocol string to append to URLs that have none + * @returns - String corresponding to sanitized input depending if it's a search or url + */ +export default function onUrlSubmit(input: string, searchEngine = 'Google', defaultProtocol = 'https://') { + //Check if it's a url or a keyword + const regEx = new RegExp(/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!&',;=.+]+$/g); + if (!isUrl(input) && !regEx.test(input)) { + // Add exception for localhost + if (!input.startsWith('http://localhost') && !input.startsWith('localhost')) { + // In case of keywords we default to google search + let searchUrl = 'https://www.google.com/search?q=' + encodeURIComponent(input); + if (searchEngine === 'DuckDuckGo') { + searchUrl = 'https://duckduckgo.com/?q=' + encodeURIComponent(input); + } + return searchUrl; + } + } + return prefixUrlWithProtocol(input, defaultProtocol); +} + +export const checkIsUrl = (value: string) => { + const regEx = new RegExp(/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!&',;=.+]+$/g); + if (!isUrl(value) && !regEx.test(value)) return false; + return true; +}; + export function isEqDapp(dapp1?: DappStoreItem, dapp2?: DappStoreItem) { if (!dapp1 || !dapp2) return false; return dapp1.origin === dapp2.origin && dapp1.name === dapp2.name && dapp1.icon === dapp2.icon; From 83776d80664753383c93b4058e10242f06d2b484 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 7 Jun 2023 19:53:31 +0800 Subject: [PATCH 044/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/babel.config.js | 6 + packages/mobile-app-did/ios/Podfile.lock | 41 ++++- packages/mobile-app-did/package.json | 11 +- yarn.lock | 215 ++++++++++++++++++++++- 4 files changed, 259 insertions(+), 14 deletions(-) diff --git a/packages/mobile-app-did/babel.config.js b/packages/mobile-app-did/babel.config.js index ce2ab07d05..a721e6121a 100644 --- a/packages/mobile-app-did/babel.config.js +++ b/packages/mobile-app-did/babel.config.js @@ -13,6 +13,12 @@ function getAliasesFromTsConfig() { module.exports = { presets: ['module:metro-react-native-babel-preset'], plugins: [ + [ + 'react-native-reanimated/plugin', + { + relativeSourceLocation: true, + }, + ], [ 'module-resolver', { diff --git a/packages/mobile-app-did/ios/Podfile.lock b/packages/mobile-app-did/ios/Podfile.lock index 95e522a639..547b8cc9d7 100644 --- a/packages/mobile-app-did/ios/Podfile.lock +++ b/packages/mobile-app-did/ios/Podfile.lock @@ -570,6 +570,8 @@ PODS: - ReactCommon/turbomodule/core - react-native-spinkit (1.4.1): - React + - react-native-view-shot (3.6.0): + - React-Core - react-native-webview (11.23.1): - React-Core - React-perflogger (0.69.7) @@ -667,7 +669,7 @@ PODS: - React-Core - RNFS (2.20.0): - React-Core - - RNGestureHandler (2.7.0): + - RNGestureHandler (2.10.2): - React-Core - RNGoogleSignin (8.2.2): - GoogleSignIn (~> 6.2) @@ -679,6 +681,33 @@ PODS: - RNNotifee/NotifeeCore (= 5.7.0) - RNNotifee/NotifeeCore (5.7.0): - React-Core + - RNReanimated (2.17.0): + - DoubleConversion + - FBLazyVector + - FBReactNativeSpec + - glog + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-callinvoker + - React-Core + - React-Core/DevSupport + - React-Core/RCTWebSocket + - React-CoreModules + - React-cxxreact + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-RCTActionSheet + - React-RCTAnimation + - React-RCTBlob + - React-RCTImage + - React-RCTLinking + - React-RCTNetwork + - React-RCTSettings + - React-RCTText + - ReactCommon/turbomodule/core + - Yoga - RNScreens (3.18.0): - React-Core - React-RCTImage @@ -795,6 +824,7 @@ DEPENDENCIES: - react-native-randombytes (from `../node_modules/react-native-randombytes`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-spinkit (from `../node_modules/react-native-spinkit`) + - react-native-view-shot (from `../node_modules/react-native-view-shot`) - react-native-webview (from `../node_modules/react-native-webview`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) @@ -821,6 +851,7 @@ DEPENDENCIES: - "RNGoogleSignin (from `../node_modules/@react-native-google-signin/google-signin`)" - RNLocalize (from `../node_modules/react-native-localize`) - "RNNotifee (from `../node_modules/@notifee/react-native`)" + - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) - "RNSentry (from `../node_modules/@sentry/react-native`)" - RNSpringScrollView (from `../node_modules/react-native-spring-scrollview`) @@ -990,6 +1021,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-safe-area-context" react-native-spinkit: :path: "../node_modules/react-native-spinkit" + react-native-view-shot: + :path: "../node_modules/react-native-view-shot" react-native-webview: :path: "../node_modules/react-native-webview" React-perflogger: @@ -1042,6 +1075,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-localize" RNNotifee: :path: "../node_modules/@notifee/react-native" + RNReanimated: + :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" RNSentry: @@ -1153,6 +1188,7 @@ SPEC CHECKSUMS: react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 react-native-safe-area-context: 99b24a0c5acd0d5dcac2b1a7f18c49ea317be99a react-native-spinkit: da294fd828216ad211fe36a5c14c1e09f09e62db + react-native-view-shot: 705f999ac2a24e4e6c909c0ca65c732ed33ca2ff react-native-webview: d33e2db8925d090871ffeb232dfa50cb3a727581 React-perflogger: 8e832d4e21fdfa613033c76d58d7e617341e804b React-RCTActionSheet: 9ca778182a9523991bff6381045885b6e808bb73 @@ -1175,10 +1211,11 @@ SPEC CHECKSUMS: RNFBPerf: 789e745a88bc520bcab1fcc9e6cc80d3c9aaa6aa RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 - RNGestureHandler: 7673697e7c0e9391adefae4faa087442bc04af33 + RNGestureHandler: f75d81410b40aaa99e71ae8f8bb7a88620c95042 RNGoogleSignin: 81521697b2c8f97f9a586ac7257b1a1d9b51b115 RNLocalize: 0df7970cfc60389f00eb62fd7c097dc75af3fb4f RNNotifee: 5155e0a5e0a97d0c839030d8192783cd63053999 + RNReanimated: c6f8f70e96dd6ca21b65aef9b3e4efe4965db905 RNScreens: f3230dd008a7d0ce5c0a8bc78ff12cf2315bda24 RNSentry: acebe4104a6f5915ae871eb59dc73f13dcc92ef7 RNSpringScrollView: e354167d96f2c3d2f20b186990b2c3946987cb3c diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index f5c41c9e30..b2f2524559 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -112,9 +112,11 @@ "react-native-background-timer": "^2.4.1", "react-native-config": "^1.4.6", "react-native-crypto": "^2.2.0", + "react-native-drawer-layout": "^3.2.0", "react-native-echarts-pro": "^1.8.5", "react-native-flipper": "^0.161.0", - "react-native-gesture-handler": "^2.5.0", + "react-native-fs": "^2.20.0", + "react-native-gesture-handler": "^2.10.2", "react-native-get-random-values": "^1.8.0", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-largelist": "3.1.0-rc.2", @@ -122,6 +124,7 @@ "react-native-pager-view": "^6.0.2", "react-native-qrcode-styled": "^0.2.1", "react-native-randombytes": "^3.6.1", + "react-native-reanimated": "^2.17.0", "react-native-safe-area-context": "^4.3.1", "react-native-screens": "^3.15.0", "react-native-spinkit": "1.5.1", @@ -131,8 +134,8 @@ "react-native-switch-toggle": "^2.2.1", "react-native-tab-view": "^3.2.1", "react-native-vector-icons": "^9.1.0", + "react-native-view-shot": "^3.6.0", "react-native-webview": "^11.23.0", - "react-native-fs": "^2.20.0", "react-redux": "^8.0.2", "redux-flipper": "^2.0.2", "redux-persist": "^6.0.0", @@ -141,8 +144,8 @@ "rn-teaset": "^0.1.1", "seamless-immutable": "^7.1.4", "stream": "^0.0.2", - "uuid": "^8.3.2", "url-parse": "^1.5.10", + "uuid": "^8.3.2", "web3-utils": "^1.8.0" }, "devDependencies": { @@ -161,8 +164,8 @@ "@types/react-native-background-timer": "2.0.0", "@types/react-test-renderer": "^18.0.0", "@types/seamless-immutable": "^7.1.16", - "@types/uuid": "^8.3.4", "@types/url-parse": "^1.4.8", + "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^5.29.0", "@typescript-eslint/parser": "^5.29.0", "babel-jest": "^26.6.3", diff --git a/yarn.lock b/yarn.lock index ebef4a7321..1fea7d9f1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -286,6 +286,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" + integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== + dependencies: + "@babel/types" "^7.22.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -335,6 +345,21 @@ "@babel/helper-replace-supers" "^7.18.9" "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b" + integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.22.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.22.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + semver "^6.3.0" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" @@ -360,6 +385,11 @@ resolved "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" + integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -413,6 +443,13 @@ dependencies: "@babel/types" "^7.18.9" +"@babel/helper-member-expression-to-functions@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz#4b77a12c1b4b8e9e28736ed47d8b91f00976911f" + integrity sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA== + dependencies: + "@babel/types" "^7.22.3" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -420,6 +457,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" @@ -448,6 +492,20 @@ "@babel/traverse" "^7.21.2" "@babel/types" "^7.21.2" +"@babel/helper-module-transforms@^7.21.5": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" + integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -465,6 +523,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== +"@babel/helper-plugin-utils@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" + integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== + "@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -486,6 +549,18 @@ "@babel/traverse" "^7.19.1" "@babel/types" "^7.19.0" +"@babel/helper-replace-supers@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95" + integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-member-expression-to-functions" "^7.22.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -500,6 +575,13 @@ dependencies: "@babel/types" "^7.20.2" +"@babel/helper-simple-access@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" + integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== + dependencies: + "@babel/types" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.18.9": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" @@ -507,6 +589,13 @@ dependencies: "@babel/types" "^7.18.9" +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + "@babel/helper-split-export-declaration@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" @@ -531,6 +620,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" @@ -607,6 +701,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== +"@babel/parser@^7.21.9", "@babel/parser@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" + integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -869,7 +968,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== @@ -939,7 +1038,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.21.4", "@babel/plugin-syntax-typescript@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== @@ -1094,6 +1193,15 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" +"@babel/plugin-transform-modules-commonjs@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" + integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== + dependencies: + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-simple-access" "^7.21.5" + "@babel/plugin-transform-modules-systemjs@^7.19.0": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" @@ -1128,6 +1236,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-transform-object-assign@^7.16.7": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.18.6.tgz#7830b4b6f83e1374a5afb9f6111bcfaea872cdd2" + integrity sha512-mQisZ3JfqWh2gVXvfqYCAAyRs6+7oev+myBsTwW5RnPhYXOTuCEw2oe3YgxlXMViXUS53lG8koulI7mJ+8JE+A== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-object-super@^7.0.0", "@babel/plugin-transform-object-super@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" @@ -1281,6 +1396,16 @@ "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-typescript" "^7.18.6" +"@babel/plugin-transform-typescript@^7.21.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.3.tgz#8f662cec8ba88c873f1c7663c0c94e3f68592f09" + integrity sha512-pyjnCIniO5PNaEuGxT28h0HbMru3qCVrMqVgVOz/krComdIrY9W6FCLBq9NWHY8HDGaUlan+UhmZElDENIfCcw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-typescript" "^7.21.4" + "@babel/plugin-transform-unicode-escapes@^7.18.10": version "7.18.10" resolved "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" @@ -1426,6 +1551,17 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-typescript" "^7.18.6" +"@babel/preset-typescript@^7.16.7": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz#68292c884b0e26070b4d66b202072d391358395f" + integrity sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-typescript" "^7.21.3" + "@babel/register@^7.13.16": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" @@ -1494,6 +1630,15 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/template@^7.21.9": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" + integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/parser" "^7.21.9" + "@babel/types" "^7.21.5" + "@babel/traverse@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" @@ -1558,6 +1703,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.22.1": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" + integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.22.3" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.22.4" + "@babel/types" "^7.22.4" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" @@ -1585,6 +1746,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.20.0", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" + integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@babel/types@^7.20.2", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" @@ -1809,7 +1979,7 @@ "@egjs/hammerjs@^2.0.17": version "2.0.17" - resolved "https://registry.npmmirror.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124" + resolved "https://registry.yarnpkg.com/@egjs/hammerjs/-/hammerjs-2.0.17.tgz#5dc02af75a6a06e4c2db0202cae38c9263895124" integrity sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A== dependencies: "@types/hammerjs" "^2.0.36" @@ -6301,7 +6471,7 @@ "@types/hammerjs@^2.0.36": version "2.0.41" - resolved "https://registry.npmmirror.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa" + resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.41.tgz#f6ecf57d1b12d2befcce00e928a6a097c22980aa" integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== "@types/har-format@*": @@ -23164,6 +23334,13 @@ react-native-crypto@^2.2.0: public-encrypt "^4.0.0" randomfill "^1.0.3" +react-native-drawer-layout@^2.17.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-3.2.0.tgz#1ab05d0bed6bb684353c17c96e1d3e6c1a4e225d" + integrity sha512-d/kvzeBhXjqcRGlfkTSB96ZRKH6g6YxJK+gPtUOCOCH5piHpsupgX+tLAHdM8r8NUzN6Tl9656xfJTuVb8Zrgw== + dependencies: + use-latest-callback "^0.1.5" + react-native-echarts-pro@^1.8.5: version "1.8.7" resolved "https://registry.npmmirror.com/react-native-echarts-pro/-/react-native-echarts-pro-1.8.7.tgz#8c8447bdb86bdc0910947f15f7a0fecd5c564e3c" @@ -23182,10 +23359,10 @@ react-native-fs@^2.20.0: base-64 "^0.1.0" utf8 "^3.0.0" -react-native-gesture-handler@^2.5.0: - version "2.7.0" - resolved "https://registry.npmmirror.com/react-native-gesture-handler/-/react-native-gesture-handler-2.7.0.tgz#53ad828add926c8e025f68ea581758c0f8893054" - integrity sha512-0jr3FNm2R3gv/v6XTtENgjv0fewD6LEct8EWmXw/oHw36M3YiIIpxnW57thL+0YiKwyLBXN0QHL4JZbs/heW2Q== +react-native-gesture-handler@^3.2.0: + version "2.10.2" + resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.10.2.tgz#0d7d82de767a551157eaedc5f74e389c5fc357b9" + integrity sha512-yUwTrgLinaGRdJN3igL5/QP+09B294EKgoCH7QJ4ABKb4W2mUvSDbbuGMaYBNnwMKAD87Ns2q/qibLWy3E3LTw== dependencies: "@egjs/hammerjs" "^2.0.17" hoist-non-react-statics "^3.3.0" @@ -23267,6 +23444,18 @@ react-native-ratings@^8.1.0: dependencies: lodash "^4.17.15" +react-native-reanimated@^2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.17.0.tgz#eae2308235961cdd79810e01dfdd7e88b1ae5b5c" + integrity sha512-bVy+FUEaHXq4i+aPPqzGeor1rG4scgVNBbBz21ohvC7iMpB9IIgvGsmy1FAoodZhZ5sa3EPF67Rcec76F1PXlQ== + dependencies: + "@babel/plugin-transform-object-assign" "^7.16.7" + "@babel/preset-typescript" "^7.16.7" + invariant "^2.2.4" + lodash.isequal "^4.5.0" + setimmediate "^1.0.5" + string-hash-64 "^1.0.3" + react-native-safe-area-context@^4.3.1: version "4.4.1" resolved "https://registry.npmmirror.com/react-native-safe-area-context/-/react-native-safe-area-context-4.4.1.tgz#239c60b8a9a80eac70a38a822b04c0f1d15ffc01" @@ -23350,6 +23539,11 @@ react-native-vector-icons@^9.1.0: prop-types "^15.7.2" yargs "^16.1.1" +react-native-view-shot@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-3.6.0.tgz#2c5febd03cb1effb9923372b29aed227c680d9fd" + integrity sha512-QUYGaIaAxQwOTydUzqGMooBwrg455cuOQgTloZ+gPO1QCUuLRdncCqrEMwKW5eUnN5U8JGMKeFRll2m6egOxtA== + react-native-webview@^11.23.0: version "11.23.1" resolved "https://registry.npmmirror.com/react-native-webview/-/react-native-webview-11.23.1.tgz#6a4bf2620e491dd4fecf4e6dc079005117fae12c" @@ -25480,6 +25674,11 @@ string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== +string-hash-64@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322" + integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" From fc10e8df3855b203ee541da5cd374af2554baf15 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 7 Jun 2023 20:18:05 +0800 Subject: [PATCH 045/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20svgs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/assets/image/svgs.js | 2 +- .../mobile-app-did/js/assets/image/svgs/add-blue.svg | 3 +++ .../mobile-app-did/js/assets/image/svgs/switch.svg | 7 +++++++ .../js/assets/image/svgs/wallet-white.svg | 12 ++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 packages/mobile-app-did/js/assets/image/svgs/add-blue.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/switch.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index c962093465..2fa85778d3 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n jrosmjmwjg\n \n \n \n \n \n \n \n \n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n rewbbtbkvk\n \n \n \n \n \n \n \n \n \n \n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n jrosmjmwjg\n \n \n \n \n \n \n \n \n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n rewbbtbkvk\n \n \n \n \n \n \n \n \n \n \n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/add-blue.svg b/packages/mobile-app-did/js/assets/image/svgs/add-blue.svg new file mode 100644 index 0000000000..38317d49b5 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/add-blue.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/switch.svg b/packages/mobile-app-did/js/assets/image/svgs/switch.svg new file mode 100644 index 0000000000..6aced5bc4c --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/switch.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg b/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg new file mode 100644 index 0000000000..eb98cf38f5 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + From 243839eacabd28f7f607ce76bb61d810716958fe Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 7 Jun 2023 20:23:46 +0800 Subject: [PATCH 046/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20discover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/App.tsx | 2 + .../js/components/BrowserTab/index.tsx | 60 +++--- .../TabsDrawer/TabsDrawerContent.tsx | 165 +++++++++++++++ .../TabsDrawer/components/card/index.tsx | 81 ++++++++ .../components/tabsOverlay/index.tsx | 188 ++++++++++++++++++ .../js/components/TabsDrawer/index.tsx | 31 +++ packages/mobile-app-did/js/navigation/Tab.tsx | 11 +- .../mobile-app-did/js/navigation/index.tsx | 30 +-- .../pages/Discover/DiscoverSearch/index.tsx | 40 ++-- .../Discover/SwitchTabsContent/index.tsx | 46 +++++ .../js/pages/Discover/index.tsx | 2 + .../mobile-app-did/js/store/rootReducer.ts | 1 + .../js/utils/webviewScreenShot.ts | 22 ++ packages/store/store-ca/discover/slice.ts | 43 +++- packages/store/store-ca/discover/type.ts | 13 +- packages/types/types-ca/store.ts | 4 +- .../app/web/store/Provider/Updater.tsx | 3 +- 17 files changed, 678 insertions(+), 64 deletions(-) create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx create mode 100644 packages/mobile-app-did/js/utils/webviewScreenShot.ts diff --git a/packages/mobile-app-did/App.tsx b/packages/mobile-app-did/App.tsx index 2a082cc8ac..46373a30bd 100644 --- a/packages/mobile-app-did/App.tsx +++ b/packages/mobile-app-did/App.tsx @@ -1,3 +1,5 @@ +import 'react-native-gesture-handler'; + import React, { useEffect } from 'react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { StatusBar, StatusBarProps } from 'react-native'; diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index a7f4af4205..f6cbee2de8 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useRef, useState } from 'react'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import WebView from 'react-native-webview'; import { pTd } from 'utils/unit'; @@ -12,13 +12,14 @@ import URL from 'url-parse'; import { store } from 'store'; import { DappOverlay } from 'dapp/dappOverlay'; import { DappMobileManager } from 'dapp/dappManager'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; - +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; type BrowserTabProps = { uri: string; + isHidden: boolean; }; -const BrowserTab: React.FC = ({ uri }) => { +const BrowserTab: React.FC = ({ uri, isHidden }) => { const webViewRef = useRef(null); const operatorRef = useRef(null); const [entryScriptWeb3, setEntryScriptWeb3] = useState(); @@ -54,7 +55,7 @@ const BrowserTab: React.FC = ({ uri }) => { const handleUpdate = useCallback(({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { const { origin, pathname = '', query = '' } = new URL(nativeEvent.url); const realUrl = `${origin}${pathname}${query}`; - const icon = `https://api.faviconkit.com/${getHost(realUrl)}/50`; + const icon = getFaviconUrl(realUrl, 50); operatorRef.current?.updateDappInfo({ origin, name: nativeEvent.title, @@ -63,23 +64,25 @@ const BrowserTab: React.FC = ({ uri }) => { }, []); return ( - { - operatorRef.current?.handleRequestMessage(nativeEvent.data); - }} - onLoadStart={onLoadStart} - onLoad={handleUpdate} - onLoadProgress={({ nativeEvent }) => { - console.log(nativeEvent.progress, '=onLoadProgress'); - }} - onLoadEnd={handleUpdate} - applicationNameForUserAgent={'WebView Portkey did Mobile'} - /> + + { + operatorRef.current?.handleRequestMessage(nativeEvent.data); + }} + onLoadStart={onLoadStart} + onLoad={handleUpdate} + onLoadProgress={({ nativeEvent }) => { + console.log(nativeEvent.progress, '=onLoadProgress'); + }} + onLoadEnd={handleUpdate} + applicationNameForUserAgent={'WebView Portkey did Mobile'} + /> + ); }; @@ -94,13 +97,20 @@ export const styles = StyleSheet.create({ svgWrap: { marginRight: pTd(16), }, + webViewContainerHidden: { + flex: 0, + opacity: 0, + display: 'none', + width: 0, + height: 0, + }, webViewContainer: { - flex: 1, - backgroundColor: 'red', + width: screenWidth, + height: screenHeight, }, webView: { - width: '100%', flex: 1, + zIndex: 1, }, noResult: {}, }); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx new file mode 100644 index 0000000000..1599fceca0 --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -0,0 +1,165 @@ +import fonts from 'assets/theme/fonts'; +import GStyles from 'assets/theme/GStyles'; +import { TextM } from 'components/CommonText'; +import PageContainer from 'components/PageContainer'; +import { TouchableOpacity } from 'react-native'; +import React, { useMemo } from 'react'; +import { StyleSheet, ScrollView, View, Button, Image } from 'react-native'; +import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca/index'; +import { pTd } from 'utils/unit'; +import Svg from 'components/Svg'; +import { defaultColors } from 'assets/theme'; +import { useLanguage } from 'i18n/hooks'; +import { FontStyles } from 'assets/theme/styles'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import Card from './components/card'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { changeDrawerOpenStatus } from '@portkey-wallet/store/store-ca/discover/slice'; +import BrowserTab from 'components/BrowserTab'; +import { showBrowserModal } from './components/tabsOverlay'; + +const TabsDrawerContent: React.FC = () => { + const { t } = useLanguage(); + const dispatch = useAppCommonDispatch(); + const { activeTabId, tabs } = useAppCASelector(state => state.discover); + + console.log('tabs', tabs); + + const rightDom = useMemo(() => { + const activeItem = tabs.find(ele => ele.id === activeTabId); + + console.log('activeItem', activeItem); + + if (activeTabId) + return ( + + + + + + showBrowserModal({ + browserInfo: { title: activeItem?.name ?? '', url: activeItem?.url }, + setBrowserInfo: undefined, + handleReload: undefined, + }) + } + style={rightDomStyle.iconWrap}> + + + + ); + return null; + }, [activeTabId, tabs]); + + return ( + } + leftCallback={() => dispatch(changeDrawerOpenStatus(false))} + rightDom={rightDom} + safeAreaColor={['blue', 'white']} + containerStyles={styles.container} + scrollViewProps={{ disabled: true }} + titleDom={activeTabId ? '' : `${tabs?.length} Tabs`}> + {tabs?.map(ele => ( + + ))} + + {!activeTabId && ( + <> + + + {tabs.map(ele => { + return ; + })} + + + + {t('Close All')} + dispatch(changeDrawerOpenStatus(false))}> + + + console.log('done')}> + {t('Done')} + + + + )} + + ); +}; + +export default TabsDrawerContent; + +const styles = StyleSheet.create({ + container: { + paddingLeft: 0, + paddingRight: 0, + flex: 1, + backgroundColor: defaultColors.bg6, + }, + inputContainer: { + ...GStyles.paddingArg(8, 20), + }, + inputStyle: { + width: pTd(280), + }, + sectionWrap: { + ...GStyles.paddingArg(24, 20), + }, + headerWrap: { + height: pTd(22), + }, + header: { + ...fonts.mediumFont, + lineHeight: pTd(24), + }, + cancelButton: { + paddingLeft: pTd(12), + lineHeight: pTd(36), + }, + rightIconContainerStyle: { + marginRight: pTd(10), + }, + cardsContainer: { + scrollEnabled: true, + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + flexWrap: 'wrap', + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, +}); + +const handleButtonStyle = StyleSheet.create({ + container: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + width: screenWidth, + height: pTd(44), + paddingLeft: pTd(20), + paddingRight: pTd(20), + position: 'absolute', + bottom: 0, + backgroundColor: defaultColors.bg1, + }, + handleItem: { + flex: 1, + }, +}); + +const rightDomStyle = StyleSheet.create({ + iconGroupWrap: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + iconWrap: { + ...GStyles.paddingArg(pTd(4)), + marginRight: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx new file mode 100644 index 0000000000..872ca8a460 --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx @@ -0,0 +1,81 @@ +import GStyles from 'assets/theme/GStyles'; +import { TextM } from 'components/CommonText'; +import { TouchableOpacity } from 'react-native'; +import React from 'react'; +import { StyleSheet, View, Image } from 'react-native'; +import { pTd } from 'utils/unit'; + +import Svg from 'components/Svg'; + +import { useLanguage } from 'i18n/hooks'; +import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { closeExistingTab, setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; + +interface ICardsProps { + item: ITabItem; +} + +const Card: React.FC = (props: ICardsProps) => { + const { item } = props; + const { t } = useLanguage(); + const dispatch = useAppCommonDispatch(); + + console.log('item', item); + + return ( + + + + + {item?.name ?? getHost(item?.url)} + + dispatch(closeExistingTab(item.id))}> + + + + dispatch(setActiveTab(item.id))}> + + + + ); +}; + +export default Card; + +const tabShowItemStyle = StyleSheet.create({ + cardWrap: { + borderRadius: pTd(8), + width: pTd(160), + height: pTd(214), + marginTop: pTd(24), + borderColor: 'green', + borderWidth: StyleSheet.hairlineWidth, + }, + header: { + ...GStyles.paddingArg(6, 8), + height: pTd(32), + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + icon: { + width: pTd(20), + height: pTd(20), + }, + title: { + flex: 1, + marginLeft: pTd(8), + marginRight: pTd(8), + }, + screenshot: { + width: pTd(160), + height: pTd(182), + }, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx new file mode 100644 index 0000000000..0e1e5b09be --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx @@ -0,0 +1,188 @@ +import React, { useCallback } from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { StyleSheet, TouchableOpacity, View, Share } from 'react-native'; +import { TextL, TextS } from 'components/CommonText'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import Svg from 'components/Svg'; +import { useLanguage } from 'i18n/hooks'; +import GStyles from 'assets/theme/GStyles'; +import CommonAvatar from 'components/CommonAvatar'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { FontStyles } from 'assets/theme/styles'; +import { setStringAsync } from 'expo-clipboard'; +import CommonToast from 'components/CommonToast'; +import { getFaviconUrl } from 'utils'; +import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; + +import { isIOS } from '@rneui/base'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; + +enum HANDLE_TYPE { + REFRESH = 'Refresh', + COPY = 'Copy URL', + SHARE = 'Share', + CLOSE = 'Close', + CANCEL = 'Cancel', + SWITCH = 'Switch', +} + +const handleArray = [ + { title: HANDLE_TYPE.REFRESH, icon: 'refresh1' }, + { title: HANDLE_TYPE.COPY, icon: 'copy1' }, + { title: HANDLE_TYPE.SHARE, icon: 'share' }, + { title: HANDLE_TYPE.SWITCH, icon: 'switch' }, +] as const; + +const BrowserEditModal = ({ + browserInfo, + handleReload, +}: { + browserInfo: IRecordsItemType; + setBrowserInfo: any; + handleReload: any; +}) => { + const { t } = useLanguage(); + const dispatch = useAppCommonDispatch(); + + const handleUrl = useCallback( + async (type: HANDLE_TYPE) => { + let isCopy = false; + + switch (type) { + case HANDLE_TYPE.REFRESH: + handleReload?.(); + OverlayModal.hide(); + break; + + case HANDLE_TYPE.COPY: + isCopy = await setStringAsync(browserInfo?.url || ''); + isCopy && CommonToast.success(t('Copy Success')); + break; + + case HANDLE_TYPE.SHARE: + await Share.share({ + message: isIOS ? browserInfo?.title ?? browserInfo.url : browserInfo?.url, + url: browserInfo?.url ?? browserInfo?.title ?? '', + title: browserInfo?.title ?? browserInfo.url, + }).catch(shareError => { + console.log(shareError); + }); + break; + + case HANDLE_TYPE.CLOSE: + OverlayModal.hide(); + break; + + case HANDLE_TYPE.CANCEL: + OverlayModal.hide(); + break; + + case HANDLE_TYPE.SWITCH: + OverlayModal.hide(); + dispatch(setActiveTab(undefined)); + break; + + default: + break; + } + }, + [handleReload, browserInfo.url, browserInfo?.title, t, dispatch], + ); + + return ( + + + + + {browserInfo?.title} + + + handleUrl(HANDLE_TYPE.CANCEL)}> + + + + + {handleArray.map((ele, index) => ( + handleUrl(ele.title)}> + + + + + {ele.title} + + + ))} + + + handleUrl(HANDLE_TYPE.CANCEL)}> + {t('Cancel')} + + + ); +}; + +export const showBrowserModal = (props: { browserInfo: IRecordsItemType; setBrowserInfo: any; handleReload: any }) => { + OverlayModal.show(, { + position: 'bottom', + containerStyle: { backgroundColor: defaultColors.bg6 }, + }); +}; + +export default { + showBrowserModal, +}; + +const styles = StyleSheet.create({ + modalStyle: { + ...GStyles.paddingArg(16, 20), + backgroundColor: defaultColors.bg6, + width: screenWidth, + }, + headerWrap: {}, + title: { + textAlign: 'left', + height: pTd(22), + lineHeight: pTd(22), + marginVertical: pTd(13), + paddingLeft: pTd(8), + ...fonts.mediumFont, + }, + listWrap: { + marginTop: pTd(24), + marginBottom: pTd(24), + paddingLeft: pTd(12), + display: 'flex', + flexWrap: 'wrap', + flexDirection: 'row', + }, + listItem: { + marginRight: pTd(34), + borderRadius: pTd(6), + overflow: 'hidden', + }, + listItemNoMarginRight: { + marginRight: 0, + }, + svgWrap: { + backgroundColor: defaultColors.bg1, + }, + itemTitle: { + textAlign: 'center', + marginTop: pTd(8), + }, + divider: { + width: '100%', + height: StyleSheet.hairlineWidth, + backgroundColor: defaultColors.bg7, + }, + cancelButton: { + height: pTd(44), + fontSize: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx new file mode 100644 index 0000000000..9dd71438e6 --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -0,0 +1,31 @@ +import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; +import { ScreenWidth } from '@rneui/base'; +import React from 'react'; +import { Drawer } from 'react-native-drawer-layout'; +import TabsDrawerContent from './TabsDrawerContent'; + +interface TabsDrawerPropsType { + children: React.ReactNode; +} + +export default function TabsDrawer(props: TabsDrawerPropsType) { + const { children } = props; + + const { isDrawerOpen } = useAppCASelector(state => state.discover); + + const tabsDrawerContent = React.useMemo(() => , []); + + return ( + console.log('open')} + onClose={() => console.log('close')} + drawerPosition="right" + drawerStyle={{ width: ScreenWidth }} + renderDrawerContent={() => { + return tabsDrawerContent; + }}> + {children} + + ); +} diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index 1f0a673dae..03d5ad80f7 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -9,7 +9,7 @@ import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import useLogOut from 'hooks/useLogOut'; import useInitData from 'hooks/useInitData'; import { useTabMenuList } from 'hooks/cms'; -// import DiscoverHome from 'pages/Discover/DiscoverHome'; +import DiscoverHome from 'pages/Discover/DiscoverHome'; const Tab = createBottomTabNavigator(); type TabMenuTypeType = { icon: IconName; component: React.FC }; @@ -28,14 +28,15 @@ export const tabMenuTypeMap: Record = { icon: 'my', component: MyMenu, }, - // Discover: { - // icon: 'discover', - // component: DiscoverHome, - // }, + Discover: { + icon: 'discover', + component: DiscoverHome, + }, }; export const defaultTabMenuList: TabMenuItem[] = [ { name: 'Wallet', label: 'Wallet', index: 0, icon: 'logo-icon', component: DashBoard }, + { name: 'Discover', label: 'Discover', index: 1, icon: 'discover', component: DiscoverHome }, { name: 'Settings', label: 'My', index: 2, icon: 'my', component: MyMenu }, ]; diff --git a/packages/mobile-app-did/js/navigation/index.tsx b/packages/mobile-app-did/js/navigation/index.tsx index c290fe24ac..fedfbbc31a 100644 --- a/packages/mobile-app-did/js/navigation/index.tsx +++ b/packages/mobile-app-did/js/navigation/index.tsx @@ -23,6 +23,8 @@ import DiscoverNav from 'pages/Discover/index'; import { isIos } from '@portkey-wallet/utils/mobile/device'; import Discover from 'Test/Discover'; +import TabsDrawer from 'components/TabsDrawer'; + const Stack = createStackNavigator(); export const stackNav = [ { name: 'Referral', component: Referral }, @@ -59,19 +61,21 @@ export type RootNavigationProp = StackNavigationProp; export default function NavigationRoot() { return ( - - {stackNav.map((item, index) => ( - - ))} - + + + {stackNav.map((item, index) => ( + + ))} + + ); } diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index d5a684dc64..1ce48c744c 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -17,8 +17,14 @@ import { GamesList } from '../DiscoverHome/GameData'; import { IGameListItemType } from '@portkey-wallet/types/types-ca/discover'; import { isValidUrl } from '@portkey-wallet/utils/reg'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { addRecordsItem } from '@portkey-wallet/store/store-ca/discover/slice'; +import { + addRecordsItem, + changeDrawerOpenStatus, + createNewTab, + setActiveTab, +} from '@portkey-wallet/store/store-ca/discover/slice'; import { isIOS } from '@rneui/base'; +import { checkIsUrl, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; let timer: any = null; @@ -30,7 +36,7 @@ export default function DiscoverSearch() { const iptRef = useRef(); const [value, setValue] = useState(''); const [showRecord, setShowRecord] = useState(true); - const [filterGameList, setFilterGameList] = useState(GamesList); + const [filterGameList] = useState(GamesList); const navBack = useCallback(() => { navigationService.goBack(); @@ -44,18 +50,26 @@ export default function DiscoverSearch() { const onSearch = useCallback(() => { const newValue = value.trim().replace(' ', ''); - // if URL is valid, navigate to webview + const id = Date.now(); - if (isValidUrl(newValue)) { - dispatch(addRecordsItem({ title: newValue, url: newValue })); - navigationService.navigate('ViewOnWebView', { url: newValue, webViewPageType: 'discover' }); - setShowRecord(true); - } else { - // else search in game list - const filterList = GamesList.filter(item => item.name.replace(' ', '').includes(newValue)); - setFilterGameList(filterList); - setShowRecord(false); - } + dispatch(addRecordsItem({ title: newValue, url: prefixUrlWithProtocol(newValue) })); + dispatch(createNewTab({ id, url: newValue })); + dispatch(changeDrawerOpenStatus(true)); + + return; + // if (checkIsUrl(newValue)) { + // dispatch(addRecordsItem({ title: newValue, url: prefixUrlWithProtocol(newValue) })); + // navigationService.navigate('ViewOnWebView', { + // url: prefixUrlWithProtocol(newValue), + // webViewPageType: 'discover', + // }); + // setShowRecord(true); + // } else { + // // else search in game list + // const filterList = GamesList.filter(item => item.name.replace(' ', '').includes(newValue)); + // setFilterGameList(filterList); + // setShowRecord(false); + // } }, [dispatch, value]); useFocusEffect( diff --git a/packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx b/packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx new file mode 100644 index 0000000000..599d5d8d93 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx @@ -0,0 +1,46 @@ +import { TextM } from 'components/CommonText'; +import Svg from 'components/Svg'; +import { useLanguage } from 'i18n/hooks'; +import React from 'react'; +import { ScrollView, TouchableOpacity, View, StyleSheet } from 'react-native'; +import { useDispatch } from 'react-redux'; +import { pTd } from 'utils/unit'; + +export const SwitchTabsContent: React.FC = props => { + const { t } = useLanguage(); + + const dispatch = useDispatch(); + + const closeAllTabs = () => { + dispatch; + }; + + return ( + + + hello + + + + {t('Close All')} + + + + + {t('done')} + + + ); +}; + +const styles = StyleSheet.create({ + container: {}, + handleActionWrap: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + }, + handleActionItem: { + flex: 1, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/index.tsx b/packages/mobile-app-did/js/pages/Discover/index.tsx index a0b0f6d6d9..7874314f6f 100644 --- a/packages/mobile-app-did/js/pages/Discover/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/index.tsx @@ -1,9 +1,11 @@ import DiscoverSearch from './DiscoverSearch'; import Browser from './Browser'; +import DiscoverHome from './DiscoverHome'; const stackNav = [ { name: 'DiscoverSearch', component: DiscoverSearch }, { name: 'Browser', component: Browser }, + { name: 'DiscoverHome', component: DiscoverHome }, ] as const; export default stackNav; diff --git a/packages/mobile-app-did/js/store/rootReducer.ts b/packages/mobile-app-did/js/store/rootReducer.ts index 6d1db1fa0c..e2cef66c27 100644 --- a/packages/mobile-app-did/js/store/rootReducer.ts +++ b/packages/mobile-app-did/js/store/rootReducer.ts @@ -67,6 +67,7 @@ const guardiansPersistConfig = { const discoverPersistConfig = { key: discoverSlice.name, storage: AsyncStorage, + blacklist: ['isDrawerOpen'], }; const paymentPersistConfig = { diff --git a/packages/mobile-app-did/js/utils/webviewScreenShot.ts b/packages/mobile-app-did/js/utils/webviewScreenShot.ts new file mode 100644 index 0000000000..137e3542b3 --- /dev/null +++ b/packages/mobile-app-did/js/utils/webviewScreenShot.ts @@ -0,0 +1,22 @@ +import { captureScreen } from 'react-native-view-shot'; + +export const takeScreenshot = () => + new Promise((resolve, reject) => { + captureScreen({ + format: 'jpg', + quality: 0.2, + }).then( + uri => { + console.log('uri', uri); + // updateTab(tabID, { + // url, + // image: uri, + // }); + resolve(true); + }, + error => { + // Logger.error(error, `Error saving tab ${url}`); + reject(error); + }, + ); + }); diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 63df9dfe73..15108f479a 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -1,9 +1,12 @@ import { createSlice } from '@reduxjs/toolkit'; import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; -import { DiscoverStateType } from './type'; +import { IDiscoverStateType } from './type'; -const initialState: DiscoverStateType = { +const initialState: IDiscoverStateType = { + isDrawerOpen: false, recordsList: [], + whiteList: [], + activeTabId: -1, tabs: [], }; @@ -37,10 +40,44 @@ export const discoverSlice = createSlice({ clearRecordsList: state => { state.recordsList = []; }, + closeAllTabs: (state, { payload }) => { + state.tabs = []; + state.activeTabId = -1; + console.log('closeAllTabs', payload); + }, + createNewTab: (state, { payload }) => { + state.activeTabId = payload.id; + state.tabs.push(payload); + console.log('createNewTab', payload); + }, + closeExistingTab: (state, { payload }) => { + state.tabs = state.tabs.filter(item => item.id !== payload); + }, + setActiveTab: (state, { payload }) => { + state.activeTabId = payload; + }, + updateTab: (state, { payload }) => { + state.tabs = state.tabs.map(item => (item.id === payload.id ? { ...item, ...payload } : item)); + }, + changeDrawerOpenStatus: (state, { payload }) => { + console.log('payload', payload); + state.isDrawerOpen = payload; + }, resetDiscover: () => initialState, }, }); -export const { addRecordsItem, upDateRecordsItem, clearRecordsList, resetDiscover } = discoverSlice.actions; +export const { + addRecordsItem, + upDateRecordsItem, + clearRecordsList, + resetDiscover, + closeAllTabs, + createNewTab, + closeExistingTab, + setActiveTab, + updateTab, + changeDrawerOpenStatus, +} = discoverSlice.actions; export default discoverSlice; diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index c76fa7e0fd..e9ed752091 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -1,6 +1,15 @@ import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; -export interface DiscoverStateType { +export interface ITabItem { + id: string | number; + name: string; + url: string; + screenShotUrl: string; +} +export interface IDiscoverStateType { + isDrawerOpen: boolean; recordsList: IRecordsItemType[]; - tabs: any[]; + whiteList: any[]; + activeTabId: number; + tabs: ITabItem[]; } diff --git a/packages/types/types-ca/store.ts b/packages/types/types-ca/store.ts index 93751549cb..d91940451a 100644 --- a/packages/types/types-ca/store.ts +++ b/packages/types/types-ca/store.ts @@ -14,7 +14,7 @@ import { guardiansSlice } from '@portkey-wallet/store/store-ca/guardians/slice'; import { contactSlice, ContactState } from '@portkey-wallet/store/store-ca/contact/slice'; import { ActivityStateType } from '@portkey-wallet/store/store-ca/activity/type'; import { discoverSlice } from '@portkey-wallet/store/store-ca/discover/slice'; -import { DiscoverStateType } from '@portkey-wallet/store/store-ca/discover/type'; +import { IDiscoverStateType } from '@portkey-wallet/store/store-ca/discover/type'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; import { PaymentStateType } from '@portkey-wallet/store/store-ca/payment/type'; @@ -35,7 +35,7 @@ export type CACommonState = RootCommonState & { [walletSlice.name]: WalletState; [contactSlice.name]: ContactState; [guardiansSlice.name]: GuardiansState; - [discoverSlice.name]: DiscoverStateType; + [discoverSlice.name]: IDiscoverStateType; [paymentSlice.name]: PaymentStateType; [switchSlice.name]: SwitchStateTypes; [miscSlice.name]: MiscState; diff --git a/packages/web-extension-did/app/web/store/Provider/Updater.tsx b/packages/web-extension-did/app/web/store/Provider/Updater.tsx index a4c0945824..14f271d7ce 100644 --- a/packages/web-extension-did/app/web/store/Provider/Updater.tsx +++ b/packages/web-extension-did/app/web/store/Provider/Updater.tsx @@ -16,7 +16,7 @@ import { useCheckManager } from '@portkey-wallet/hooks/hooks-ca/graphql'; import { useCheckUpdate } from 'hooks/useCheckUpdate'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; import { useLocation } from 'react-router'; -import { useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useSocialMediaList, useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; keepAliveOnPages({}); @@ -56,5 +56,6 @@ export default function Updater() { }, [onLocking]); usePhoneCountryCode(true); useSocialMediaList(true); + useDiscoverGroupList(true); return null; } From 69c99196208c46c2dc1495660322d299afd7134d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 7 Jun 2023 20:24:19 +0800 Subject: [PATCH 047/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20dapp=20file=20n?= =?UTF-8?q?ame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/dapp/MobileStream.ts | 2 +- packages/mobile-app-did/js/dapp/dappMobileOperator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/MobileStream.ts b/packages/mobile-app-did/js/dapp/MobileStream.ts index 2a7b2531be..0ef4ce664f 100644 --- a/packages/mobile-app-did/js/dapp/MobileStream.ts +++ b/packages/mobile-app-did/js/dapp/MobileStream.ts @@ -1,4 +1,4 @@ -import { DappInteractionStream } from '@portkey/providers/dist/DappStream'; +import { DappInteractionStream } from '@portkey/providers/dist/dappStream'; import type WebView from 'react-native-webview'; export class MobileStream extends DappInteractionStream { private _webViewRef: WebView; diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 68d2d12cc2..e73bd27ea8 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -13,7 +13,7 @@ import DappEventBus from './dappEventBus'; import { generateNormalResponse, generateErrorResponse } from '@portkey/provider-utils'; import { IDappManager } from '@portkey-wallet/types/types-ca/dapp'; import { IDappOverlay } from './dappOverlay'; -import { Operator } from '@portkey/providers/dist/Operator'; +import { Operator } from '@portkey/providers/dist/operator'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount, getPin } from 'utils/redux'; From 69c56c09a4ce39a3d2741862491503289bce701c Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 7 Jun 2023 20:44:37 +0800 Subject: [PATCH 048/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20calc=20fee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/ConnectWallet/index.tsx | 10 +- .../app/web/pages/SendTransactions/index.less | 22 +-- .../app/web/pages/SendTransactions/index.tsx | 143 ++++++++++++------ .../SendTransactions/utils/getTransferFee.ts | 52 +++++++ .../app/web/store/Provider/config.ts | 1 + 5 files changed, 167 insertions(+), 61 deletions(-) create mode 100644 packages/web-extension-did/app/web/pages/SendTransactions/utils/getTransferFee.ts diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index b6898c7521..e63c5fd74e 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -21,12 +21,18 @@ export default function ConnectWallet() { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { currentNetwork } = useWalletInfo(); + const disabled = useMemo(() => !detail.origin, [detail]); const renderSite = useMemo( () => detail && (
    - + {detail.appHref}
    ), @@ -76,7 +82,7 @@ export default function ConnectWallet() { }}> {t('Reject')} -
    diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.less b/packages/web-extension-did/app/web/pages/SendTransactions/index.less index 8444784d14..f5e64d7c09 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.less +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.less @@ -77,15 +77,15 @@ } .fee, .total { margin-bottom: 12px; + .fee-amount, .total-amount { + text-align: right; + } .elf { font-size: 14px; line-height: 20px; font-weight: 500; color: @font-11; } - .dollar { - margin-left: 4px; - } } } .message-wrapper { @@ -114,14 +114,14 @@ } } .fee { - .elf { - font-size: 14px; - line-height: 20px; - font-weight: 500; - color: @font-11; - } - .dollar { - margin-left: 4px; + .fee-amount { + text-align: right; + .elf { + font-size: 14px; + line-height: 20px; + font-weight: 500; + color: @font-11; + } } } } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 3778d7c761..272deed609 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -1,6 +1,6 @@ import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { ChainId } from '@portkey-wallet/types'; +import { ChainId, ChainType } from '@portkey-wallet/types'; import { ZERO } from '@portkey-wallet/constants/misc'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; @@ -10,16 +10,18 @@ import { Button, message } from 'antd'; import CustomSvg from 'components/CustomSvg'; import { useTranslation } from 'react-i18next'; import usePromptSearch from 'hooks/usePromptSearch'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useUserInfo, useWalletInfo } from 'store/Provider/hooks'; import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; import { callSendMethod } from 'utils/sandboxUtil/sendTransactions'; import { useAmountInUsdShow } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; +import getTransferFee from './utils/getTransferFee'; +import { ChainItemType } from '@portkey-wallet/store/store-ca/wallet/type'; import './index.less'; export default function SendTransactions() { - const detail = usePromptSearch<{ + const { payload } = usePromptSearch<{ payload: { chainId: ChainId; contractAddress: string; @@ -27,24 +29,79 @@ export default function SendTransactions() { params: any; }; }>(); - const chainInfo = useCurrentChain(detail?.payload?.chainId); + const chainInfo = useCurrentChain(payload?.chainId); const wallet = useCurrentWalletInfo(); const { walletName } = useWalletInfo(); const { currentNetwork } = useWalletInfo(); const isMainnet = useIsMainnet(); const { t } = useTranslation(); - const { - payload: { params }, - } = detail; const { passwordSeed } = useUserInfo(); const amountInUsdShow = useAmountInUsdShow(); + const [fee, setFee] = useState(''); + const isCAManagerForwardCall = useMemo( + () => chainInfo?.caContractAddress !== payload?.contractAddress, + [chainInfo, payload], + ); + const privateKey = useMemo( + () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), + [passwordSeed, wallet.AESEncryptPrivateKey], + ); + + const getFee = useCallback(async () => { + let paramsOption = {}; + if (isCAManagerForwardCall) { + paramsOption = { + caHash: wallet.caHash, + contractAddress: payload?.contractAddress, + methodName: payload?.method, + args: payload?.params?.paramsOption, + }; + } else { + paramsOption = { + caHash: wallet.caHash, + ...payload?.params?.paramsOption, + }; + } + try { + const params = { + isCAManagerForwardCall, + contractAddress: payload?.contractAddress, + privateKey: privateKey as string, + chainInfo: chainInfo as ChainItemType, + chainType: 'aelf' as ChainType, + paramsOption, + }; + const fee = await getTransferFee(params); + setFee(fee); + } catch (error) { + console.log('get fee error', error); + } + }, [chainInfo, isCAManagerForwardCall, payload, privateKey, wallet]); useEffect(() => { - // TODO fee - }, []); + getFee(); + }, [getFee]); + const renderAccountInfo = useMemo( + () => + payload?.contractAddress ? ( +
    +
    {walletName}
    + +
    {`${payload?.contractAddress.slice(0, 10)}...${payload?.contractAddress.slice( + -4, + )}`}
    +
    +
    + ) : ( + <> + ), + [payload, walletName], + ); + + // Transfer const renderDetail = useMemo(() => { - const { symbol, amount, decimals } = params.paramsOption; + const { symbol, amount, decimals } = payload?.params?.paramsOption || {}; return (
    Details
    @@ -57,44 +114,45 @@ export default function SendTransactions() {
    Transaction Fee
    -
    -
    {`${formatAmountShow(params.paramsOption.fee)} ELF`}
    - {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    } +
    +
    {`${formatAmountShow(amount)} ELF`}
    + {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    }
    Total (Amount + Transaction Fee)
    -
    -
    {`${formatAmountShow(ZERO.plus(amount).plus(params.paramsOption.fee))} ELF`}
    - {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    } +
    +
    {`${formatAmountShow(ZERO.plus(amount).plus(fee))} ELF`}
    + {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    }
    ); - }, [amountInUsdShow, isMainnet, params.paramsOption]); + }, [amountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); + const renderMessage = useMemo(() => { - const { messageDetail } = params.paramsOption; + const params = payload?.params?.paramsOption || {}; return (
    Message
    -
    String to be sign
    -
    {messageDetail.sign}
    -
    Method
    -
    {messageDetail.method}
    -
    Signature
    -
    {messageDetail.signature}
    + {Object.keys(params).map((item) => ( + <> +
    {item}
    +
    {params[item]}
    + + ))}
    Transaction Fee
    -
    -
    {`${formatAmountShow(params.paramsOption.fee)} ELF`}
    - {isMainnet &&
    $0.12
    } +
    +
    {`${formatAmountShow(fee)} ELF`}
    + {isMainnet &&
    {amountInUsdShow(fee, 0, 'ELF')}
    }
    ); - }, [isMainnet, params.paramsOption]); + }, [amountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); const sendHandler = useCallback(async () => { try { @@ -102,21 +160,18 @@ export default function SendTransactions() { closePrompt({ ...errorHandler(400001), data: { code: 4002, msg: 'invalid chain id' } }); return; } - const { payload } = detail; - const isCAManagerForwardCall = chainInfo.caContractAddress !== payload.contractAddress; - let paramsOption = payload.params?.paramsOption; + let paramsOption = payload?.params?.paramsOption; - const functionName = isCAManagerForwardCall ? 'ManagerForwardCall' : payload.method; + const functionName = isCAManagerForwardCall ? 'ManagerForwardCall' : payload?.method; paramsOption = isCAManagerForwardCall ? { caHash: wallet.caHash, - methodName: payload.method, - contractAddress: payload.contractAddress, + methodName: payload?.method, + contractAddress: payload?.contractAddress, args: paramsOption, } : paramsOption; - const privateKey = aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed); if (!privateKey) throw 'Invalid user information, please check'; const result = await callSendMethod({ rpcUrl: chainInfo.endPoint, @@ -135,28 +190,20 @@ export default function SendTransactions() { console.error(error, 'error===detail'); message.error(handleErrorMessage(error)); } - }, [chainInfo, detail, wallet, passwordSeed]); + }, [chainInfo, wallet, payload, isCAManagerForwardCall, privateKey]); return (
    - {formatChainInfoToShow(detail.payload.chainId, currentNetwork)} -
    -
    -
    {walletName}
    - -
    {`${detail.payload.contractAddress.slice( - 0, - 10, - )}...${detail.payload.contractAddress.slice(-4)}`}
    -
    + {formatChainInfoToShow(payload?.chainId, currentNetwork)}
    + {renderAccountInfo}
    Method
    -
    {detail.payload.method}
    +
    {payload?.method}
    - {detail.payload.method.toLowerCase() === 'transfer' ? renderDetail : renderMessage} + {payload?.method.toLowerCase() === 'transfer' ? renderDetail : renderMessage}
    - {renderSelectELe} {isPrompt ? : null}
    ), [ + amount, + curFiat, + curToken, disabled, + errMsg, handleBack, + handleInputChange, handleNext, handlePageChange, + handleSelect, isPrompt, page, rate, - renderBuyForm, + receive, renderRate, - renderSelectELe, - renderSellForm, t, ], ); diff --git a/packages/web-extension-did/app/web/pages/BuyTest/Confirm/index.less b/packages/web-extension-did/app/web/pages/BuyTest/Confirm/index.less deleted file mode 100644 index a723ae4980..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/Confirm/index.less +++ /dev/null @@ -1,81 +0,0 @@ -@import '../../../assets/theme/color.less'; - -.confirm-frame { - height: 100vh; - background-color: @bg-11; - .confirm-title { - padding-top: 16px; - background-color: @bg-13; - } - - .confirm-content { - padding:0 16px; - flex: 1; - - .content { - margin-top: 24px; - } - - .label { - margin-bottom: 8px; - font-size: 12px; - line-height: 16px; - color: @font-13; - } - .item { - height: 60px; - font-size: 14px; - line-height: 20px; - color: @font-11; - border: 1px solid @border-2; - border-radius: 6px; - } - .receive-item { - padding-left: 12px; - align-items: center; - } - - .address-item { - padding: 12px; - align-items: center; - } - - .visa { - border: 1px solid @border-2; - border-radius: 6px; - align-items: center; - height: 72px; - .custom-svg { - margin-left: 12px; - width: 44px; - height: 28px; - } - .card-type { - margin-left: 16px; - font-size: 16px; - line-height: 22px; - color: @font-11; - } - .card-number { - margin-left: 8px; - font-size: 14px; - line-height: 20px; - color: @font-13; - } - } - } - - .confirm-footer { - width: 100%; - height: 80px; - padding: 16px; - border-top: 1px solid @border-2; - - .@{app-prefix}-btn { - height: 48px; - border-radius: 22px; - font-size: 16px; - line-height: 22px; - } - } -} diff --git a/packages/web-extension-did/app/web/pages/BuyTest/Confirm/index.tsx b/packages/web-extension-did/app/web/pages/BuyTest/Confirm/index.tsx deleted file mode 100644 index 3fb64f3917..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/Confirm/index.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Button, message } from 'antd'; -import BackHeader from 'components/BackHeader'; -import CustomSvg from 'components/CustomSvg'; -import { useLocation, useNavigate } from 'react-router'; -import { confirmReceiveText, overTimeText, visaCardNum, visaCardType } from '../const'; -import { useCurrentWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { getEntireDIDAelfAddress } from '@portkey-wallet/utils/aelf'; -import getELF from 'utils/sandboxUtil/getELF'; -import { timesDecimals } from '@portkey-wallet/utils/converter'; -import { useCommonState, useLoading, useUserInfo } from 'store/Provider/hooks'; -import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; -import aes from '@portkey-wallet/utils/aes'; -import PromptFrame from 'pages/components/PromptFrame'; -import clsx from 'clsx'; -import './index.less'; - -export default function Confirm() { - const { t } = useTranslation(); - const navigate = useNavigate(); - const chainInfo = useCurrentChain('AELF'); - const { state } = useLocation(); - const { isPrompt } = useCommonState(); - const { passwordSeed } = useUserInfo(); - const { walletInfo } = useCurrentWallet(); - const { setLoading } = useLoading(); - const currentNetwork = useCurrentNetworkInfo(); - const caAddress = useMemo( - () => getEntireDIDAelfAddress(walletInfo.AELF?.caAddress || '', undefined, 'AELF'), - [walletInfo], - ); - const handleBack = useCallback(() => { - navigate('/buy-test/preview', { state: state }); - }, [navigate, state]); - - const handleConfirm = useCallback(async () => { - try { - if (!passwordSeed) return message.error('Missing pin'); - const privateKey = aes.decrypt(walletInfo.AESEncryptPrivateKey, passwordSeed); - if (!privateKey) return message.error('Missing private key'); - if (!chainInfo) return message.error('Missing chainInfo'); - if (!currentNetwork.tokenClaimContractAddress) return message.error('Missing tokenClaimContractAddress'); - setLoading(true); - await getELF({ - chainInfo, - chainType: currentNetwork.walletType, - privateKey, - address: currentNetwork.tokenClaimContractAddress, - caHash: walletInfo.AELF?.caHash || '', - amount: timesDecimals(100, 8).toNumber(), - }); - navigate('/'); - } catch (error) { - message.error(overTimeText); - console.log('getELF error', error); - } finally { - setLoading(false); - } - }, [chainInfo, currentNetwork, navigate, passwordSeed, setLoading, walletInfo]); - - const mainContent = useMemo( - () => ( -
    -
    - } - /> -
    -
    -
    -
    {t('Pay with')}
    -
    - - {visaCardType} - {visaCardNum} -
    -
    -
    -
    {t('Wallet address')}
    -
    - {caAddress?.replace(/(?<=^\w{18})\w+(?=\w{17})/, '...')} -
    -
    -
    -
    {t('You will get')}
    -
    - {confirmReceiveText} -
    -
    -
    -
    - -
    -
    - ), - [caAddress, handleBack, handleConfirm, isPrompt, t], - ); - - return <>{isPrompt ? : mainContent}; -} diff --git a/packages/web-extension-did/app/web/pages/BuyTest/Preview/index.less b/packages/web-extension-did/app/web/pages/BuyTest/Preview/index.less deleted file mode 100644 index 54e1507bc0..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/Preview/index.less +++ /dev/null @@ -1,84 +0,0 @@ -@import '../../../assets/theme/color.less'; - -.preview-frame { - height: 100vh; - background-color: @bg-11; - .preview-title { - padding-top: 16px; - background-color: @bg-13; - } - - .preview-content { - padding: 0 16px; - flex: 1; - - .transaction { - margin: 40px; - .send { - font-weight: 500; - color: @font-11; - .amount { - font-size: 28px; - line-height: 28px; - } - .currency { - margin-left: 8px; - font-size: 12px; - line-height: 16px; - } - } - .receive { - margin-top: 12px; - font-size: 12px; - line-height: 16px; - color: @font-13; - } - } - - .card { - width: 100%; - .label { - margin-bottom: 8px; - font-size: 12px; - line-height: 16px; - } - .visa { - border: 1px solid @border-2; - border-radius: 6px; - align-items: center; - height: 72px; - .custom-svg { - margin-left: 12px; - width: 44px; - height: 28px; - } - .card-type { - margin-left: 16px; - font-size: 16px; - line-height: 22px; - color: @font-11; - } - .card-number { - margin-left: 8px; - font-size: 14px; - line-height: 20px; - color: @font-13; - } - } - } - } - - .preview-footer { - width: 100%; - height: 80px; - padding: 16px; - border-top: 1px solid @border-2; - - .@{app-prefix}-btn { - height: 48px; - border-radius: 22px; - font-size: 16px; - line-height: 22px; - } - } -} diff --git a/packages/web-extension-did/app/web/pages/BuyTest/Preview/index.tsx b/packages/web-extension-did/app/web/pages/BuyTest/Preview/index.tsx deleted file mode 100644 index 6a5e4233b5..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/Preview/index.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Button } from 'antd'; -import BackHeader from 'components/BackHeader'; -import CustomSvg from 'components/CustomSvg'; -import { useLocation, useNavigate } from 'react-router'; -import { payment, receiveText, visaCardNum, visaCardType } from '../const'; -import PromptFrame from 'pages/components/PromptFrame'; -import { useCommonState } from 'store/Provider/hooks'; -import clsx from 'clsx'; -import './index.less'; - -export default function Preview() { - const { t } = useTranslation(); - const navigate = useNavigate(); - const { state } = useLocation(); - const { isPrompt } = useCommonState(); - - const handleBack = useCallback(() => { - navigate('/buy-test', { state: state }); - }, [navigate, state]); - - const handleProceed = useCallback(() => { - navigate('/buy-test/confirm', { state: state }); - }, [navigate, state]); - - const mainContent = useMemo( - () => ( -
    -
    - } - /> -
    -
    -
    -
    - {payment} - USD -
    -
    {receiveText}
    -
    -
    -
    {t('Choose payment method')}
    -
    - - {visaCardType} - {visaCardNum} -
    -
    -
    -
    - -
    -
    - ), - [handleBack, handleProceed, isPrompt, t], - ); - return <>{isPrompt ? : mainContent}; -} diff --git a/packages/web-extension-did/app/web/pages/BuyTest/const.ts b/packages/web-extension-did/app/web/pages/BuyTest/const.ts deleted file mode 100644 index 755fe79de9..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/const.ts +++ /dev/null @@ -1,27 +0,0 @@ -export enum PageType { - buy, - sell, -} - -export const rate = 0.3077; -export const receive = 100; -export const payment = 30.77; -export const MAX_UPDATE_TIME = 15; -export const curToken = { - symbol: 'ELF', - chainId: 'AELF', -}; -export const curFiat = { - currency: 'USD', - country: 'US', -}; -export const testnetTip = - "This is a simulated on-ramp purchase on aelf's Testnet with virtual payment method. The tokens you will receive are Testnet tokens."; -export const sellSoonText = - 'Off-ramp is currently not supported. It will be enabled once Portkey launches on aelf Mainnet.'; -export const receiveText = 'I will receive ≈ 100 ELF'; -export const showRateText = `1 ELF ≈ ${rate} USD`; -export const confirmReceiveText = `100 ELF for ${payment} USD`; -export const visaCardType = 'Visa Card'; -export const visaCardNum = '(****7760)'; -export const overTimeText = "Today's limit has been reached."; diff --git a/packages/web-extension-did/app/web/pages/BuyTest/index.less b/packages/web-extension-did/app/web/pages/BuyTest/index.less deleted file mode 100644 index 95091acab3..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/index.less +++ /dev/null @@ -1,143 +0,0 @@ -@import '../../assets/theme/color.less'; - -.buy-frame { - height: 100vh; - background-color: @bg-11; - .buy-title { - padding-top: 16px; - background-color: @bg-13; - } - - .buy-content { - padding: 0 24px; - flex: 1; - - .buy-radio { - margin: 16px; - .@{app-prefix}-radio-group-solid { - display: flex; - font-size: 14px; - line-height: 20px; - font-weight: 500; - background-color: @bg-10; - border-radius: 6px; - .@{app-prefix}-radio-button-wrapper { - display: flex; - justify-content: center; - margin: 3px; - width: 88px; - height: 30px; - background-color: @bg-10; - color: @font-12; - border: none; - &:not(:first-child)::before { - display: none; - } - &:focus-within { - box-shadow: none; - } - } - .@{app-prefix}-radio-button-wrapper-checked { - background-color: @bg-11; - box-shadow: none; - color: @font-11; - border-radius: 6px; - } - } - } - .buy-input { - margin-top: 16px; - width: 100%; - .label { - font-size: 14px; - line-height: 20px; - color: @font-13; - } - .@{app-prefix}-input-affix-wrapper { - margin-top: 8px; - padding: 16px; - height: 56px; - border-radius: 6px; - border: 1px solid @border-1; - input { - font-size: 24px; - line-height: 28px; - font-weight: 500; - color: @font-11; - } - .@{app-prefix}-input-suffix { - display: flex; - padding-left: 12px; - border-left: 1px solid @border-2; - .img, - .elf-icon-icon { - width: 24px; - height: 24px; - border-radius: 50%; - img, - svg { - width: 100%; - height: 100%; - } - } - .currency{ - font-size: 16px; - line-height: 22px; - font-weight: 500; - color: @font-11; - } - } - } - .@{app-prefix}-input-affix-wrapper:focus, - .@{app-prefix}-input-affix-wrapper-focused { - box-shadow: none; - } - .error-text { - margin-top: 4px; - font-size: 12px; - line-height: 16px; - color: @font-14; - } - } - .buy-target { - width: 100%; - } - .buy-rate { - width: 100%; - margin-top: 24px; - font-size: 14px; - line-height: 20px; - color: @font-13; - .timer { - .custom-svg { - margin-right: 5px; - } - .timestamp { - font-size: 12px; - line-height: 16px; - } - } - } - } - - .testnet-tip { - padding: 0 24px 12px; - font-size: 14px; - line-height: 20px; - color: @font-13; - } - - .buy-footer { - width: 100%; - height: 80px; - padding: 16px; - border-top: 1px solid @border-2; - - .@{app-prefix}-btn { - height: 48px; - border-radius: 22px; - font-size: 16px; - line-height: 22px; - } - } -} diff --git a/packages/web-extension-did/app/web/pages/BuyTest/index.tsx b/packages/web-extension-did/app/web/pages/BuyTest/index.tsx deleted file mode 100644 index 5ac4b72385..0000000000 --- a/packages/web-extension-did/app/web/pages/BuyTest/index.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Button, Input, Radio, RadioChangeEvent } from 'antd'; -import BackHeader from 'components/BackHeader'; -import CustomSvg from 'components/CustomSvg'; -import { useLocation, useNavigate } from 'react-router'; -import { countryCodeMap } from '@portkey-wallet/constants/constants-ca/payment'; -import { - curFiat, - curToken, - MAX_UPDATE_TIME, - PageType, - payment, - receive, - sellSoonText, - showRateText, - testnetTip, -} from './const'; -import PromptFrame from 'pages/components/PromptFrame'; -import { useCommonState } from 'store/Provider/hooks'; -import clsx from 'clsx'; -import CustomModal from 'pages/components/CustomModal'; -import './index.less'; - -export default function Buy() { - const { t } = useTranslation(); - const navigate = useNavigate(); - const { state } = useLocation(); - const { isPrompt } = useCommonState(); - const timerRef = useRef(); - const [rateUpdateTime, setRateUpdateTime] = useState(MAX_UPDATE_TIME); - - const handlePageChange = useCallback((e: RadioChangeEvent) => { - if (e.target.value === PageType.sell) { - CustomModal({ - content: sellSoonText, - }); - } - }, []); - - useEffect(() => { - const timer = setInterval(() => { - if (rateUpdateTime === 0) { - setRateUpdateTime(MAX_UPDATE_TIME); - } else { - setRateUpdateTime(rateUpdateTime - 1); - } - }, 1000); - timerRef.current = timer; - return () => { - clearInterval(timerRef.current); - }; - }, [rateUpdateTime]); - - const renderTokenInput = useMemo(() => { - return ( - - -
    {curToken.symbol}
    - - } - /> - ); - }, []); - - const renderCurrencyInput = useMemo(() => { - return ( - -
    - -
    -
    {curFiat.currency}
    - - } - /> - ); - }, []); - - const handleNext = useCallback(() => { - navigate('/buy-test/preview', { state: state }); - }, [navigate, state]); - - const handleBack = useCallback(() => { - if (state && state.tokenInfo) { - navigate('/token-detail', { state: state.tokenInfo }); - } else { - navigate('/'); - } - }, [navigate, state]); - - const mainContent = useMemo( - () => ( -
    -
    - } - /> -
    -
    -
    - - {t('Buy')} - {t('Sell')} - -
    -
    -
    I want to pay
    - {renderCurrencyInput} -
    -
    -
    I will receive ≈
    - {renderTokenInput} -
    -
    -
    {showRateText}
    -
    - -
    {rateUpdateTime}s
    -
    -
    -
    -
    -
    {testnetTip}
    -
    - -
    -
    -
    - ), - [handleBack, handleNext, handlePageChange, isPrompt, rateUpdateTime, renderCurrencyInput, renderTokenInput, t], - ); - - return <>{isPrompt ? : mainContent}; -} diff --git a/packages/web-extension-did/app/web/pages/Home/index.tsx b/packages/web-extension-did/app/web/pages/Home/index.tsx index 486a650980..ddf4dfada2 100644 --- a/packages/web-extension-did/app/web/pages/Home/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/index.tsx @@ -7,8 +7,24 @@ import popupHandler from 'utils/popupHandler'; import { getLocalStorage } from 'utils/storage/chromeStorage'; import MyBalance from './components/MyBalance'; import './index.less'; +// import { useHandleAchSell } from 'pages/Buy/hooks/useHandleAchSell'; +// import { getPaymentOrderNo } from '@portkey-wallet/api/api-did/payment/util'; +// import { ACH_MERCHANT_NAME, TransDirectEnum } from '@portkey-wallet/constants/constants-ca/payment'; export default function Home() { + // TODO SELL + // const handleAchSell = useHandleAchSell(); + + // useEffect(() => { + // getPaymentOrderNo({ + // transDirect: TransDirectEnum.TOKEN_SELL, + // merchantName: ACH_MERCHANT_NAME, + // }).then((orderNo) => { + // // const orderNo = 'f5298810-35f2-d210-2eba-3a0b8ce563cb'; // mock orderNo; + // handleAchSell(orderNo); + // }); + // }, [handleAchSell]); + const navigate = useNavigate(); const { isPopupInit, isPrompt, isNotLessThan768 } = useCommonState(); diff --git a/packages/web-extension-did/app/web/types/index.d.ts b/packages/web-extension-did/app/web/types/index.d.ts index ba7e6f546f..d30669b5a5 100644 --- a/packages/web-extension-did/app/web/types/index.d.ts +++ b/packages/web-extension-did/app/web/types/index.d.ts @@ -16,3 +16,8 @@ export type ReCaptchaResponseParams = { message?: string; name?: string; }; + +export interface IKeyDownParams { + key: string; + preventDefault: () => any; +} diff --git a/packages/web-extension-did/app/web/utils/keyDown.ts b/packages/web-extension-did/app/web/utils/keyDown.ts index 5008fa01ca..f291527014 100644 --- a/packages/web-extension-did/app/web/utils/keyDown.ts +++ b/packages/web-extension-did/app/web/utils/keyDown.ts @@ -1,4 +1,6 @@ -export function handleKeyDown(e: { key: string; preventDefault: () => any }) { +import { IKeyDownParams } from 'types'; + +export function handleKeyDown(e: IKeyDownParams) { const allow = [ ...Array(10) .fill('') @@ -15,7 +17,7 @@ export function handleKeyDown(e: { key: string; preventDefault: () => any }) { e.preventDefault(); } } -export function handleKeyDownInt(e: { key: string; preventDefault: () => any }) { +export function handleKeyDownInt(e: IKeyDownParams) { const allow = [ ...Array(10) .fill('') diff --git a/packages/web-extension-did/app/web/utils/sandboxUtil/sameChainTransfer.ts b/packages/web-extension-did/app/web/utils/sandboxUtil/sameChainTransfer.ts index d20aeb42e6..963ae97f0e 100644 --- a/packages/web-extension-did/app/web/utils/sandboxUtil/sameChainTransfer.ts +++ b/packages/web-extension-did/app/web/utils/sandboxUtil/sameChainTransfer.ts @@ -22,7 +22,7 @@ const sameChainTransfer = async ({ toAddress: string; memo?: string; }) => { - await managerForwardCall({ + return await managerForwardCall({ rpcUrl: chainInfo.endPoint, chainType, address: chainInfo.caContractAddress, From 681418f53145d07465278fa1589c1b3238534b8c Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 9 Jun 2023 15:22:34 +0800 Subject: [PATCH 057/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20updateA?= =?UTF-8?q?lchemyOrderTxHash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/index.ts | 2 +- packages/hooks/hooks-ca/payment.ts | 2 +- .../Buy/BuyHome/components/SellForm/index.tsx | 21 ++++++------ .../mobile-app-did/js/pages/Buy/hooks.tsx | 32 +++++++++++-------- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/packages/api/api-did/payment/index.ts b/packages/api/api-did/payment/index.ts index 134abaa6a9..1bcc4a80e1 100644 --- a/packages/api/api-did/payment/index.ts +++ b/packages/api/api-did/payment/index.ts @@ -12,5 +12,5 @@ export default { getOrderNo: '/api/app/thirdPart/order', getAchSignature: '/api/app/thirdPart/alchemy/signature', updateAchOrder: '/api/app/thirdPart/order/alchemy', - setOrderTxHash: 'api/app/thirdPart/txHash', + updateAlchemyOrderTxHash: 'api/app/thirdPart/alchemy/txHash', } as const; diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 2294b7b2cb..3a11cf4834 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -76,7 +76,7 @@ export const useSellTransfer = () => { throw new Error('transaction is error'); } - await request.payment.setOrderTxHash({ + await request.payment.updateAlchemyOrderTxHash({ params: { merchantName: ACH_MERCHANT_NAME, orderId, diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index 6e9b642078..488a790b2d 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -135,6 +135,9 @@ export default function SellForm() { return; } if (!reg.test(text)) return; + const arr = text.split('.'); + if (arr[1]?.length > 8) return; + if (arr.join('').length > 13) return; setAmount(text); }, []); @@ -175,6 +178,13 @@ export default function SellForm() { // TODO: add Toast return; } + + if (isRefreshReceiveValid.current === false) { + const rst = await refreshReceive(); + if (rst === undefined) return; + _rate = rst.rate; + _receiveAmount = rst.receiveAmount; + } } catch (error) { // TODO: add Toast console.log('error', error); @@ -183,15 +193,6 @@ export default function SellForm() { Loading.hide(); } - if (isRefreshReceiveValid.current === false) { - Loading.show(); - const rst = await refreshReceive(); - Loading.hide(); - if (rst === undefined) return; - _rate = rst.rate; - _receiveAmount = rst.receiveAmount; - } - navigationService.navigate('BuyPreview', { amount, fiat, @@ -226,7 +227,7 @@ export default function SellForm() { } type="general" - maxLength={30} + maxLength={14} autoCorrect={false} keyboardType="decimal-pad" onChangeText={onAmountInput} diff --git a/packages/mobile-app-did/js/pages/Buy/hooks.tsx b/packages/mobile-app-did/js/pages/Buy/hooks.tsx index 81bb389f38..3b4eee8147 100644 --- a/packages/mobile-app-did/js/pages/Buy/hooks.tsx +++ b/packages/mobile-app-did/js/pages/Buy/hooks.tsx @@ -31,7 +31,8 @@ export const useReceive = ( const [amountError, setAmountError] = useState(INIT_NONE_ERROR); - const isAllowAmount = useMemo(() => { + const isAllowAmountRef = useRef(false); + isAllowAmountRef.current = useMemo(() => { const reg = /^\d+(\.\d+)?$/; if (amount === '' || !reg.test(amount)) return false; return true; @@ -71,7 +72,14 @@ export const useReceive = ( const lastParams = useRef(); const refreshReceive = useCallback(async () => { - if (fiat === undefined || token === undefined || !isAllowAmount) return; + if (amount === '') { + setRate(''); + setReceiveAmount(''); + clearRefreshReceive(); + return; + } + + if (fiat === undefined || token === undefined || !isAllowAmountRef.current) return; if (limitAmountRef) { if (limitAmountRef.current === undefined) return; @@ -144,17 +152,7 @@ export const useReceive = ( // TODO: add error // CommonToast.failError('get order error'); } - }, [ - amount, - clearRefreshReceive, - fiat, - isAllowAmount, - isRefreshReceiveValid, - limitAmountRef, - registerRefreshReceive, - token, - type, - ]); + }, [amount, clearRefreshReceive, fiat, isRefreshReceiveValid, limitAmountRef, registerRefreshReceive, token, type]); refreshReceiveRef.current = refreshReceive; const timer = useRef(); @@ -173,5 +171,11 @@ export const useReceive = ( debounceRefreshReceiveRef.current?.(); }, [amount]); - return { receiveAmount, rate, rateRefreshTime, refreshReceive, amountError, isAllowAmount }; + useEffect(() => { + if (!isAllowAmountRef.current) { + setReceiveAmount(''); + } + }, [fiat, token]); + + return { receiveAmount, rate, rateRefreshTime, refreshReceive, amountError, isAllowAmount: isAllowAmountRef.current }; }; From 6015d6f3b635ed8cb80ac63e5a45432c14075338 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 9 Jun 2023 16:04:01 +0800 Subject: [PATCH 058/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20getAchS?= =?UTF-8?q?ignature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/index.ts | 5 ++++- packages/api/api-did/payment/util/index.ts | 6 ++---- packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx | 7 ------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/api/api-did/payment/index.ts b/packages/api/api-did/payment/index.ts index 1bcc4a80e1..5f14e287bc 100644 --- a/packages/api/api-did/payment/index.ts +++ b/packages/api/api-did/payment/index.ts @@ -10,7 +10,10 @@ export default { getOrderQuote: '/api/app/thirdPart/alchemy/order/quote', getAchToken: '/api/app/thirdPart/alchemy/token', getOrderNo: '/api/app/thirdPart/order', - getAchSignature: '/api/app/thirdPart/alchemy/signature', + getAchSignature: { + target: '/api/app/thirdPart/alchemy/signature', + config: { method: 'GET' }, + }, updateAchOrder: '/api/app/thirdPart/order/alchemy', updateAlchemyOrderTxHash: 'api/app/thirdPart/alchemy/txHash', } as const; diff --git a/packages/api/api-did/payment/util/index.ts b/packages/api/api-did/payment/util/index.ts index 8efe169109..0fe2107997 100644 --- a/packages/api/api-did/payment/util/index.ts +++ b/packages/api/api-did/payment/util/index.ts @@ -69,11 +69,9 @@ export const getPaymentOrderNo = async (params: { transDirect: TransDirectEnum; return rst.id as string; }; -export const getAchSignature = async (signParams: Record) => { +export const getAchSignature = async (params: { address: string }) => { const rst = await request.payment.getAchSignature({ - params: { - signParams, - }, + params, }); console.log('getAchSignature', rst); if (rst.returnCode !== '0000' || !rst?.signature) { diff --git a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx index 9ff2b58ad8..159c416932 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx @@ -89,13 +89,6 @@ export default function BuyPreview() { )}&sign=${encodeURIComponent(signature)}`; } else { const withdrawUrl = encodeURIComponent(ACH_WITHDRAW_URL); - // const signature = await getAchSignature({ - // withdrawUrl, - // callbackUrl, - // appId, - // fiat: fiat.currency, - // cryptoAmount: amount, - // }); achUrl += `&type=sell&cryptoAmount=${amount}&withdrawUrl=${withdrawUrl}&source=3#/sell-formUserInfo`; } From 9ea6dca27e50511aa77a82fe94903004d58ebd54 Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 9 Jun 2023 17:14:08 +0800 Subject: [PATCH 059/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20sell=20wak?= =?UTF-8?q?e=20up=20home?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 1 + .../app/web/constants/index.ts | 3 ++ .../app/web/messages/unrestrictedMethods.ts | 1 + .../app/web/messages/walletMessage.ts | 1 + .../app/web/pages/Buy/Preview/index.tsx | 26 ++++-------- .../web/pages/Buy/hooks/useHandleAchSell.ts | 20 +++++---- .../app/web/pages/Home/index.tsx | 42 +++++++++++-------- .../serviceWorker/ServiceWorkerInstantiate.ts | 14 +++++++ 8 files changed, 65 insertions(+), 43 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 2294b7b2cb..25d494be1b 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -69,6 +69,7 @@ export const useSellTransfer = () => { } const result = await paymentSellTransfer(achTxAddressReceived); + console.log('🌹 🌹 🌹 paymentSellTransfer', result); if (result.error) { throw result.error; } diff --git a/packages/web-extension-did/app/web/constants/index.ts b/packages/web-extension-did/app/web/constants/index.ts index 2860be223f..7849d54ea1 100644 --- a/packages/web-extension-did/app/web/constants/index.ts +++ b/packages/web-extension-did/app/web/constants/index.ts @@ -41,3 +41,6 @@ export const AUTH_HOST = 'https://portkey.finance'; export const JOIN_AUTH_URL = `${AUTH_HOST}/join`; export const AUTH_APPLE_URL = `${AUTH_HOST}/apple-auth`; export const RECAPTCHA_URL = `${AUTH_HOST}/recaptcha-check`; + +// after ach-sell, redirect url, then wake up extension. +export const ACH_WITHDRAW_URL = 'https://openlogin-test.portkey.finance/extension-rouse?method=portkey_achSellRedirect'; // TODO: change url diff --git a/packages/web-extension-did/app/web/messages/unrestrictedMethods.ts b/packages/web-extension-did/app/web/messages/unrestrictedMethods.ts index c27b5a2626..1cdf9b9def 100644 --- a/packages/web-extension-did/app/web/messages/unrestrictedMethods.ts +++ b/packages/web-extension-did/app/web/messages/unrestrictedMethods.ts @@ -78,6 +78,7 @@ export const unrestrictedMethods = Object.freeze([ WalletMessageTypes.DISCONNECT, WalletMessageTypes.SET_RECAPTCHA_CODE_V2, WalletMessageTypes.SOCIAL_LOGIN, + WalletMessageTypes.ACH_SELL_REDIRECT, ]); export const authorizationList = [WalletMessageTypes.SWITCH_CHAIN]; diff --git a/packages/web-extension-did/app/web/messages/walletMessage.ts b/packages/web-extension-did/app/web/messages/walletMessage.ts index 8d81ce983c..c4e75fffd1 100644 --- a/packages/web-extension-did/app/web/messages/walletMessage.ts +++ b/packages/web-extension-did/app/web/messages/walletMessage.ts @@ -6,4 +6,5 @@ export default { GET_WALLET_STATE: 'portkey_getState', SET_RECAPTCHA_CODE_V2: 'portkey_setReCaptchaCodeV2', SOCIAL_LOGIN: 'portkey_socialLogin', + ACH_SELL_REDIRECT: 'portkey_achSellRedirect', }; diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index 9e26d2fd9c..cdc12c372a 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -19,6 +19,7 @@ import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import './index.less'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import { ACH_WITHDRAW_URL } from 'constants/index'; export default function Preview() { const { t } = useTranslation(); @@ -103,6 +104,12 @@ export default function Preview() { `${apiUrl}${paymentApi.updateAchOrder}`, )}`; + const orderNo = await getPaymentOrderNo({ + transDirect: side === 'BUY' ? TransDirectEnum.TOKEN_BUY : TransDirectEnum.TOKEN_SELL, + merchantName: ACH_MERCHANT_NAME, + }); + achUrl += `&merchantOrderNo=${orderNo}`; + if (side === PaymentTypeEnum.BUY) { achUrl += `&type=buy&fiatAmount=${amount}`; @@ -115,26 +122,11 @@ export default function Preview() { const signature = await getAchSignature({ address }); achUrl += `&address=${address}&sign=${encodeURIComponent(signature)}`; } else { - // achUrl += `&type=sell&cryptoAmount=${amount}`; - - const ACH_WITHDRAW_URL = 'http://portkey_sell'; // TODO SELL position - const withdrawUrl = encodeURIComponent(ACH_WITHDRAW_URL); - // const signature = await getAchSignature({ - // withdrawUrl, - // callbackUrl, - // appId, - // fiat: fiat.currency, - // cryptoAmount: amount, - // }); + const withdrawUrl = encodeURIComponent(ACH_WITHDRAW_URL + `&payload=${orderNo}`); + achUrl += `&type=sell&cryptoAmount=${amount}&withdrawUrl=${withdrawUrl}&source=3#/sell-formUserInfo`; } - const orderNo = await getPaymentOrderNo({ - transDirect: side === 'BUY' ? TransDirectEnum.TOKEN_BUY : TransDirectEnum.TOKEN_SELL, - merchantName: ACH_MERCHANT_NAME, - }); - achUrl += `&merchantOrderNo=${orderNo}`; - console.log('achUrl', achUrl); const openWinder = window.open(achUrl, '_blank'); if (openWinder) { diff --git a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts index ac67ef6dc1..bb8affcc20 100644 --- a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts +++ b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts @@ -7,10 +7,12 @@ import { AchTxAddressReceivedType } from '@portkey-wallet/types/types-ca/payment import { timesDecimals } from '@portkey-wallet/utils/converter'; import { message } from 'antd'; import { useCallback, useMemo } from 'react'; -import { useLoading, useUserInfo } from 'store/Provider/hooks'; +import { useLoading } from 'store/Provider/hooks'; import sameChainTransfer from 'utils/sandboxUtil/sameChainTransfer'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import aes from '@portkey-wallet/utils/aes'; +import InternalMessage from 'messages/InternalMessage'; +import InternalMessageTypes from 'messages/InternalMessageTypes'; export const useHandleAchSell = () => { const { setLoading } = useLoading(); @@ -24,17 +26,19 @@ export const useHandleAchSell = () => { const chainInfo = useCurrentChain('AELF'); const wallet = useCurrentWalletInfo(); const currentNetwork = useCurrentNetworkInfo(); - const { passwordSeed } = useUserInfo(); const paymentSellTransfer = useCallback( async (params: AchTxAddressReceivedType) => { if (!chainInfo) throw new Error(''); - const privateKey = aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed); + + const getSeedResult = await InternalMessage.payload(InternalMessageTypes.GET_SEED).send(); + const pin = getSeedResult.data.privateKey; + const privateKey = await aes.decrypt(wallet.AESEncryptPrivateKey, pin); if (!privateKey) throw new Error(''); if (!aelfToken) throw new Error(''); - const res = await sameChainTransfer({ + const transferParams = { chainInfo, chainType: currentNetwork.walletType, privateKey, @@ -51,15 +55,15 @@ export const useHandleAchSell = () => { }, caHash: wallet?.caHash || '', amount: timesDecimals(params.cryptoAmount, aelfToken.decimals).toNumber(), - toAddress: `ELF_${params.address}_AELF`, // 'ELF_2KQWh5v6Y24VcGgsx2KHpQvRyyU5DnCZ4eAUPqGQbnuZgExKaV_AELF', // TODO SELL - }); - + toAddress: `ELF_${params.address}_AELF`, + }; + const res = await sameChainTransfer(transferParams); return { error: res?.result?.Error, transactionId: res?.result.TransactionId || '', }; }, - [aelfToken, chainInfo, currentNetwork.walletType, passwordSeed, wallet.AESEncryptPrivateKey, wallet?.caHash], + [aelfToken, chainInfo, currentNetwork.walletType, wallet.AESEncryptPrivateKey, wallet?.caHash], ); return useCallback( diff --git a/packages/web-extension-did/app/web/pages/Home/index.tsx b/packages/web-extension-did/app/web/pages/Home/index.tsx index ddf4dfada2..f8c1a2386d 100644 --- a/packages/web-extension-did/app/web/pages/Home/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/index.tsx @@ -1,30 +1,17 @@ import clsx from 'clsx'; import PortKeyHeader from 'pages/components/PortKeyHeader'; -import { useCallback, useEffect } from 'react'; -import { useNavigate } from 'react-router'; +import { useCallback, useEffect, useRef } from 'react'; +import { useNavigate, useLocation } from 'react-router'; import { useCommonState, useLoading } from 'store/Provider/hooks'; import popupHandler from 'utils/popupHandler'; import { getLocalStorage } from 'utils/storage/chromeStorage'; import MyBalance from './components/MyBalance'; import './index.less'; -// import { useHandleAchSell } from 'pages/Buy/hooks/useHandleAchSell'; -// import { getPaymentOrderNo } from '@portkey-wallet/api/api-did/payment/util'; -// import { ACH_MERCHANT_NAME, TransDirectEnum } from '@portkey-wallet/constants/constants-ca/payment'; +import qs from 'query-string'; +import { useHandleAchSell } from 'pages/Buy/hooks/useHandleAchSell'; +import { useStorage } from 'hooks/useStorage'; export default function Home() { - // TODO SELL - // const handleAchSell = useHandleAchSell(); - - // useEffect(() => { - // getPaymentOrderNo({ - // transDirect: TransDirectEnum.TOKEN_SELL, - // merchantName: ACH_MERCHANT_NAME, - // }).then((orderNo) => { - // // const orderNo = 'f5298810-35f2-d210-2eba-3a0b8ce563cb'; // mock orderNo; - // handleAchSell(orderNo); - // }); - // }, [handleAchSell]); - const navigate = useNavigate(); const { isPopupInit, isPrompt, isNotLessThan768 } = useCommonState(); @@ -34,6 +21,25 @@ export default function Home() { }, [isNotLessThan768, navigate]); const { setLoading } = useLoading(); + const { search } = useLocation(); + const isSell = useRef(0); // guaranteed to make only one transfer + const handleAchSell = useHandleAchSell(); + const locked = useStorage('locked'); + + useEffect(() => { + if (search) { + const { detail } = qs.parse(search); + // if (detail) { + // // TODO SELL LOCKED + // } + if (detail && !locked && isSell.current === 0) { + console.log('🌹 🌹 🌹', ''); + isSell.current = 1; + handleAchSell(detail); + } + } + }, [handleAchSell, locked, search]); + const getLocationState = useCallback(async () => { try { if (!isPopupInit) return; diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 6e74b1ddb7..9ca23428d3 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -56,6 +56,7 @@ const allowedMethod = [ MethodMessageTypes.GET_WALLET_STATE, WalletMessageTypes.SET_RECAPTCHA_CODE_V2, WalletMessageTypes.SOCIAL_LOGIN, + WalletMessageTypes.ACH_SELL_REDIRECT, PortkeyMessageTypes.ACTIVE_LOCK_STATUS, PortkeyMessageTypes.SETTING, PortkeyMessageTypes.ADD_GUARDIANS, @@ -201,6 +202,9 @@ export default class ServiceWorkerInstantiate { case WalletMessageTypes.SOCIAL_LOGIN: this.getSocialLogin(sendResponse, message.payload); break; + case WalletMessageTypes.ACH_SELL_REDIRECT: + ServiceWorkerInstantiate.expandHome(message.payload); + break; default: sendResponse(errorHandler(700001, `Portkey does not contain this method (${message.type})`)); @@ -345,6 +349,16 @@ export default class ServiceWorkerInstantiate { ); } + static async expandHome(payload: any) { + notificationService.openPrompt( + { + method: PromptRouteTypes.EXPAND_FULL_SCREEN, + search: payload.payload, + }, + 'tabs', + ); + } + /** * Determine whether the portkey is locked, and if not, get the list of authorized users */ From b052011de51e792fc6a1ac7c1e99569b24acffdf Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 9 Jun 2023 17:48:47 +0800 Subject: [PATCH 060/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20sell=20inp?= =?UTF-8?q?ut=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web-extension-did/app/web/constants/index.ts | 3 ++- .../app/web/pages/Buy/components/SellFrom/index.tsx | 12 +++++++++++- .../web-extension-did/app/web/pages/Home/index.tsx | 5 +++-- .../web/serviceWorker/ServiceWorkerInstantiate.ts | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/web-extension-did/app/web/constants/index.ts b/packages/web-extension-did/app/web/constants/index.ts index 7849d54ea1..197948f008 100644 --- a/packages/web-extension-did/app/web/constants/index.ts +++ b/packages/web-extension-did/app/web/constants/index.ts @@ -1,4 +1,5 @@ import { DeviceType } from '@portkey-wallet/types/types-ca/device'; +import walletMessage from 'messages/walletMessage'; export const prefixCls = 'portkey'; // redux @@ -43,4 +44,4 @@ export const AUTH_APPLE_URL = `${AUTH_HOST}/apple-auth`; export const RECAPTCHA_URL = `${AUTH_HOST}/recaptcha-check`; // after ach-sell, redirect url, then wake up extension. -export const ACH_WITHDRAW_URL = 'https://openlogin-test.portkey.finance/extension-rouse?method=portkey_achSellRedirect'; // TODO: change url +export const ACH_WITHDRAW_URL = `https://openlogin-test.portkey.finance/extension-rouse?method=${walletMessage.ACH_SELL_REDIRECT}`; // TODO: change url diff --git a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx index bf2f5e94c4..fde787c0da 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx @@ -17,13 +17,23 @@ export default function SellFrom({ errMsg, }: IBuyOrSellFromProps) { + const tokenChange = (val: string) => { + const arr = val.split('.'); + // No more than eight digits after the decimal point + if (arr[1]?.length > 8) return; + // The total number does not exceed 13 digits, not include decimal point + if (arr.join('').length > 13) return; + + handleTokenChange(val); + }; + return ( <>
    {`I want to sell`}
    handleTokenChange(val)} + onChange={(val) => tokenChange(val)} readOnly={false} onKeyDown={handleTokenKeyDown} curToken={curToken} diff --git a/packages/web-extension-did/app/web/pages/Home/index.tsx b/packages/web-extension-did/app/web/pages/Home/index.tsx index f8c1a2386d..dc13d8395f 100644 --- a/packages/web-extension-did/app/web/pages/Home/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/index.tsx @@ -10,6 +10,7 @@ import './index.less'; import qs from 'query-string'; import { useHandleAchSell } from 'pages/Buy/hooks/useHandleAchSell'; import { useStorage } from 'hooks/useStorage'; +import walletMessage from 'messages/walletMessage'; export default function Home() { const navigate = useNavigate(); @@ -28,11 +29,11 @@ export default function Home() { useEffect(() => { if (search) { - const { detail } = qs.parse(search); + const { detail, method } = qs.parse(search); // if (detail) { // // TODO SELL LOCKED // } - if (detail && !locked && isSell.current === 0) { + if (detail && method === walletMessage.ACH_SELL_REDIRECT && !locked && isSell.current === 0) { console.log('🌹 🌹 🌹', ''); isSell.current = 1; handleAchSell(detail); diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 9ca23428d3..c10adb86cc 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -353,7 +353,7 @@ export default class ServiceWorkerInstantiate { notificationService.openPrompt( { method: PromptRouteTypes.EXPAND_FULL_SCREEN, - search: payload.payload, + search: `${payload.payload}&method=${payload.method}`, }, 'tabs', ); From 7791d1675412817f1d17be44b9208c3ccd6f4f1c Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 9 Jun 2023 17:59:40 +0800 Subject: [PATCH 061/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/type.ts | 4 ++-- packages/mobile-app-did/js/hooks/useInitData.ts | 3 ++- .../js/pages/Buy/BuyHome/components/BuyForm/index.tsx | 2 +- .../js/pages/Buy/BuyHome/components/SellForm/index.tsx | 8 ++++---- packages/mobile-app-did/js/pages/Buy/constants.ts | 1 + 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/api/api-did/payment/type.ts b/packages/api/api-did/payment/type.ts index 8256679207..3bc92efdeb 100644 --- a/packages/api/api-did/payment/type.ts +++ b/packages/api/api-did/payment/type.ts @@ -11,8 +11,8 @@ export interface OrderQuoteType { export interface CryptoInfoType { crypto: string; network: string; - buyEnable: number; - sellEnable: number; + buyEnable: string; + sellEnable: string; minPurchaseAmount: number | null; maxPurchaseAmount: number | null; address: null; diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index b93a883fe7..2846c3bc3f 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -1,6 +1,6 @@ import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { fetchBuyFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; +import { fetchBuyFiatListAsync, fetchSellFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; import { getSymbolImagesAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import { getWalletNameAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useCallback } from 'react'; @@ -23,6 +23,7 @@ export default function useInitData() { // mainnet only if (isMainNetwork) { dispatch(fetchBuyFiatListAsync()); + dispatch(fetchSellFiatListAsync()); } getCurrentCAViewContract(); dispatch(getWalletNameAsync()); diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx index 69ddaee0ca..6363edabf6 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx @@ -67,7 +67,7 @@ export default function BuyForm() { } if (token === undefined || cryptoListRef.current === undefined) return; const cryptoInfo = cryptoListRef.current.find( - item => item.crypto === token.crypto && item.network === token.network, + item => item.crypto === token.crypto && item.network === token.network && Number(item.buyEnable) === 1, ); if (cryptoInfo === undefined || cryptoInfo.minPurchaseAmount === null || cryptoInfo.maxPurchaseAmount === null) { diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index 488a790b2d..3692bcf537 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -22,7 +22,7 @@ import { ErrorType } from 'types/common'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import { CryptoInfoType } from '@portkey-wallet/api/api-did/payment/type'; import { CryptoItemType, LimitType, TypeEnum } from 'pages/Buy/types'; -import { INIT_BUY_AMOUNT, tokenList } from 'pages/Buy/constants'; +import { INIT_SELL_AMOUNT, tokenList } from 'pages/Buy/constants'; import Loading from 'components/Loading'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { useReceive } from 'pages/Buy/hooks'; @@ -36,13 +36,13 @@ import { ZERO } from '@portkey-wallet/constants/misc'; import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; export default function SellForm() { - const { buyFiatList: fiatList } = usePayment(); + const { sellFiatList: fiatList } = usePayment(); const [fiat, setFiat] = useState( fiatList.find(item => item.currency === 'USD' && item.country === 'US'), ); const [token, setToken] = useState(tokenList[0]); - const [amount, setAmount] = useState(INIT_BUY_AMOUNT); + const [amount, setAmount] = useState(INIT_SELL_AMOUNT); const [amountLocalError, setAmountLocalError] = useState(INIT_NONE_ERROR); const { accountToken } = useAssets(); const aelfToken = useMemo( @@ -82,7 +82,7 @@ export default function SellForm() { } if (token === undefined || cryptoListRef.current === undefined) return; const cryptoInfo = cryptoListRef.current.find( - item => item.crypto === token.crypto && item.network === token.network, + item => item.crypto === token.crypto && item.network === token.network && Number(item.sellEnable) === 1, ); console.log('cryptoInfo', cryptoInfo); diff --git a/packages/mobile-app-did/js/pages/Buy/constants.ts b/packages/mobile-app-did/js/pages/Buy/constants.ts index a4f892eecd..0d538a67f2 100644 --- a/packages/mobile-app-did/js/pages/Buy/constants.ts +++ b/packages/mobile-app-did/js/pages/Buy/constants.ts @@ -10,3 +10,4 @@ export const tokenList: CryptoItemType[] = [ export const MAX_REFRESH_TIME = 15; export const INIT_BUY_AMOUNT = '200'; +export const INIT_SELL_AMOUNT = '300'; From 189a9c3bfe0928c3d5b0d31ab362e28a1e4b0780 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 9 Jun 2023 18:36:38 +0800 Subject: [PATCH 062/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20devDependencies?= =?UTF-8?q?=20&&=20babel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - packages/mobile-app-did/babel.config.js | 2 +- packages/mobile-app-did/package.json | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index fd7f10c5ab..9b8c6b5d02 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "test-badges": "jest --coverage && istanbul-badges-readme" }, "devDependencies": { - "react-native": "0.69.7", "@babel/core": "^7.19.1", "@babel/plugin-proposal-decorators": "^7.19.1", "@babel/plugin-transform-modules-commonjs": "^7.21.2", diff --git a/packages/mobile-app-did/babel.config.js b/packages/mobile-app-did/babel.config.js index 4be3c57690..ce2ab07d05 100644 --- a/packages/mobile-app-did/babel.config.js +++ b/packages/mobile-app-did/babel.config.js @@ -11,7 +11,7 @@ function getAliasesFromTsConfig() { return alias; } module.exports = { - presets: ['module:metro-react-native-babel-preset', ['@babel/preset-env', { loose: true }]], + presets: ['module:metro-react-native-babel-preset'], plugins: [ [ 'module-resolver', diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index ab3225b69a..eb42dc0061 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -146,7 +146,6 @@ "web3-utils": "^1.8.0" }, "devDependencies": { - "react-native": "0.69.7", "@babel/core": "^7.12.9", "@babel/runtime": "^7.12.5", "@commitlint/cli": "^17.0.0", From e680e14477c66ec8749eec2fec28dc391584593d Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 9 Jun 2023 18:55:34 +0800 Subject: [PATCH 063/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20style=20&=20amoun?= =?UTF-8?q?tShow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/content.ts | 2 +- packages/web-extension-did/app/web/inject.ts | 7 +-- .../app/web/pages/SendTransactions/index.less | 17 ++++-- .../app/web/pages/SendTransactions/index.tsx | 55 +++++++++++-------- .../SendTransactions/utils/getTransferFee.ts | 33 ++++++----- .../components/ConnectedSiteList/index.less | 31 +++++++---- .../components/ConnectedSiteList/index.tsx | 10 ++-- .../components/AccountConnectModal/index.less | 2 +- packages/web-extension-did/package.json | 8 +-- 9 files changed, 97 insertions(+), 68 deletions(-) diff --git a/packages/web-extension-did/app/web/content.ts b/packages/web-extension-did/app/web/content.ts index 725d913fe4..6b0434aa28 100644 --- a/packages/web-extension-did/app/web/content.ts +++ b/packages/web-extension-did/app/web/content.ts @@ -124,7 +124,7 @@ class Content { setupPageStream() { // Setting up a new encrypted stream for // interaction between the extension and the application - pageStream = new ContentPostStream({ name: CONTENT_TARGET, postWindow: window }); + pageStream = new ContentPostStream({ name: CONTENT_TARGET }); pageStream.on('data', (data: Buffer) => { const params = JSON.parse(data.toString()); diff --git a/packages/web-extension-did/app/web/inject.ts b/packages/web-extension-did/app/web/inject.ts index e28ebeaec0..fc7c25fdd5 100644 --- a/packages/web-extension-did/app/web/inject.ts +++ b/packages/web-extension-did/app/web/inject.ts @@ -1,6 +1,5 @@ -import { InitializeProvider } from '@portkey/extension-provider'; +import { InitializeProvider, InpagePostStream } from '@portkey/extension-provider'; import { shouldInjectProvider } from '@portkey/provider-utils'; -import { PortkeyPostStream } from '@portkey/providers'; const INPAGE_TARGET = 'portkey-inpage'; export default class Inject { @@ -10,10 +9,8 @@ export default class Inject { initPortKey() { if (shouldInjectProvider()) { - const portkeyStream = new PortkeyPostStream({ + const portkeyStream = new InpagePostStream({ name: INPAGE_TARGET, - postWindow: window, - originWindow: window, }); new InitializeProvider({ connectionStream: portkeyStream, diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.less b/packages/web-extension-did/app/web/pages/SendTransactions/index.less index f5e64d7c09..9ed45980f9 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.less +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.less @@ -2,7 +2,7 @@ .send-transaction { width: 328px; - margin: 40px auto; + margin: 40px auto 16px; flex-direction: column; font-size: 12px; line-height: 16px; @@ -19,10 +19,14 @@ padding: 8px 16px; border-radius: 24px; border: 1px solid @border-2; - .aelf-icon { + .aelf-icon, .elf-icon-icon { width: 24px; height: 24px; margin-right: 8px; + svg { + width: 24px; + height: 24px; + } } } .account { @@ -76,21 +80,21 @@ border-radius: 6px; } .fee, .total { - margin-bottom: 12px; .fee-amount, .total-amount { text-align: right; } .elf { + margin-left: 4px; font-size: 14px; line-height: 20px; font-weight: 500; color: @font-11; + white-space: nowrap; } } } .message-wrapper { color: @font-13; - margin-bottom: 42px; .title { margin-bottom: 4px; } @@ -125,8 +129,13 @@ } } } + .error-message { + margin-top: 16px; + color: @font-14; + } .btn { width: 100%; + margin-top: 40px; .@{app-prefix}-btn { width: 156px; height: 48px; diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 61f91a89c2..14a8f8c8a9 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -3,7 +3,7 @@ import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { ChainId } from '@portkey-wallet/types'; import { ZERO } from '@portkey-wallet/constants/misc'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; -import { formatAmountShow } from '@portkey-wallet/utils/converter'; +import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { formatChainInfoToShow, handleErrorMessage } from '@portkey-wallet/utils'; import aes from '@portkey-wallet/utils/aes'; import { Button, message } from 'antd'; @@ -38,6 +38,7 @@ export default function SendTransactions() { const amountInUsdShow = useAmountInUsdShow(); const [_tokenPriceObject, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); const [fee, setFee] = useState(''); + const [errMsg, setErrMsg] = useState(''); const isCAContract = useMemo(() => chainInfo?.caContractAddress === payload?.contractAddress, [chainInfo, payload]); const privateKey = useMemo( () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), @@ -45,6 +46,7 @@ export default function SendTransactions() { ); const getFee = useCallback(async () => { + if (!privateKey) return; if (!chainInfo?.endPoint || !wallet?.caHash || !chainInfo.caContractAddress) return; const mth = isCAContract ? payload?.method : 'ManagerForwardCall'; const paramsOption = isCAContract @@ -55,20 +57,19 @@ export default function SendTransactions() { contractAddress: payload?.contractAddress, args: payload?.params?.paramsOption, }; - try { - if (!privateKey) throw 'Invalid user information, please check'; - const fee = await getTransferFee({ - rpcUrl: chainInfo.endPoint, - chainType: 'aelf', - methodName: mth, - paramsOption, - privateKey, - contractAddress: chainInfo.caContractAddress, - }); - setFee(fee); - } catch (error) { + const fee = await getTransferFee({ + rpcUrl: chainInfo.endPoint, + chainType: 'aelf', + methodName: mth, + paramsOption, + privateKey, + contractAddress: chainInfo.caContractAddress, + }); + if (fee === '--') { setFee('0'); - console.log('get fee error', error); + setErrMsg('Insufficient funds for transaction fee'); + } else { + setFee(fee); } }, [chainInfo, isCAContract, payload, privateKey, wallet]); @@ -78,13 +79,13 @@ export default function SendTransactions() { useEffect(() => { const symbol = payload?.params?.paramsOption?.symbol; - if (!symbol) return; + if (!symbol || !isMainnet) return; if (symbol === 'ELF') { getTokenPrice(symbol); } else { getTokensPrice([symbol, 'ELF']); } - }, [getTokenPrice, getTokensPrice, payload?.params?.paramsOption?.symbol]); + }, [getTokenPrice, getTokensPrice, payload, isMainnet]); const renderAccountInfo = useMemo( () => @@ -106,20 +107,22 @@ export default function SendTransactions() { // Transfer const renderDetail = useMemo(() => { const { symbol, amount } = payload?.params?.paramsOption || {}; + const decimals = symbol === 'ELF' ? 8 : 0; + return (
    Details
    Amount
    -
    {`${formatAmountShow(amount)} ${symbol}`}
    - {isMainnet &&
    {amountInUsdShow(amount, 0, symbol)}
    } +
    {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`}
    + {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    }
    Transaction Fee
    -
    {`${formatAmountShow(fee)} ELF`}
    +
    {`${formatAmountShow(fee, 8)} ELF`}
    {isMainnet &&
    {amountInUsdShow(fee, 0, 'ELF')}
    }
    @@ -128,12 +131,17 @@ export default function SendTransactions() {
    {symbol === 'ELF' ? ( <> -
    {`${formatAmountShow(ZERO.plus(amount).plus(fee))} ${symbol}`}
    - {isMainnet &&
    {amountInUsdShow(ZERO.plus(amount).plus(fee).toNumber(), 0, symbol)}
    } +
    {`${formatAmountShow( + ZERO.plus(divDecimals(amount, decimals)).plus(fee), + 8, + )} ${symbol}`}
    + {isMainnet && ( +
    {amountInUsdShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee).toNumber(), 0, symbol)}
    + )} ) : ( <> -
    {`${formatAmountShow(fee)} ELF`}
    +
    {`${formatAmountShow(fee, 8)} ELF`}
    {isMainnet &&
    {amountInUsdShow(fee, 0, 'ELF')}
    }
    {`${formatAmountShow(amount)} ${symbol}`}
    {isMainnet &&
    {amountInUsdShow(amount, 0, symbol)}
    } @@ -219,6 +227,7 @@ export default function SendTransactions() {
    {payload?.method}
    {payload?.method.toLowerCase() === 'transfer' ? renderDetail : renderMessage} + {errMsg &&
    {errMsg}
    }
    -
    diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/utils/getTransferFee.ts b/packages/web-extension-did/app/web/pages/SendTransactions/utils/getTransferFee.ts index 8a4f27597a..3f9aeba0c4 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/utils/getTransferFee.ts +++ b/packages/web-extension-did/app/web/pages/SendTransactions/utils/getTransferFee.ts @@ -18,20 +18,25 @@ const getTransferFee = async ({ methodName: string; privateKey: string; }) => { - const transactionRes = await getTransactionFee({ - rpcUrl, - contractAddress, - paramsOption, - chainType, - methodName, - privateKey, - }); - const feeRes = transactionRes.result['ELF']; - const fee = divDecimalsStr(ZERO.plus(feeRes), 8); - if (Number.isNaN(ZERO.plus(fee).toNumber())) { - return '0'; - } else { - return fee; + try { + const transactionRes = await getTransactionFee({ + rpcUrl, + contractAddress, + paramsOption, + chainType, + methodName, + privateKey, + }); + const feeRes = transactionRes.result['ELF']; + const fee = divDecimalsStr(ZERO.plus(feeRes), 8); + if (Number.isNaN(ZERO.plus(fee).toNumber())) { + return '0'; + } else { + return fee; + } + } catch (error) { + console.error('getTransactionFee', error); + return '--'; } }; diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less index 530016277a..22684788cb 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less @@ -1,4 +1,5 @@ @import '../../../../assets/theme/color.less'; +@import '../../../../assets/theme/constants.less'; .connected-site-list { .connected-site-item { @@ -10,18 +11,19 @@ color: black; .content { align-items: center; + width: 100%; .icon { flex-shrink: 0; width: 32px; border-radius: 50%; } .desc { + flex: 1; + width: 0; + word-wrap: break-word; margin-left: 16px; .text { - max-width: 169px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + .text-overflow(1); } .name { font-size: 16px; @@ -36,16 +38,21 @@ } } } - .@{app-prefix}-btn-text { + .btn { width: 79px; height: 24px; - font-size: 12px; - line-height: 16px; - color: @font-14; - background-color: @bg-11; - border-radius: 24px; - border: 1px solid @border-7; - text-shadow: none; + margin-left: 16px; + .@{app-prefix}-btn-text { + width: 79px; + height: 24px; + font-size: 12px; + line-height: 16px; + color: @font-14; + background-color: @bg-11; + border-radius: 24px; + border: 1px solid @border-7; + text-shadow: none; + } } } } diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx index 50c03c35a4..23ac960d74 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx @@ -26,14 +26,16 @@ export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSite loadEle={item?.name?.[0] || ''} errorEle={item?.name?.[0] || ''} /> -
    +
    {item.name}
    {item.origin}
    - +
    + +
    ))}
    diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less index f7ec20d210..fa1319f795 100644 --- a/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less +++ b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.less @@ -12,7 +12,7 @@ } } .@{app-prefix}-modal-body { - padding: 0 24px 24px; + padding: 0 23px 24px; display: flex; flex-direction: column; justify-content: center; diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 1b70c5e5d2..f76d8d8890 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.20", - "@portkey/provider-utils": "0.0.1-alpha.20", - "@portkey/provider-types": "0.0.1-alpha.20", - "@portkey/providers": "0.0.1-alpha.20", + "@portkey/extension-provider": "0.0.1-alpha.21", + "@portkey/provider-utils": "0.0.1-alpha.21", + "@portkey/provider-types": "0.0.1-alpha.21", + "@portkey/providers": "0.0.1-alpha.21", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", From d6247fde105cb9ee85074f2edba36196cadd664c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 9 Jun 2023 19:10:54 +0800 Subject: [PATCH 064/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20discover=20with?= =?UTF-8?q?=20dapp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 30 +- .../js/components/ButtonRow/index.tsx | 9 +- .../js/components/ModalBody/index.tsx | 42 ++- .../js/components/NoData/index.tsx | 11 +- .../TabsDrawer/TabsDrawerContent.tsx | 95 ++++-- .../components/WalletInfoOverlay/index.tsx | 115 ++++++++ .../TabsDrawer/components/card/index.tsx | 5 +- .../components/tabsOverlay/index.tsx | 49 +++- .../js/components/TabsDrawer/index.tsx | 1 + .../dapp/components/ConnectOverlay/index.tsx | 181 ++++++++++++ .../components/TransactionOverlay/index.tsx | 276 ++++++++++++++++++ .../js/dapp/dappMobileOperator.ts | 5 +- .../mobile-app-did/js/dapp/dappOverlay.ts | 46 +-- .../pages/Discover/DiscoverSearch/index.tsx | 4 +- .../components/RecordSection/index.tsx | 18 +- .../Dapp/components/DappItem/index.tsx | 96 ++++++ .../js/pages/My/WalletSecurity/Dapp/index.tsx | 123 ++++++++ .../js/pages/My/WalletSecurity/index.tsx | 9 + .../js/pages/My/WalletSecurity/router.ts | 5 + .../js/pages/My/components/MenuItem/index.tsx | 1 + packages/mobile-app-did/package.json | 4 +- packages/store/store-ca/discover/slice.ts | 7 +- packages/store/store-ca/discover/type.ts | 2 +- packages/types/types-ca/discover.ts | 1 + yarn.lock | 6 +- 25 files changed, 1033 insertions(+), 108 deletions(-) create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx create mode 100644 packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx create mode 100644 packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx create mode 100644 packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx create mode 100644 packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index f6cbee2de8..b18744fc35 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import WebView from 'react-native-webview'; @@ -14,15 +14,23 @@ import { DappOverlay } from 'dapp/dappOverlay'; import { DappMobileManager } from 'dapp/dappManager'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; + type BrowserTabProps = { - uri: string; isHidden: boolean; + item: ITabItem; + setActiveTabRef: (ref: any) => void; + setActiveWebViewRef: (ref: any) => void; }; -const BrowserTab: React.FC = ({ uri, isHidden }) => { +const BrowserTab: React.FC = ({ isHidden, item, setActiveTabRef, setActiveWebViewRef }) => { + const viewRef = useRef(null); const webViewRef = useRef(null); const operatorRef = useRef(null); const [entryScriptWeb3, setEntryScriptWeb3] = useState(); + const { activeTabId } = useAppCASelector(state => state.discover); + useEffectOnce(() => { const getEntryScriptWeb3 = async () => { const script = await EntryScriptWeb3.get(); @@ -63,13 +71,25 @@ const BrowserTab: React.FC = ({ uri, isHidden }) => { }); }, []); + useEffect(() => { + if (viewRef && viewRef.current && activeTabId === item.id) { + setActiveTabRef(viewRef); + } + }, [activeTabId, item.id, setActiveTabRef, viewRef]); + + useEffect(() => { + if (webViewRef && webViewRef.current) { + setActiveWebViewRef(webViewRef); + } + }, [webViewRef, setActiveWebViewRef]); + return ( - + { operatorRef.current?.handleRequestMessage(nativeEvent.data); diff --git a/packages/mobile-app-did/js/components/ButtonRow/index.tsx b/packages/mobile-app-did/js/components/ButtonRow/index.tsx index 12077f8508..91d864b0ea 100644 --- a/packages/mobile-app-did/js/components/ButtonRow/index.tsx +++ b/packages/mobile-app-did/js/components/ButtonRow/index.tsx @@ -15,7 +15,12 @@ export type ButtonRowProps = { }[]; style?: StyleProp; } & CommonButtonProps; -export default function ButtonRow({ buttonStyle: propsButtonStyle, buttons, style }: ButtonRowProps) { +export default function ButtonRow({ + buttonStyle: propsButtonStyle, + titleStyle: propsTitleStyle, + buttons, + style, +}: ButtonRowProps) { return ( {Array.isArray(buttons) && @@ -37,7 +42,7 @@ export default function ButtonRow({ buttonStyle: propsButtonStyle, buttons, styl loading={item.loading} containerStyle={containerStyle} buttonStyle={[buttonStyle, propsButtonStyle]} - titleStyle={titleStyle} + titleStyle={[titleStyle, propsTitleStyle]} onPress={item.onPress} type={item.type || 'primary'} key={index} diff --git a/packages/mobile-app-did/js/components/ModalBody/index.tsx b/packages/mobile-app-did/js/components/ModalBody/index.tsx index 952c34cec0..43886b5588 100644 --- a/packages/mobile-app-did/js/components/ModalBody/index.tsx +++ b/packages/mobile-app-did/js/components/ModalBody/index.tsx @@ -8,22 +8,35 @@ import { defaultColors } from 'assets/theme'; import { TextXL } from 'components/CommonText'; import Svg from 'components/Svg'; import GStyles from 'assets/theme/GStyles'; +import fonts from 'assets/theme/fonts'; +import { useGStyles } from 'assets/theme/useGStyles'; +import ButtonRow from 'components/ButtonRow'; +import { CommonButtonProps } from 'components/CommonButton'; export interface ModalBodyProps extends ViewProps { title?: string; modalBodyType?: 'center' | 'bottom'; style?: ViewStyle; onClose?: () => void; + bottomButtonGroup?: { + onPress?: () => void; + type?: CommonButtonProps['type']; + title: string; + loading?: CommonButtonProps['loading']; + disabled?: boolean; + }[]; } export const ModalBody: React.FC = props => { - const { modalBodyType, title, children, style = {}, onClose } = props; + const { modalBodyType, title, children, style = {}, onClose, bottomButtonGroup } = props; + + const gStyles = useGStyles(); if (modalBodyType === 'bottom') { return ( - + - + {title} = props => { {children} + {!!bottomButtonGroup && ( + + )} ); } @@ -51,7 +72,7 @@ export const styles = StyleSheet.create({ borderRadius: 10, backgroundColor: 'white', }, - bottomBox: { + wrapStyle: { width: screenWidth, }, centerBox: { @@ -85,4 +106,17 @@ export const styles = StyleSheet.create({ backgroundColor: defaultColors.bg7, width: pTd(48), }, + buttonGroup: { + position: 'absolute', + bottom: 0, + paddingRight: pTd(20), + ...GStyles.paddingArg(10, 20, 16, 20), + }, + buttonStyle: { + height: pTd(48), + fontSize: pTd(18), + }, + buttonTitleStyle: { + fontSize: pTd(16), + }, }); diff --git a/packages/mobile-app-did/js/components/NoData/index.tsx b/packages/mobile-app-did/js/components/NoData/index.tsx index 325a9f9779..aa6c17cdd3 100644 --- a/packages/mobile-app-did/js/components/NoData/index.tsx +++ b/packages/mobile-app-did/js/components/NoData/index.tsx @@ -10,10 +10,17 @@ export type NoDataPropsType = { message?: string; type?: 'center' | 'top'; topDistance?: number | string; + style?: ViewStyle; }; const NoData: React.FC = props => { - const { message = 'You have no transactions!', type = 'top', topDistance = pTd(89), noPic = false } = props; + const { + message = 'You have no transactions!', + type = 'top', + topDistance = pTd(89), + noPic = false, + style = {}, + } = props; let topStyle: ViewStyle = {}; @@ -25,7 +32,7 @@ const NoData: React.FC = props => { } return ( - + {!noPic && } {message} diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 1599fceca0..349efed0fa 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -3,8 +3,8 @@ import GStyles from 'assets/theme/GStyles'; import { TextM } from 'components/CommonText'; import PageContainer from 'components/PageContainer'; import { TouchableOpacity } from 'react-native'; -import React, { useMemo } from 'react'; -import { StyleSheet, ScrollView, View, Button, Image } from 'react-native'; +import React, { useCallback, useMemo, useState } from 'react'; +import { StyleSheet, ScrollView, View } from 'react-native'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca/index'; import { pTd } from 'utils/unit'; import Svg from 'components/Svg'; @@ -12,36 +12,66 @@ import { defaultColors } from 'assets/theme'; import { useLanguage } from 'i18n/hooks'; import { FontStyles } from 'assets/theme/styles'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; -import Card from './components/card'; +import Card from './components/Card'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { changeDrawerOpenStatus } from '@portkey-wallet/store/store-ca/discover/slice'; +import { + changeDrawerOpenStatus, + closeAllTabs, + setActiveTab, + updateTab, +} from '@portkey-wallet/store/store-ca/discover/slice'; import BrowserTab from 'components/BrowserTab'; -import { showBrowserModal } from './components/tabsOverlay'; +import { showBrowserModal } from './components/TabsOverlay'; +import { captureRef } from 'react-native-view-shot'; + +import { showWalletInfo } from './components/WalletInfoOverlay'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; + +const takeSnapshot = (viewRef: any) => captureRef(viewRef?.current, { format: 'jpg', quality: 0.2 }); const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); - const { activeTabId, tabs } = useAppCASelector(state => state.discover); + const { activeTabId, tabs, isDrawerOpen } = useAppCASelector(state => state.discover); - console.log('tabs', tabs); + const [activeTabRef, setActiveTabRef] = React.useState(null); + const [activeWebViewRef, setActiveWebViewRef] = React.useState(null); - const rightDom = useMemo(() => { - const activeItem = tabs.find(ele => ele.id === activeTabId); + const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); + + const activeWebviewScreenShot = useCallback(() => { + takeSnapshot(activeTabRef).then( + uri => { + console.log('Image saved to', uri); + dispatch(updateTab({ id: activeTabId, screenShotUrl: uri })); + }, + error => console.error('Oops, snapshot failed', error), + ); + }, [activeTabId, activeTabRef, dispatch]); - console.log('activeItem', activeItem); + const backToSearchPage = useCallback(() => { + activeWebviewScreenShot(); + dispatch(setActiveTab(undefined)); + dispatch(changeDrawerOpenStatus(false)); + }, [activeWebviewScreenShot, dispatch]); + + // header right + const rightDom = useMemo(() => { + const activeItem = tabs.find(ele => ele.id === activeTabId) as ITabItem; if (activeTabId) return ( - + showWalletInfo()}> showBrowserModal({ - browserInfo: { title: activeItem?.name ?? '', url: activeItem?.url }, - setBrowserInfo: undefined, - handleReload: undefined, + browserInfo: activeItem, + activeWebViewRef, + activeWebviewScreenShot, + setPreActiveTabId, }) } style={rightDomStyle.iconWrap}> @@ -50,37 +80,55 @@ const TabsDrawerContent: React.FC = () => { ); return null; - }, [activeTabId, tabs]); + }, [tabs, activeTabId, activeWebViewRef, activeWebviewScreenShot]); return ( } - leftCallback={() => dispatch(changeDrawerOpenStatus(false))} + leftCallback={backToSearchPage} rightDom={rightDom} safeAreaColor={['blue', 'white']} containerStyles={styles.container} scrollViewProps={{ disabled: true }} titleDom={activeTabId ? '' : `${tabs?.length} Tabs`}> {tabs?.map(ele => ( - + ))} - {!activeTabId && ( + {/* card group */} + {!activeTabId && isDrawerOpen && ( <> - {tabs.map(ele => { - return ; - })} + {tabs.map(ele => ( + + ))} - {t('Close All')} + dispatch(closeAllTabs())}> + {t('Close All')} + dispatch(changeDrawerOpenStatus(false))}> - console.log('done')}> + { + if (tabs.length === 0) return; + if (tabs.find(ele => ele.id === preActiveTabId)) { + dispatch(setActiveTab(preActiveTabId)); + } else { + dispatch(setActiveTab(tabs[tabs.length - 1].id)); + } + }}> {t('Done')} @@ -130,6 +178,7 @@ const styles = StyleSheet.create({ flexWrap: 'wrap', paddingLeft: pTd(20), paddingRight: pTd(20), + paddingBottom: pTd(50), }, }); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx new file mode 100644 index 0000000000..a7c5fd2637 --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx @@ -0,0 +1,115 @@ +import React, { useMemo } from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { StyleSheet, View } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { ModalBody } from 'components/ModalBody'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import { useCurrentCaInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; +import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; +import { ChainId } from '@portkey-wallet/types'; +import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; +import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; +import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; + +const MyWalletModal = () => { + const { t } = useLanguage(); + const caInfo = useCurrentCaInfo(); + const { walletName, currentNetwork } = useWallet(); + + const { + accountToken: { accountTokenList }, + } = useAppCASelector(state => state.assets); + + const caInfoList = useMemo(() => { + return Object.entries(caInfo || {}) + .map(([key, value]) => { + const info = value as CAInfo; + return info?.caAddress + ? { + chaiId: key, + caAddress: info.caAddress, + ...accountTokenList.find(token => token.chainId === key && token.symbol === ELF_SYMBOL), + } + : undefined; + }) + .filter(item => !!item); + }, [accountTokenList, caInfo]); + + return ( + + + {t('Wallet')} + + {walletName} + {caInfoList?.map((item, index) => ( + + + {formatStr2EllipsisStr(addressFormat(item?.caAddress, item?.chaiId as ChainId), 10)} + + {formatChainInfoToShow(item?.chaiId as ChainId, currentNetwork)} + + + + + {`${formatAmountShow(divDecimals(item?.balance, item?.decimals))} ${item?.symbol}`} + + + + + ))} + + + + ); +}; + +export const showWalletInfo = () => { + OverlayModal.show(, { + position: 'bottom', + containerStyle: { backgroundColor: defaultColors.bg6 }, + }); +}; + +export default { + showWalletInfo, +}; + +const styles = StyleSheet.create({ + contentWrap: { + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + group: { + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + marginTop: pTd(8), + borderRadius: pTd(6), + ...GStyles.paddingArg(16, 16, 0), + }, + walletTitle: { + marginTop: pTd(24), + paddingLeft: pTd(10), + }, + itemWrap: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingTop: pTd(16), + paddingBottom: pTd(16), + }, + itemBorderTop: { + borderTopColor: defaultColors.border6, + borderTopWidth: StyleSheet.hairlineWidth, + }, + itemChainInfo: { + marginTop: pTd(4), + }, + itemBalance: {}, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx index 872ca8a460..28250a4e9a 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx @@ -22,8 +22,6 @@ const Card: React.FC = (props: ICardsProps) => { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); - console.log('item', item); - return ( @@ -37,9 +35,10 @@ const Card: React.FC = (props: ICardsProps) => { dispatch(setActiveTab(item.id))}> diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx index 0e1e5b09be..de687dd98f 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { Dispatch, SetStateAction, useCallback } from 'react'; import OverlayModal from 'components/OverlayModal'; import { StyleSheet, TouchableOpacity, View, Share } from 'react-native'; import { TextL, TextS } from 'components/CommonText'; @@ -14,11 +14,11 @@ import { FontStyles } from 'assets/theme/styles'; import { setStringAsync } from 'expo-clipboard'; import CommonToast from 'components/CommonToast'; import { getFaviconUrl } from 'utils'; -import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; import { isIOS } from '@rneui/base'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; enum HANDLE_TYPE { REFRESH = 'Refresh', @@ -38,11 +38,14 @@ const handleArray = [ const BrowserEditModal = ({ browserInfo, - handleReload, + activeWebViewRef, + activeWebviewScreenShot, + setPreActiveTabId, }: { - browserInfo: IRecordsItemType; - setBrowserInfo: any; - handleReload: any; + browserInfo: ITabItem; + activeWebViewRef: any; + activeWebviewScreenShot: () => void; + setPreActiveTabId: Dispatch>; }) => { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); @@ -53,7 +56,7 @@ const BrowserEditModal = ({ switch (type) { case HANDLE_TYPE.REFRESH: - handleReload?.(); + activeWebViewRef?.current?.reload?.(); OverlayModal.hide(); break; @@ -64,9 +67,9 @@ const BrowserEditModal = ({ case HANDLE_TYPE.SHARE: await Share.share({ - message: isIOS ? browserInfo?.title ?? browserInfo.url : browserInfo?.url, - url: browserInfo?.url ?? browserInfo?.title ?? '', - title: browserInfo?.title ?? browserInfo.url, + message: isIOS ? browserInfo?.name ?? browserInfo.url : browserInfo?.url, + url: browserInfo?.url ?? browserInfo?.name ?? '', + title: browserInfo?.name ?? browserInfo.url, }).catch(shareError => { console.log(shareError); }); @@ -82,22 +85,34 @@ const BrowserEditModal = ({ case HANDLE_TYPE.SWITCH: OverlayModal.hide(); + activeWebviewScreenShot(); dispatch(setActiveTab(undefined)); + setPreActiveTabId(Number(browserInfo?.id)); + break; default: break; } }, - [handleReload, browserInfo.url, browserInfo?.title, t, dispatch], + [ + activeWebViewRef, + browserInfo.url, + browserInfo?.name, + browserInfo?.id, + t, + activeWebviewScreenShot, + dispatch, + setPreActiveTabId, + ], ); return ( - + - {browserInfo?.title} + {browserInfo?.name} handleUrl(HANDLE_TYPE.CANCEL)}> @@ -127,7 +142,12 @@ const BrowserEditModal = ({ ); }; -export const showBrowserModal = (props: { browserInfo: IRecordsItemType; setBrowserInfo: any; handleReload: any }) => { +export const showBrowserModal = (props: { + browserInfo: ITabItem; + activeWebViewRef: any; + activeWebviewScreenShot: () => void; + setPreActiveTabId: Dispatch>; +}) => { OverlayModal.show(, { position: 'bottom', containerStyle: { backgroundColor: defaultColors.bg6 }, @@ -144,7 +164,6 @@ const styles = StyleSheet.create({ backgroundColor: defaultColors.bg6, width: screenWidth, }, - headerWrap: {}, title: { textAlign: 'left', height: pTd(22), diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx index 9dd71438e6..77dface772 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -18,6 +18,7 @@ export default function TabsDrawer(props: TabsDrawerPropsType) { return ( console.log('open')} onClose={() => console.log('close')} drawerPosition="right" diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx new file mode 100644 index 0000000000..e1b3369e48 --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -0,0 +1,181 @@ +import React, { useMemo } from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { StyleSheet, View, Image } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { ModalBody } from 'components/ModalBody'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import { useCurrentCaInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; +import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; +import { ChainId } from '@portkey-wallet/types'; +import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; +import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; +import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { useGStyles } from 'assets/theme/useGStyles'; +import { CommonButtonProps } from 'components/CommonButton'; + +interface ConnectModalType { + dappInfo: DappStoreItem; + onReject: () => void; + onApprove: () => void; +} + +const ConnectModal = (props: ConnectModalType) => { + const { + dappInfo: { origin, name, icon }, + onReject, + onApprove, + } = props; + const { t } = useLanguage(); + const caInfo = useCurrentCaInfo(); + const { walletName, currentNetwork } = useWallet(); + const gStyles = useGStyles(); + + const { + accountToken: { accountTokenList }, + } = useAppCASelector(state => state.assets); + + const caInfoList = useMemo(() => { + return Object.entries(caInfo || {}) + .map(([key, value]) => { + const info = value as CAInfo; + return info?.caAddress + ? { + chaiId: key, + caAddress: info.caAddress, + ...accountTokenList.find(token => token.chainId === key && token.symbol === ELF_SYMBOL), + } + : undefined; + }) + .filter(item => !!item); + }, [accountTokenList, caInfo]); + + const buttonList = useMemo( + () => [ + { + title: t('Reject'), + type: 'outline' as CommonButtonProps['type'], + onPress: () => { + onReject?.(); + OverlayModal.hide(); + }, + }, + { + title: t('Approve'), + type: 'primary' as CommonButtonProps['type'], + onPress: () => { + onApprove?.(); + OverlayModal.hide(); + }, + }, + ], + [onApprove, onReject, t], + ); + + return ( + + + + + + {name} + + + {origin} + + + {t('Wallet')} + + {walletName} + {caInfoList?.map((item, index) => ( + + + {formatStr2EllipsisStr(addressFormat(item?.caAddress, item?.chaiId as ChainId), 10)} + + {formatChainInfoToShow(item?.chaiId as ChainId, currentNetwork)} + + + + {`${formatAmountShow(divDecimals(item?.balance, item?.decimals))} ${item?.symbol}`} + + + + ))} + + + + ); +}; + +export const showConnectModal = (props: ConnectModalType) => { + OverlayModal.show(, { + position: 'bottom', + enabledNestScrollView: true, + }); +}; + +export default { + showConnectModal, +}; + +const styles = StyleSheet.create({ + contentWrap: { + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + favIcon: { + width: pTd(48), + height: pTd(48), + borderRadius: pTd(24), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + marginBottom: pTd(8), + marginTop: pTd(24), + }, + title: { + marginBottom: pTd(2), + }, + group: { + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + marginTop: pTd(8), + borderRadius: pTd(6), + ...GStyles.paddingArg(16, 16, 0), + }, + walletTitle: { + marginTop: pTd(24), + paddingLeft: pTd(10), + }, + itemWrap: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingTop: pTd(16), + paddingBottom: pTd(16), + }, + itemBorderTop: { + borderTopColor: defaultColors.border6, + borderTopWidth: StyleSheet.hairlineWidth, + }, + itemChainInfo: { + marginTop: pTd(4), + }, + + buttonGroup: { + width: '100%', + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + }, + btn: { + width: pTd(160), + height: pTd(44), + }, +}); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx new file mode 100644 index 0000000000..318b19efc7 --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -0,0 +1,276 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { StyleSheet, View, Image } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { ModalBody } from 'components/ModalBody'; +import { TextL, TextS } from 'components/CommonText'; +import { useCurrentCaInfo, useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; +import { formatAmountShow } from '@portkey-wallet/utils/converter'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { useGStyles } from 'assets/theme/useGStyles'; +import { CommonButtonProps } from 'components/CommonButton'; +import { SendTransactionParams } from '@portkey/provider-types'; +import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; +import { ZERO } from '@portkey-wallet/constants/misc'; +import { usePin } from 'hooks/store'; +import { getContractBasic } from '@portkey-wallet/contracts/utils'; +import { getManagerAccount } from 'utils/redux'; +import { customFetch } from '@portkey-wallet/utils/fetch'; + +interface TransactionModalPropsType { + dappInfo: DappStoreItem; + transactionInfo: SendTransactionParams & { params: any }; + onReject: () => void; + onSign: () => void; +} +const ConnectModal = (props: TransactionModalPropsType) => { + const { + dappInfo: { origin, name, icon }, + transactionInfo, + onReject, + onSign, + } = props; + const { t } = useLanguage(); + const gStyles = useGStyles(); + const isMainnet = useIsMainnet(); + const pin = usePin(); + + const { walletName, caHash } = useCurrentWalletInfo(); + const amountInUsdShow = useAmountInUsdShow(); + + const chainInfo = useCurrentChain(transactionInfo.chainId); + const [, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); + + const currentCaInfo = useCurrentCaInfo(); + const currentCaAddress = currentCaInfo?.[transactionInfo.chainId]?.caAddress; + const isCAContract = useMemo( + () => chainInfo?.caContractAddress === transactionInfo?.contractAddress, + [chainInfo?.caContractAddress, transactionInfo?.contractAddress], + ); + + const [fee, setFee] = useState(''); + + const isTransfer = useMemo(() => transactionInfo.method.toLowerCase() === 'transfer', [transactionInfo.method]); + + const buttonList = useMemo( + () => [ + { + title: t('Reject'), + type: 'outline' as CommonButtonProps['type'], + onPress: () => { + onReject?.(); + OverlayModal.hide(); + }, + }, + { + title: t('Approve'), + type: 'primary' as CommonButtonProps['type'], + onPress: () => { + onSign?.(); + OverlayModal.hide(); + }, + }, + ], + [onReject, onSign, t], + ); + + const transferContent = useMemo(() => { + const { symbol, amount } = transactionInfo?.params?.paramsOption || {}; + + return ( + + Amount + {`${formatAmountShow(amount)} ${symbol}`} + {amountInUsdShow(amount, 0, symbol)} + {walletName} + from + {formatStr2EllipsisStr(addressFormat(currentCaAddress, transactionInfo.chainId))} + network + {formatChainInfoToShow(transactionInfo.chainId)} + transactionFee + {`${formatAmountShow(fee)} ELF`} + {amountInUsdShow(fee, 0, 'ELF')} + Total + {symbol === 'ELF' ? ( + <> + {`${formatAmountShow(ZERO.plus(amount).plus(fee))} ${symbol}`} + {isMainnet && {amountInUsdShow(ZERO.plus(amount).plus(fee).toNumber(), 0, symbol)}} + + ) : ( + <> + {`${formatAmountShow(fee)} ELF`} + {isMainnet && {amountInUsdShow(fee, 0, 'ELF')}} + {`${formatAmountShow(amount)} ${symbol}`} + {isMainnet && {amountInUsdShow(amount, 0, symbol)}} + + )} + + ); + }, [ + amountInUsdShow, + currentCaAddress, + fee, + isMainnet, + transactionInfo.chainId, + transactionInfo?.params?.paramsOption, + walletName, + ]); + + const otherTransactionContent = useMemo(() => { + const data = transactionInfo.params?.paramsOption || {}; + + return ( + + from + {formatStr2EllipsisStr(addressFormat(currentCaAddress, transactionInfo.chainId))} + network + {formatChainInfoToShow(transactionInfo.chainId)} + transactionFee + {`${formatAmountShow(fee)} ELF`} + {amountInUsdShow(fee, 0, 'ELF')} + + Data + + {Object.keys(data).map(item => ( + <> +
    {item}
    +
    {data[item]}
    + + ))} +
    +
    + ); + }, [amountInUsdShow, currentCaAddress, fee, transactionInfo.chainId, transactionInfo.params?.paramsOption]); + + // get fee + const getFee = useCallback(async () => { + if (!chainInfo || !pin) return; + const account = getManagerAccount(pin); + if (!account) return; + + const contract = await getContractBasic({ + contractAddress: chainInfo.caContractAddress, + rpcUrl: chainInfo?.endPoint, + account: account, + }); + + try { + const raw = await contract.encodedTx( + 'ManagerForwardCall', + isCAContract + ? transactionInfo?.params?.paramsOption + : { + caHash: caHash, + contractAddress: transactionInfo.contractAddress, + methodName: transactionInfo.method, + args: transactionInfo.params?.paramsOption, + }, + ); + + const { TransactionFee } = await customFetch(`${chainInfo?.endPoint}/api/blockChain/calculateTransactionFee`, { + method: 'POST', + params: { + RawTransaction: raw, + }, + }); + + setFee(TransactionFee?.ELF); + } catch (e) { + console.log(e); + setFee('0'); + console.log('get fee error', e); + } + }, [ + caHash, + chainInfo, + isCAContract, + pin, + transactionInfo.contractAddress, + transactionInfo.method, + transactionInfo.params?.paramsOption, + ]); + + useEffect(() => { + getFee(); + }, [getFee]); + + useEffect(() => { + const symbol = transactionInfo?.params?.paramsOption?.symbol; + if (!symbol) return; + if (symbol === 'ELF') { + getTokenPrice(symbol); + } else { + getTokensPrice([symbol, 'ELF']); + } + }, [getTokenPrice, getTokensPrice, transactionInfo?.params?.paramsOption?.symbol]); + + return ( + + + + + + {name} + + + {origin} + + {transactionInfo.method} + {isTransfer ? transferContent : otherTransactionContent} + + + + ); +}; + +export const showTransactionModal = (props: TransactionModalPropsType) => { + OverlayModal.show(, { + position: 'bottom', + enabledNestScrollView: true, + }); +}; + +export default { + showTransactionModal, +}; + +const styles = StyleSheet.create({ + contentWrap: { + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + favIcon: { + width: pTd(48), + height: pTd(48), + borderRadius: pTd(24), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + marginBottom: pTd(8), + marginTop: pTd(24), + }, + title: { + marginBottom: pTd(2), + }, + method: { + borderRadius: pTd(6), + marginTop: pTd(24), + textAlign: 'center', + color: defaultColors.primaryColor, + backgroundColor: defaultColors.bg9, + ...GStyles.paddingArg(2, 8), + }, +}); + +const transferGroupStyle = StyleSheet.create({ + title: {}, +}); + +const otherTransactionGroupStyle = StyleSheet.create({}); diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 3b2d3961bb..0462645136 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -72,7 +72,7 @@ export default class DappMobileOperator extends Operator { params: any; method: keyof IDappOverlay; }): Promise => { - const authorized = await this.dappOverlay[method](params); + const authorized = await this.dappOverlay[method](this.dapp, params); if (!authorized) return this.userDenied(eventName); }; protected isActive = async () => { @@ -135,6 +135,8 @@ export default class DappMobileOperator extends Operator { }; protected handleRequestAccounts: SendRequest = async (eventName, params) => { + console.log('===========params=========================', params); + await this.dappManager.addDapp(params); return generateNormalResponse({ eventName, @@ -208,6 +210,7 @@ export default class DappMobileOperator extends Operator { }) { // user confirm const response = await this.userConfirmation({ eventName, method, params }); + console.log('===response=================================', response); if (response) return response; return callBack(eventName, params); } diff --git a/packages/mobile-app-did/js/dapp/dappOverlay.ts b/packages/mobile-app-did/js/dapp/dappOverlay.ts index 40ebc92bef..f37020b453 100644 --- a/packages/mobile-app-did/js/dapp/dappOverlay.ts +++ b/packages/mobile-app-did/js/dapp/dappOverlay.ts @@ -1,50 +1,30 @@ import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { SendTransactionParams } from '@portkey/provider-types'; -import ActionSheet from 'components/ActionSheet'; +import ConnectOverlay from './components/ConnectOverlay'; +import TransactionOverlay from './components/TransactionOverlay'; export interface IDappOverlay { requestAccounts(dapp: DappStoreItem): Promise; - sendTransaction(params: SendTransactionParams): Promise; + sendTransaction(dapp: DappStoreItem, params: SendTransactionParams): Promise; } export class DappOverlay implements IDappOverlay { async requestAccounts(dapp: DappStoreItem): Promise { return new Promise(resolve => { - // mock approve - ActionSheet.alert({ - title: 'approve', - message: dapp.origin, - buttons: [ - { - title: 'OK', - type: 'solid', - onPress: () => resolve(true), - }, - { - title: 'DENIED', - onPress: () => resolve(false), - }, - ], + ConnectOverlay.showConnectModal({ + dappInfo: dapp, + onApprove: () => resolve(true), + onReject: () => resolve(false), }); }); } - async sendTransaction(params: SendTransactionParams): Promise { + async sendTransaction(dapp: DappStoreItem, params: SendTransactionParams): Promise { return new Promise(resolve => { - // mock approve - ActionSheet.alert({ - title: 'send', - message: JSON.stringify(params), - buttons: [ - { - title: 'OK', - type: 'solid', - onPress: () => resolve(true), - }, - { - title: 'DENIED', - onPress: () => resolve(false), - }, - ], + TransactionOverlay.showTransactionModal({ + dappInfo: dapp, + transactionInfo: params as any, + onSign: () => resolve(true), + onReject: () => resolve(false), }); }); } diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 1ce48c744c..56cedf3597 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -52,8 +52,8 @@ export default function DiscoverSearch() { const newValue = value.trim().replace(' ', ''); const id = Date.now(); - dispatch(addRecordsItem({ title: newValue, url: prefixUrlWithProtocol(newValue) })); - dispatch(createNewTab({ id, url: newValue })); + dispatch(addRecordsItem({ id, title: newValue, url: prefixUrlWithProtocol(newValue) })); + dispatch(createNewTab({ id, url: prefixUrlWithProtocol(newValue) })); dispatch(changeDrawerOpenStatus(true)); return; diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx index c9a25454eb..ff75dab8bf 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx @@ -9,8 +9,12 @@ import fonts from 'assets/theme/fonts'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import RecordItem from '../RecordItem'; -import { addRecordsItem, clearRecordsList } from '@portkey-wallet/store/store-ca/discover/slice'; -import navigationService from 'utils/navigationService'; +import { + addRecordsItem, + changeDrawerOpenStatus, + clearRecordsList, + createNewTab, +} from '@portkey-wallet/store/store-ca/discover/slice'; export default function RecordSection() { const { t } = useLanguage(); @@ -43,12 +47,10 @@ export default function RecordSection() { key={index} item={item} onPress={() => { - navigationService.navigate('ViewOnWebView', { - url: item?.url, - title: item?.title ?? '', - webViewPageType: 'discover', - }); - dispatch(addRecordsItem({ ...item })); + const id = Date.now(); + dispatch(addRecordsItem(item)); + dispatch(createNewTab({ id, url: item.url })); + dispatch(changeDrawerOpenStatus(true)); }} /> ))} diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx new file mode 100644 index 0000000000..a7e5037d90 --- /dev/null +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx @@ -0,0 +1,96 @@ +import React, { memo } from 'react'; +import { StyleSheet, View } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import Touchable from 'components/Touchable'; +import { pTd } from 'utils/unit'; +import { FontStyles } from 'assets/theme/styles'; +import { formatTransferTime } from 'utils'; +import { DeviceItemType, DeviceType } from '@portkey-wallet/types/types-ca/device'; +import Svg, { IconName } from 'components/Svg'; + +const deviceTypeIconMap: Record = { + [DeviceType.IOS]: 'phone-iOS', + [DeviceType.ANDROID]: 'phone-Android', + [DeviceType.MAC]: 'desk-mac', + [DeviceType.WINDOWS]: 'desk-win', + [DeviceType.OTHER]: 'desk-win', +}; + +interface DeviceItemProps { + onPress?: (e: any) => void; + isCurrent?: boolean; + deviceItem: DeviceItemType; + isShowArrow?: boolean; +} + +const DeviceItemRender = ({ onPress, isCurrent, deviceItem, isShowArrow = true }: DeviceItemProps) => { + return ( + + + + + + + + + {deviceItem.deviceInfo.deviceName} + {isCurrent && ( + + Current + + )} + + + {deviceItem.transactionTime ? formatTransferTime(deviceItem.transactionTime) : ''} + + + + {isShowArrow && } + + + ); +}; +const DeviceItem = memo(DeviceItemRender); + +export default DeviceItem; + +const styles = StyleSheet.create({ + deviceItemWrap: { + height: pTd(72), + paddingHorizontal: pTd(16), + marginBottom: pTd(24), + backgroundColor: defaultColors.bg1, + borderRadius: pTd(6), + flexDirection: 'row', + alignItems: 'center', + }, + leftWrap: { + marginRight: pTd(12), + paddingTop: pTd(17), + height: '100%', + }, + deviceItemInfoWrap: { + flex: 1, + height: '100%', + paddingVertical: pTd(14), + justifyContent: 'space-between', + }, + deviceItemInfo: { + flexDirection: 'row', + alignItems: 'center', + }, + currentWrap: { + marginLeft: pTd(12), + height: pTd(20), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + borderRadius: pTd(10), + paddingHorizontal: pTd(9), + justifyContent: 'center', + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx new file mode 100644 index 0000000000..32a74a5e90 --- /dev/null +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -0,0 +1,123 @@ +import React, { useEffect } from 'react'; +import PageContainer from 'components/PageContainer'; +import { StyleSheet, View, Image } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import { useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; +// import DeviceItem from './components/DeviceItem'; +import { FontStyles } from 'assets/theme/styles'; +import { pTd } from 'utils/unit'; +import myEvents from 'utils/deviceEvent'; + +import { useCurrentDappList } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import { useAppDispatch } from 'store/hooks'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import Touchable from 'components/Touchable'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import fonts from 'assets/theme/fonts'; +import NoData from 'components/NoData'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; + +const DeviceList: React.FC = () => { + const { refetch } = useDeviceList(); + const currentNetwork = useCurrentNetworkInfo(); + + const dispatch = useAppDispatch(); + const dappList = useCurrentDappList(); + + useEffect(() => { + const listener = myEvents.refreshDeviceList.addListener(() => { + refetch(); + }); + return () => { + listener.remove(); + }; + }, [refetch]); + + return ( + + {dappList?.map(item => ( + + { const { deviceAmount } = useDeviceList(); + const dappList = useCurrentDappList(); return ( { navigationService.navigate('DeviceList'); }} /> + { + navigationService.navigate('ConnectedSites'); + }} + /> ); }; diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts b/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts index 49d2d26303..6ebeacd090 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts @@ -1,11 +1,16 @@ import WalletSecurity from '.'; import DeviceNav from './Device/router'; +import Dapp from './Dapp/index'; const stackNav = [ { name: 'WalletSecurity', component: WalletSecurity, }, + { + name: 'ConnectedSites', + component: Dapp, + }, ...DeviceNav, ] as const; diff --git a/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx b/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx index 768a85d1d5..5f5f969585 100644 --- a/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx +++ b/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx @@ -58,6 +58,7 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingHorizontal: pTd(16), borderRadius: pTd(6), + marginBottom: pTd(24), }, menuIcon: { borderRadius: pTd(6), diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index 41710e8986..59d183f955 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -116,7 +116,7 @@ "react-native-echarts-pro": "^1.8.5", "react-native-flipper": "^0.161.0", "react-native-fs": "^2.20.0", - "react-native-gesture-handler": "^2.10.2", + "react-native-gesture-handler": "2.10.2", "react-native-get-random-values": "^1.8.0", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-largelist": "3.1.0-rc.2", @@ -124,7 +124,7 @@ "react-native-pager-view": "^6.0.2", "react-native-qrcode-styled": "^0.2.1", "react-native-randombytes": "^3.6.1", - "react-native-reanimated": "^2.17.0", + "react-native-reanimated": "2.17.0", "react-native-safe-area-context": "^4.3.1", "react-native-screens": "^3.15.0", "react-native-spinkit": "1.5.1", diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 15108f479a..b0d56a204c 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -6,7 +6,7 @@ const initialState: IDiscoverStateType = { isDrawerOpen: false, recordsList: [], whiteList: [], - activeTabId: -1, + activeTabId: undefined, tabs: [], }; @@ -40,10 +40,9 @@ export const discoverSlice = createSlice({ clearRecordsList: state => { state.recordsList = []; }, - closeAllTabs: (state, { payload }) => { + closeAllTabs: state => { state.tabs = []; - state.activeTabId = -1; - console.log('closeAllTabs', payload); + state.activeTabId = undefined; }, createNewTab: (state, { payload }) => { state.activeTabId = payload.id; diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index e9ed752091..63bea4c565 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -10,6 +10,6 @@ export interface IDiscoverStateType { isDrawerOpen: boolean; recordsList: IRecordsItemType[]; whiteList: any[]; - activeTabId: number; + activeTabId?: number; tabs: ITabItem[]; } diff --git a/packages/types/types-ca/discover.ts b/packages/types/types-ca/discover.ts index a8c695fd4f..d2621d62a9 100644 --- a/packages/types/types-ca/discover.ts +++ b/packages/types/types-ca/discover.ts @@ -11,4 +11,5 @@ export interface IGameListItemType { export interface IRecordsItemType { title?: string; url?: string; + id: string | number; } diff --git a/yarn.lock b/yarn.lock index bafba7cf0e..da45ceacb3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23367,7 +23367,7 @@ react-native-crypto@^2.2.0: public-encrypt "^4.0.0" randomfill "^1.0.3" -react-native-drawer-layout@^2.17.0: +react-native-drawer-layout@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-3.2.0.tgz#1ab05d0bed6bb684353c17c96e1d3e6c1a4e225d" integrity sha512-d/kvzeBhXjqcRGlfkTSB96ZRKH6g6YxJK+gPtUOCOCH5piHpsupgX+tLAHdM8r8NUzN6Tl9656xfJTuVb8Zrgw== @@ -23392,7 +23392,7 @@ react-native-fs@^2.20.0: base-64 "^0.1.0" utf8 "^3.0.0" -react-native-gesture-handler@^3.2.0: +react-native-gesture-handler@2.10.2: version "2.10.2" resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.10.2.tgz#0d7d82de767a551157eaedc5f74e389c5fc357b9" integrity sha512-yUwTrgLinaGRdJN3igL5/QP+09B294EKgoCH7QJ4ABKb4W2mUvSDbbuGMaYBNnwMKAD87Ns2q/qibLWy3E3LTw== @@ -23477,7 +23477,7 @@ react-native-ratings@^8.1.0: dependencies: lodash "^4.17.15" -react-native-reanimated@^2.17.0: +react-native-reanimated@2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.17.0.tgz#eae2308235961cdd79810e01dfdd7e88b1ae5b5c" integrity sha512-bVy+FUEaHXq4i+aPPqzGeor1rG4scgVNBbBz21ohvC7iMpB9IIgvGsmy1FAoodZhZ5sa3EPF67Rcec76F1PXlQ== From 533c3cb299e811679685ebb8cd593c682ac5cdb9 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 9 Jun 2023 19:16:02 +0800 Subject: [PATCH 065/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20yarn=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yarn.lock | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/yarn.lock b/yarn.lock index da45ceacb3..4782aaa9b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5201,13 +5201,13 @@ "@portkey/provider-types" "^0.0.1-alpha.19" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.20": - version "0.0.1-alpha.20" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.20.tgz#83fa5c6f2f835067f231376426e1b782fc1e1d7d" - integrity sha512-fMV/kZxXG1XMgo6FMH5Cqbc3wfcPaWPUhdbwKxrKl8UxdiAj9w5T8lISTOTArHipKcIGROYRGuvZO7QK7KdBCA== +"@portkey/extension-provider@0.0.1-alpha.21": + version "0.0.1-alpha.21" + resolved "https://registry.yarnpkg.com/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.21.tgz#388c6ce3a8832a9bd869854fe7cff46f398eb465" + integrity sha512-Cy+JrhCZde8h10iG9pyXD9uHtv/IVsc7HRLgrC0X1KBNWM60TFxhHsCVHyQcVivvRooFbGDxqlQpGWbwqlJNIw== dependencies: "@portkey/provider-types" "^0.0.1-alpha.19" - "@portkey/providers" "^0.0.1-alpha.20" + "@portkey/providers" "^0.0.1-alpha.21" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" @@ -5216,29 +5216,36 @@ resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.19.tgz#2c20c58aa914145119dc865b644299f37fe7344e" integrity sha512-aPZP3Ud+xroya1s9bGdQ/RCXsvuzUegw40lMFV2hj8vHMhlFbd90R4U0tlITRJYModxjs8mUB81CMoH+6C+vFg== -"@portkey/provider-types@0.0.1-alpha.20", "@portkey/provider-types@^0.0.1-alpha.19": +"@portkey/provider-types@0.0.1-alpha.21", "@portkey/provider-types@^0.0.1-alpha.19": version "0.0.1-alpha.19" resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.19.tgz#d67194cda11641c6b9ad2b10d8421ee159fd214f" integrity sha512-nNV2tk+ttNTItZwVRuvWh5OlUPPw5rv0C68n/b73BksJ8ChRh7kK0Bmc6mQkKsmx2J6IWTMtMQix+3VbINS9EA== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.20", "@portkey/provider-utils@^0.0.1-alpha.19": +"@portkey/provider-utils@0.0.1-alpha.21", "@portkey/provider-utils@^0.0.1-alpha.21": + version "0.0.1-alpha.21" + resolved "https://registry.yarnpkg.com/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.21.tgz#6548ecd3a9d9abd809a968bc0700deae6c8bc0e9" + integrity sha512-BXihRsZyMNrvN2PM8AP0GyglzbtAwULE6wfjqY4bjaOLkvT48jZ/VmQy7nvMpYaQciXEMiL0tUooOCqlQ0W1Sg== + dependencies: + "@portkey/provider-types" "^0.0.1-alpha.19" + +"@portkey/provider-utils@^0.0.1-alpha.19": version "0.0.1-alpha.19" resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.19.tgz#0917943b88e91456e989c51e60f0a186fc6395cc" integrity sha512-RiTE21bl1fEfvsth97L8gyfRanYyQy1H69ozMdltW+FFb3bd6Skcd1qMJgNQwQVsiVTepjpzvxqJuHWM/N4Wvw== dependencies: "@portkey/provider-types" "^0.0.1-alpha.19" -"@portkey/providers@0.0.1-alpha.20", "@portkey/providers@^0.0.1-alpha.20": - version "0.0.1-alpha.20" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.20.tgz#c39cd18ca7ae4b4a98fc0df26e88bef76182174b" - integrity sha512-FyjTY3yRb9yiRTJF0BZueVzOH9MHDQV5GcsvE9gwg2zLjvBfEaT5lxBbAS2FgoL1d2mBLc2mthlyxa8nCAJQ7Q== +"@portkey/providers@0.0.1-alpha.21", "@portkey/providers@^0.0.1-alpha.21": + version "0.0.1-alpha.21" + resolved "https://registry.yarnpkg.com/@portkey/providers/-/providers-0.0.1-alpha.21.tgz#c233c0deab104dafb83a8975316d5cbd936c9f76" + integrity sha512-0iBi0VD5i6/HJOf+0Xw3e7Lv1fyL8aR/zNcEu/3UYjsxjFGZys55mhK1Pe8XHTRgQPJY6RpU1DTecmquFwG2ew== dependencies: "@metamask/object-multiplex" "^1.1.0" "@portkey/chain" "^0.0.1-alpha.19" "@portkey/provider-types" "^0.0.1-alpha.19" - "@portkey/provider-utils" "^0.0.1-alpha.19" + "@portkey/provider-utils" "^0.0.1-alpha.21" "@types/readable-stream" "^2.3.15" lodash "^4.17.21" pump "^3.0.0" From 516dd80ce6d98160f799eec84a99eef43b5a207e Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 9 Jun 2023 20:26:33 +0800 Subject: [PATCH 066/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20sell=20?= =?UTF-8?q?fetch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/util/index.ts | 15 ++++++++++++-- .../app/web/pages/Buy/const.ts | 4 ++-- .../app/web/pages/Buy/index.tsx | 20 +++++++++++-------- .../pages/Home/components/MyBalance/index.tsx | 4 ++-- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/api/api-did/payment/util/index.ts b/packages/api/api-did/payment/util/index.ts index 0fe2107997..1dea0df537 100644 --- a/packages/api/api-did/payment/util/index.ts +++ b/packages/api/api-did/payment/util/index.ts @@ -1,6 +1,7 @@ import { request } from '@portkey-wallet/api/api-did'; import { CryptoInfoType, GetAchTokenDataType, OrderQuoteType } from '../type'; import { TransDirectEnum } from '@portkey-wallet/constants/constants-ca/payment'; +import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; export interface GetOrderQuoteParamsType { crypto: string; @@ -23,7 +24,12 @@ export const getOrderQuote = async (params: GetOrderQuoteParamsType) => { return rst.data as OrderQuoteType; }; -export const getCryptoInfo = async (params: { fiat: string }, symbol: string, _chainId: string) => { +export const getCryptoInfo = async ( + params: { fiat: string }, + symbol: string, + _chainId: string, + side: PaymentTypeEnum, +) => { // FIXME _chainId to chainId console.log( 'At present, only the main network is connected to legal currency, and the test is the faucet. If the test network is connected to legal currency, chainId will be used', @@ -35,7 +41,12 @@ export const getCryptoInfo = async (params: { fiat: string }, symbol: string, _c if (rst.returnCode !== '0000') { throw new Error(rst.returnMsg); } - return (rst.data as CryptoInfoType[]).find((item: any) => item.crypto === symbol && item.network === symbol); + return (rst.data as CryptoInfoType[]).find( + (item: any) => + item.crypto === symbol && + item.network === symbol && + (side === PaymentTypeEnum.BUY ? Number(item.buyEnable) === 1 : Number(item.sellEnable) === 1), + ); }; export const getCryptoList = async (params: { fiat: string }) => { diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index 02a76e41b0..6883cd7a0e 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -44,7 +44,7 @@ export const initLimit = { export const MAX_UPDATE_TIME = 15; export const initCurrency = '200'; -export const initCrypto = '100'; +export const initCrypto = '300'; export const initValueSave: { amount: string; currency: string; @@ -53,7 +53,7 @@ export const initValueSave: { network: string; min: number | null; max: number | null; - side: string; + side: PaymentTypeEnum; } = { amount: initCurrency, currency: 'USD', diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 89cf3105db..c7ecf1b1ff 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -188,12 +188,12 @@ export default function Buy() { }, 1000); }; return { updateReceive, handleSetTimer }; - }, [setErrMsgCase]); + }, [setErrMsgCase, setReceiveCase]); const updateCrypto = useCallback( async (fiat = curFiat.currency || 'USD') => { const { crypto, network, side } = valueSaveRef.current; - const data = await getCryptoInfo({ fiat }, crypto, network); + const data = await getCryptoInfo({ fiat }, crypto, network, side); if (side === PaymentTypeEnum.BUY) { if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { setLimit({ max: data.maxPurchaseAmount, min: data.minPurchaseAmount }); @@ -256,7 +256,7 @@ export default function Buy() { setRateUpdateTime(MAX_UPDATE_TIME); updateTimeRef.current = MAX_UPDATE_TIME; handleSetTimer(); - }, [handleSetTimer]); + }, [handleSetTimer, setReceiveCase]); const handlePageChange = useCallback( async (e: RadioChangeEvent) => { @@ -304,9 +304,12 @@ export default function Buy() { if (v.currency === curFiat.currency) return; try { clearInterval(updateTimerRef.current); + setErrMsg(''); + setReceive(''); + setRate(''); setLoading(true); const { crypto, network, amount, side } = valueSaveRef.current; - const data = await getCryptoInfo({ fiat: v.currency }, crypto, network); + const data = await getCryptoInfo({ fiat: v.currency }, crypto, network, side); if (side === PaymentTypeEnum.BUY) { if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { setLimit({ max: data.maxPurchaseAmount, min: data.minPurchaseAmount }); @@ -317,7 +320,7 @@ export default function Buy() { await getQuoteAndSetData(); setErrMsg(''); } else { - setErrMsg(showLimitText(data.minPurchaseAmount, data.maxPurchaseAmount, v.currency)); + setErrMsgCase(); setReceive(''); setRate(''); } @@ -332,12 +335,13 @@ export default function Buy() { setLimit({ max: data.maxSellAmount, min: data.minSellAmount }); valueSaveRef.current.max = data.maxSellAmount; valueSaveRef.current.min = data.minSellAmount; + setErrMsgCase(); - if (isValidValue({ amount, min: data.maxSellAmount, max: data.minSellAmount })) { + if (isValidValue({ amount, max: data.maxSellAmount, min: data.minSellAmount })) { await getQuoteAndSetData(); setErrMsg(''); } else { - setErrMsg(showLimitText(data.minSellAmount, data.maxSellAmount, v.currency)); + setErrMsgCase(); setReceive(''); setRate(''); } @@ -355,7 +359,7 @@ export default function Buy() { } } }, - [curFiat.currency, getQuoteAndSetData, isValidValue, setLoading, showLimitText], + [curFiat.currency, getQuoteAndSetData, isValidValue, setErrMsgCase, setLoading], ); const { diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index 32b733a7f6..dbb1c6fb9d 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -20,7 +20,7 @@ import { fetchAllTokenListAsync, getSymbolImagesAsync } from '@portkey-wallet/st import { getWalletNameAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import CustomTokenModal from 'pages/components/CustomTokenModal'; import { AccountAssetItem } from '@portkey-wallet/types/types-ca/token'; -import { fetchBuyFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; +import { fetchBuyFiatListAsync, fetchSellFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; import { useFreshTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; import useVerifierList from 'hooks/useVerifierList'; @@ -89,12 +89,12 @@ export default function MyBalance() { appDispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray })); appDispatch(getWalletNameAsync()); appDispatch(getSymbolImagesAsync()); - // appDispatch(fetchSellFiatListAsync()); }, [passwordSeed, appDispatch, caAddresses, chainIdArray, caAddressInfos, isMainNet, state?.key]); useEffect(() => { getGuardianList({ caHash: walletInfo?.caHash }); isMainNet && appDispatch(fetchBuyFiatListAsync()); + isMainNet && appDispatch(fetchSellFiatListAsync()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMainNet]); From 2113045b59639d7b864a85bc68d4baca0b0c094a Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Sat, 10 Jun 2023 10:49:10 +0800 Subject: [PATCH 067/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20change=20to?= =?UTF-8?q?=20simulated=20Input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/CustomHeader/index.tsx | 5 ++- .../js/pages/Discover/DiscoverHome/index.tsx | 14 +++--- .../components/SimulatedInputBox/index.tsx | 45 +++++++++++++++++++ 3 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx diff --git a/packages/mobile-app-did/js/components/CustomHeader/index.tsx b/packages/mobile-app-did/js/components/CustomHeader/index.tsx index d1b601f910..ea16f93fcf 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/index.tsx +++ b/packages/mobile-app-did/js/components/CustomHeader/index.tsx @@ -14,6 +14,7 @@ import { useHardwareBackPress } from '@portkey-wallet/hooks/mobile'; export type CustomHeaderProps = { themeType?: SafeAreaColorMapKeyUnit; + noLeftDom?: boolean; leftDom?: ReactNode; titleDom?: ReactNode | string; rightDom?: ReactNode; @@ -29,6 +30,7 @@ const CustomHeader: React.FC = props => { const { t } = useLanguage(); const { + noLeftDom = false, leftDom = null, titleDom = 'title', rightDom = null, @@ -77,6 +79,7 @@ const CustomHeader: React.FC = props => { }, [navigation, onGestureStartCallback]); const letElement = useMemo(() => { + if (noLeftDom) return null; if (leftDom) return leftDom; if (!isCanGoBack && !leftCallback) return null; const onPress = leftCallback ? leftCallback : () => navigationService.goBack(); @@ -93,7 +96,7 @@ const CustomHeader: React.FC = props => { {leftIcon}
    ); - }, [backTitle, isCanGoBack, leftCallback, leftDom, leftIcon, styles.leftBackTitle, t, type]); + }, [backTitle, isCanGoBack, leftCallback, leftDom, leftIcon, noLeftDom, styles.leftBackTitle, t, type]); const centerElement = useMemo(() => { if (typeof titleDom === 'string') diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 6c98069a96..7b138f144c 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -8,25 +8,21 @@ import navigationService from 'utils/navigationService'; import { useLanguage } from 'i18n/hooks'; import GameSection from '../components/GameSection'; import { GamesList } from './GameData'; +import SimulatedInputBox from '../components/SimulatedInputBox'; export default function DiscoverHome() { const { t } = useLanguage(); - const navigateToSearch = useCallback(() => { - console.log('aaa'); - - return navigationService.navigate('DiscoverSearch'); - }, []); + const navigateToSearch = useCallback(() => navigationService.navigate('DiscoverSearch'), []); return ( - - navigateToSearch()} /> - + ); diff --git a/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx new file mode 100644 index 0000000000..9f59616472 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx @@ -0,0 +1,45 @@ +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import { TextM } from 'components/CommonText'; +import Svg from 'components/Svg'; +import React from 'react'; +import { StyleSheet, View, TouchableWithoutFeedback } from 'react-native'; +import { pTd } from 'utils/unit'; + +interface SimulatedInputBoxProps { + onClickInput: () => any; +} + +export default function SimulatedInputBox({ onClickInput }: SimulatedInputBoxProps) { + return ( + + + + + Search Dapp or enter URL to explore + + + + ); +} + +const styles = StyleSheet.create({ + wrap: { + width: '100%', + ...GStyles.paddingArg(8, 20), + }, + innerInput: { + height: pTd(36), + ...GStyles.paddingArg(7, 17), + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + borderRadius: pTd(6), + backgroundColor: defaultColors.bg4, + }, + content: { + marginLeft: pTd(8), + }, +}); From a79efeb48fbf7944a98c0b61b51cae7cb3e65380 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 10:14:18 +0800 Subject: [PATCH 068/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20isIp=20func?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/dapp/browser.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/utils/dapp/browser.ts b/packages/utils/dapp/browser.ts index 578a25ba9b..fda7084013 100644 --- a/packages/utils/dapp/browser.ts +++ b/packages/utils/dapp/browser.ts @@ -12,6 +12,15 @@ export function getUrlObj(url: string) { return new Url(url); } +/** + * check if url is ip + * @param url + * @returns + */ +const isIp = (url: string): boolean => { + return /((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))/.test(url); +}; + /** * Returns URL prefixed with protocol * @@ -20,6 +29,7 @@ export function getUrlObj(url: string) { * @returns - String corresponding to sanitized input depending if it's a search or url */ export const prefixUrlWithProtocol = (url: string, defaultProtocol = 'https://') => { + if (isIp(url)) defaultProtocol = 'http://'; const hasProtocol = /^[a-z]*:\/\//.test(url); const sanitizedURL = hasProtocol ? url : `${defaultProtocol}${url}`; return sanitizedURL; @@ -58,24 +68,11 @@ export function getFaviconUrl(url: string, size: number = 50): string { * a keyword is detected instead of a url * * @param input - String corresponding to url input - * @param searchEngine - Protocol string to append to URLs that have none * @param defaultProtocol - Protocol string to append to URLs that have none * @returns - String corresponding to sanitized input depending if it's a search or url */ -export default function onUrlSubmit(input: string, searchEngine = 'Google', defaultProtocol = 'https://') { +export default function getFullUrl(input: string, defaultProtocol = 'https://') { //Check if it's a url or a keyword - const regEx = new RegExp(/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!&',;=.+]+$/g); - if (!isUrl(input) && !regEx.test(input)) { - // Add exception for localhost - if (!input.startsWith('http://localhost') && !input.startsWith('localhost')) { - // In case of keywords we default to google search - let searchUrl = 'https://www.google.com/search?q=' + encodeURIComponent(input); - if (searchEngine === 'DuckDuckGo') { - searchUrl = 'https://duckduckgo.com/?q=' + encodeURIComponent(input); - } - return searchUrl; - } - } return prefixUrlWithProtocol(input, defaultProtocol); } From 42e00aa5917750e709ec151b50c217601f312aa7 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 10:18:33 +0800 Subject: [PATCH 069/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20discove?= =?UTF-8?q?r=20store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TabsDrawer/TabsDrawerContent.tsx | 32 +- .../TabsDrawer/components/card/index.tsx | 16 +- .../components/tabsOverlay/index.tsx | 11 +- .../dapp/components/ConnectOverlay/index.tsx | 6 +- .../components/TransactionOverlay/index.tsx | 315 +++++++++++++++--- packages/mobile-app-did/js/hooks/discover.ts | 26 ++ packages/mobile-app-did/js/hooks/useLogOut.ts | 3 + .../pages/Discover/DiscoverHome/GameData.ts | 14 - .../js/pages/Discover/DiscoverHome/index.tsx | 34 +- .../pages/Discover/DiscoverSearch/index.tsx | 73 ++-- .../Discover/SwitchTabsContent/index.tsx | 46 --- .../DiscoverCmsListSection/index.tsx | 92 +++++ .../index.tsx | 8 +- .../Discover/components/GameItem/index.tsx | 71 ---- .../Discover/components/GameSection/index.tsx | 66 ---- .../Discover/components/RecordItem/index.tsx | 8 +- .../components/RecordSection/index.tsx | 22 +- .../SearchDiscoverSection/index.tsx | 114 +++++++ .../Dapp/components/DappItem/index.tsx | 96 ------ .../js/pages/My/WalletSecurity/Dapp/index.tsx | 18 +- packages/store/store-ca/cms/types.ts | 2 + .../store/store-ca/discover/index.test.ts | 120 ------- packages/store/store-ca/discover/slice.ts | 92 +++-- packages/store/store-ca/discover/type.ts | 19 +- 24 files changed, 690 insertions(+), 614 deletions(-) create mode 100644 packages/mobile-app-did/js/hooks/discover.ts delete mode 100644 packages/mobile-app-did/js/pages/Discover/DiscoverHome/GameData.ts delete mode 100644 packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx rename packages/mobile-app-did/js/pages/Discover/components/{RecordImage => DiscoverWebsiteImage}/index.tsx (82%) delete mode 100644 packages/mobile-app-did/js/pages/Discover/components/GameItem/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/Discover/components/GameSection/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx delete mode 100644 packages/store/store-ca/discover/index.test.ts diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 349efed0fa..50513ecc5b 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -27,37 +27,41 @@ import { captureRef } from 'react-native-view-shot'; import { showWalletInfo } from './components/WalletInfoOverlay'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; + const takeSnapshot = (viewRef: any) => captureRef(viewRef?.current, { format: 'jpg', quality: 0.2 }); const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); + const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { activeTabId, tabs, isDrawerOpen } = useAppCASelector(state => state.discover); + const { isDrawerOpen, discoverMap } = useAppCASelector(state => state.discover); + const { activeTabId, tabs } = discoverMap[networkType] ?? {}; const [activeTabRef, setActiveTabRef] = React.useState(null); const [activeWebViewRef, setActiveWebViewRef] = React.useState(null); - const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); + const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); const activeWebviewScreenShot = useCallback(() => { takeSnapshot(activeTabRef).then( uri => { console.log('Image saved to', uri); - dispatch(updateTab({ id: activeTabId, screenShotUrl: uri })); + dispatch(updateTab({ id: activeTabId || 0, screenShotUrl: uri, networkType })); }, error => console.error('Oops, snapshot failed', error), ); - }, [activeTabId, activeTabRef, dispatch]); + }, [activeTabId, activeTabRef, dispatch, networkType]); const backToSearchPage = useCallback(() => { activeWebviewScreenShot(); - dispatch(setActiveTab(undefined)); + dispatch(setActiveTab({ id: undefined, networkType })); dispatch(changeDrawerOpenStatus(false)); - }, [activeWebviewScreenShot, dispatch]); + }, [activeWebviewScreenShot, dispatch, networkType]); // header right const rightDom = useMemo(() => { - const activeItem = tabs.find(ele => ele.id === activeTabId) as ITabItem; + const activeItem = tabs?.find(ele => ele.id === activeTabId) as ITabItem; if (activeTabId) return ( @@ -80,7 +84,7 @@ const TabsDrawerContent: React.FC = () => {
    ); return null; - }, [tabs, activeTabId, activeWebViewRef, activeWebviewScreenShot]); + }, [activeTabId, activeWebViewRef, activeWebviewScreenShot, tabs]); return ( { <> - {tabs.map(ele => ( + {tabs?.map(ele => ( ))} - dispatch(closeAllTabs())}> + dispatch(closeAllTabs({ networkType }))}> {t('Close All')} dispatch(changeDrawerOpenStatus(false))}> @@ -122,11 +126,11 @@ const TabsDrawerContent: React.FC = () => { { - if (tabs.length === 0) return; - if (tabs.find(ele => ele.id === preActiveTabId)) { - dispatch(setActiveTab(preActiveTabId)); + if (tabs?.length === 0) return; + if (tabs?.find(ele => ele.id === preActiveTabId)) { + dispatch(setActiveTab({ id: preActiveTabId, networkType })); } else { - dispatch(setActiveTab(tabs[tabs.length - 1].id)); + dispatch(setActiveTab({ id: tabs?.[tabs?.length - 1]?.id, networkType })); } }}> {t('Done')} diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx index 28250a4e9a..3067bcf8ea 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx @@ -7,11 +7,12 @@ import { pTd } from 'utils/unit'; import Svg from 'components/Svg'; -import { useLanguage } from 'i18n/hooks'; import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { closeExistingTab, setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; interface ICardsProps { item: ITabItem; @@ -19,21 +20,22 @@ interface ICardsProps { const Card: React.FC = (props: ICardsProps) => { const { item } = props; - const { t } = useLanguage(); + const dispatch = useAppCommonDispatch(); + const { networkType } = useCurrentNetworkInfo(); return ( - + {item?.name ?? getHost(item?.url)} - dispatch(closeExistingTab(item.id))}> + dispatch(closeExistingTab({ id: item?.id, networkType }))}> - dispatch(setActiveTab(item.id))}> + dispatch(setActiveTab({ id: item.id, networkType }))}> { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); + const { networkType } = useCurrentNetworkInfo(); const handleUrl = useCallback( async (type: HANDLE_TYPE) => { @@ -86,7 +88,7 @@ const BrowserEditModal = ({ case HANDLE_TYPE.SWITCH: OverlayModal.hide(); activeWebviewScreenShot(); - dispatch(setActiveTab(undefined)); + dispatch(setActiveTab({ id: undefined, networkType })); setPreActiveTabId(Number(browserInfo?.id)); break; @@ -103,6 +105,7 @@ const BrowserEditModal = ({ t, activeWebviewScreenShot, dispatch, + networkType, setPreActiveTabId, ], ); @@ -110,7 +113,7 @@ const BrowserEditModal = ({ return ( - + {browserInfo?.name} diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index e1b3369e48..29aea53f64 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -19,6 +19,8 @@ import { FontStyles } from 'assets/theme/styles'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { useGStyles } from 'assets/theme/useGStyles'; import { CommonButtonProps } from 'components/CommonButton'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; interface ConnectModalType { dappInfo: DappStoreItem; @@ -82,9 +84,9 @@ const ConnectModal = (props: ConnectModalType) => { - + - {name} + {name || getHost(origin)} {origin} diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 318b19efc7..178e9ae24a 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -1,13 +1,13 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, View, Image } from 'react-native'; +import { StyleSheet, View, Text } from 'react-native'; import { defaultColors } from 'assets/theme'; import fonts from 'assets/theme/fonts'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; import { ModalBody } from 'components/ModalBody'; -import { TextL, TextS } from 'components/CommonText'; -import { useCurrentCaInfo, useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import { useCurrentCaInfo, useCurrentWalletInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; import GStyles from 'assets/theme/GStyles'; @@ -24,6 +24,9 @@ import { usePin } from 'hooks/store'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount } from 'utils/redux'; import { customFetch } from '@portkey-wallet/utils/fetch'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; interface TransactionModalPropsType { dappInfo: DappStoreItem; @@ -43,11 +46,13 @@ const ConnectModal = (props: TransactionModalPropsType) => { const isMainnet = useIsMainnet(); const pin = usePin(); - const { walletName, caHash } = useCurrentWalletInfo(); + const { walletName } = useWallet(); + const wallet = useCurrentWalletInfo(); + const amountInUsdShow = useAmountInUsdShow(); const chainInfo = useCurrentChain(transactionInfo.chainId); - const [, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); + const [tokenPriceObject, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); const currentCaInfo = useCurrentCaInfo(); const currentCaAddress = currentCaInfo?.[transactionInfo.chainId]?.caAddress; @@ -86,41 +91,82 @@ const ConnectModal = (props: TransactionModalPropsType) => { const { symbol, amount } = transactionInfo?.params?.paramsOption || {}; return ( - - Amount - {`${formatAmountShow(amount)} ${symbol}`} - {amountInUsdShow(amount, 0, symbol)} - {walletName} - from - {formatStr2EllipsisStr(addressFormat(currentCaAddress, transactionInfo.chainId))} - network - {formatChainInfoToShow(transactionInfo.chainId)} - transactionFee - {`${formatAmountShow(fee)} ELF`} - {amountInUsdShow(fee, 0, 'ELF')} - Total - {symbol === 'ELF' ? ( - <> - {`${formatAmountShow(ZERO.plus(amount).plus(fee))} ${symbol}`} - {isMainnet && {amountInUsdShow(ZERO.plus(amount).plus(fee).toNumber(), 0, symbol)}} - - ) : ( - <> - {`${formatAmountShow(fee)} ELF`} - {isMainnet && {amountInUsdShow(fee, 0, 'ELF')}} - {`${formatAmountShow(amount)} ${symbol}`} - {isMainnet && {amountInUsdShow(amount, 0, symbol)}} - + <> + + {`- ${formatAmountShow(amount)} ${symbol}`} + + {!isMainnet && ( + {`-$ ${formatAmountShow( + ZERO.plus(amount).multipliedBy(tokenPriceObject[symbol]), + )}`} )} - + + + {/* From */} + + + {t('From')} + {walletName} + + + + + {formatStr2EllipsisStr( + addressFormat(wallet?.[transactionInfo?.chainId]?.caAddress, transactionInfo.chainId), + )} + + + + {/* network */} + + + + {t('Network')} + + {formatChainInfoToShow(transactionInfo.chainId)} + + + + + {/* transactionFee */} + + + + {t('Transaction Fee')} + {`${formatAmountShow(fee)} ELF`} + + + + {amountInUsdShow(fee, 0, 'ELF')} + + + + {/* Total + {symbol === 'ELF' ? ( + <> + {`${formatAmountShow(ZERO.plus(amount).plus(fee))} ${symbol}`} + {isMainnet && {amountInUsdShow(ZERO.plus(amount).plus(fee).toNumber(), 0, symbol)}} + + ) : ( + <> + {`${formatAmountShow(fee)} ELF`} + {isMainnet && {amountInUsdShow(fee, 0, 'ELF')}} + {`${formatAmountShow(amount)} ${symbol}`} + {isMainnet && {amountInUsdShow(amount, 0, symbol)}} + + )} */} + + ); }, [ amountInUsdShow, - currentCaAddress, fee, isMainnet, + t, + tokenPriceObject, transactionInfo.chainId, transactionInfo?.params?.paramsOption, + wallet, walletName, ]); @@ -129,16 +175,49 @@ const ConnectModal = (props: TransactionModalPropsType) => { return ( - from - {formatStr2EllipsisStr(addressFormat(currentCaAddress, transactionInfo.chainId))} - network - {formatChainInfoToShow(transactionInfo.chainId)} - transactionFee - {`${formatAmountShow(fee)} ELF`} - {amountInUsdShow(fee, 0, 'ELF')} + + {/* From */} + + + {t('From')} + {walletName} + + + + + {formatStr2EllipsisStr( + addressFormat(wallet?.[transactionInfo?.chainId]?.caAddress, transactionInfo.chainId), + )} + + + + {/* network */} + + + + {t('Network')} + + {formatChainInfoToShow(transactionInfo.chainId)} + + + - Data - + {/* transactionFee */} + + + + {t('Transaction Fee')} + {`${formatAmountShow(fee)} ELF`} + + + + {amountInUsdShow(fee, 0, 'ELF')} + + + + + + Card {Object.keys(data).map(item => ( <>
    {item}
    @@ -148,7 +227,7 @@ const ConnectModal = (props: TransactionModalPropsType) => {
    ); - }, [amountInUsdShow, currentCaAddress, fee, transactionInfo.chainId, transactionInfo.params?.paramsOption]); + }, [amountInUsdShow, fee, t, transactionInfo.chainId, transactionInfo.params?.paramsOption, wallet, walletName]); // get fee const getFee = useCallback(async () => { @@ -168,7 +247,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { isCAContract ? transactionInfo?.params?.paramsOption : { - caHash: caHash, + caHash: wallet.caHash, contractAddress: transactionInfo.contractAddress, methodName: transactionInfo.method, args: transactionInfo.params?.paramsOption, @@ -189,13 +268,13 @@ const ConnectModal = (props: TransactionModalPropsType) => { console.log('get fee error', e); } }, [ - caHash, chainInfo, isCAContract, pin, transactionInfo.contractAddress, transactionInfo.method, transactionInfo.params?.paramsOption, + wallet.caHash, ]); useEffect(() => { @@ -216,9 +295,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { - + - {name} + {name || getHost(origin)} {origin} @@ -270,7 +349,149 @@ const styles = StyleSheet.create({ }); const transferGroupStyle = StyleSheet.create({ - title: {}, + tokenCount: { + marginTop: pTd(4), + fontSize: pTd(28), + width: screenWidth, + textAlign: 'center', + }, + tokenUSD: { + color: defaultColors.font3, + width: screenWidth, + textAlign: 'center', + marginTop: pTd(4), + }, + group: { + backgroundColor: defaultColors.bg1, + marginTop: pTd(24), + paddingLeft: pTd(16), + paddingRight: pTd(16), + borderRadius: pTd(6), + }, + buttonWrapStyle: { + justifyContent: 'flex-end', + paddingBottom: pTd(12), + paddingTop: pTd(12), + }, + errorMessage: { + lineHeight: pTd(16), + color: defaultColors.error, + marginTop: pTd(4), + paddingLeft: pTd(8), + }, + wrap: { + height: pTd(56), + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + }, + borderTop: { + borderTopColor: defaultColors.border6, + borderTopWidth: StyleSheet.hairlineWidth, + }, + title: { + flex: 1, + color: defaultColors.font3, + }, + tokenNum: { + textAlign: 'right', + color: defaultColors.font5, + }, + usdtNum: { + marginLeft: pTd(6), + marginTop: pTd(4), + color: defaultColors.font3, + textAlign: 'right', + }, + notELFWrap: { + height: pTd(84), + alignItems: 'flex-start', + paddingTop: pTd(18), + paddingBottom: pTd(18), + }, + totalWithUSD: { + marginTop: pTd(12), + display: 'flex', + justifyContent: 'flex-end', + flexDirection: 'row', + }, + flexSpaceBetween: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + width: '100%', + lineHeight: pTd(20), + }, + titles1: { + marginTop: pTd(56), + }, + values1: { + marginTop: pTd(4), + }, + divider: { + marginTop: pTd(24), + width: '100%', + height: StyleSheet.hairlineWidth, + backgroundColor: defaultColors.border6, + }, + titles2: { + marginTop: pTd(25), + }, + values2: { + marginTop: pTd(4), + }, + card: { + marginTop: pTd(24), + borderRadius: pTd(6), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border1, + width: pTd(335), + }, + section: { + ...GStyles.paddingArg(16, 12), + }, + marginTop16: { + marginTop: pTd(16), + }, + marginTop4: { + marginTop: pTd(4), + }, + marginTop0: { + marginTop: 0, + }, + marginLeft8: { + marginLeft: pTd(8), + }, + space: { + flex: 1, + }, + button: { + marginBottom: pTd(30), + }, + lightGrayFontColor: { + color: defaultColors.font3, + }, + blackFontColor: { + color: defaultColors.font5, + }, + fontBold: { + ...fonts.mediumFont, + }, + greenFontColor: { + color: defaultColors.font10, + }, + alignItemsCenter: { + alignItems: 'center', + }, + alignItemsEnd: { + alignItems: 'flex-end', + }, + leftTitle: { + width: pTd(120), + lineHeight: pTd(20), + }, }); const otherTransactionGroupStyle = StyleSheet.create({}); diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts new file mode 100644 index 0000000000..6336b42b84 --- /dev/null +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -0,0 +1,26 @@ +import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { + changeDrawerOpenStatus, + setActiveTab, + addRecordsItem, + createNewTab, +} from '@portkey-wallet/store/store-ca/discover/slice'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; + +export const useIsDrawerOpen = () => useAppCASelector(state => state.discover.isDrawerOpen); + +// discover jump +export const useDiscoverJumpWithNetWork = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const discoverJump = ({ item }: { item: ITabItem }) => { + dispatch(changeDrawerOpenStatus(true)); + dispatch(createNewTab({ ...item, networkType })); + dispatch(setActiveTab({ ...item, networkType })); + dispatch(addRecordsItem({ ...item, networkType })); + }; + + return discoverJump; +}; diff --git a/packages/mobile-app-did/js/hooks/useLogOut.ts b/packages/mobile-app-did/js/hooks/useLogOut.ts index a8a71a9a4f..6f7f9ef537 100644 --- a/packages/mobile-app-did/js/hooks/useLogOut.ts +++ b/packages/mobile-app-did/js/hooks/useLogOut.ts @@ -25,6 +25,7 @@ import { useGetChainInfo } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { ChainId } from '@portkey-wallet/types'; import { getWalletInfo, isCurrentCaHash } from 'utils/redux'; import { resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { resetDiscover } from '@portkey-wallet/store/store-ca/discover/slice'; export default function useLogOut() { const dispatch = useAppDispatch(); @@ -36,6 +37,8 @@ export default function useLogOut() { try { resetStore(); dispatch(resetDappList(currentNetwork)); + dispatch(resetDiscover(currentNetwork)); + if (otherNetworkLogged) { dispatch(resetCaInfo(currentNetwork)); navigationService.reset('LoginPortkey'); diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/GameData.ts b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/GameData.ts deleted file mode 100644 index c78ebe7c90..0000000000 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/GameData.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BingoGame } from '@portkey-wallet/constants/constants-ca/network'; -import { IGameListItemType } from '@portkey-wallet/types/types-ca/discover'; - -export const GamesList: IGameListItemType[] = [ - { - title: 'Bingo Game', - label: 'Bingo Game', - name: 'Bingo Game', - imgUrl: '@portkey-wallet/assets/images/bingo.png', - pngName: 'bingoGame', - introduction: 'An on-chain big and small betting game', - url: BingoGame, - }, -]; diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 7b138f144c..b3c3a8b4f8 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -1,37 +1,33 @@ import React, { useCallback } from 'react'; -import { StyleSheet, View, ScrollView } from 'react-native'; -import CommonInput from 'components/CommonInput'; +import { StyleSheet, View } from 'react-native'; import GStyles from 'assets/theme/GStyles'; -import { BGStyles } from 'assets/theme/styles'; -import PageContainer from 'components/PageContainer'; import navigationService from 'utils/navigationService'; import { useLanguage } from 'i18n/hooks'; -import GameSection from '../components/GameSection'; -import { GamesList } from './GameData'; import SimulatedInputBox from '../components/SimulatedInputBox'; +import { DiscoverCmsListSection } from '../components/DiscoverCmsListSection'; +import { defaultColors } from 'assets/theme'; +import SafeAreaBox from 'components/SafeAreaBox'; +import CustomHeader from 'components/CustomHeader'; +import { BGStyles } from 'assets/theme/styles'; export default function DiscoverHome() { - const { t } = useLanguage(); - const navigateToSearch = useCallback(() => navigationService.navigate('DiscoverSearch'), []); return ( - - - - + + + + + + + ); } const styles = StyleSheet.create({ container: { - paddingLeft: 0, - paddingRight: 0, + backgroundColor: defaultColors.bg4, + flex: 1, }, inputContainer: { ...GStyles.paddingArg(8, 20), diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 56cedf3597..efed9557d6 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import CommonInput from 'components/CommonInput'; import GStyles from 'assets/theme/GStyles'; @@ -12,36 +12,41 @@ import { useFocusEffect } from '@react-navigation/native'; import Svg from 'components/Svg'; import fonts from 'assets/theme/fonts'; import RecordSection from '../components/RecordSection'; -import GameSection from '../components/GameSection'; -import { GamesList } from '../DiscoverHome/GameData'; -import { IGameListItemType } from '@portkey-wallet/types/types-ca/discover'; -import { isValidUrl } from '@portkey-wallet/utils/reg'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { - addRecordsItem, - changeDrawerOpenStatus, - createNewTab, - setActiveTab, -} from '@portkey-wallet/store/store-ca/discover/slice'; +import SearchDiscoverSection from '../components/SearchDiscoverSection'; import { isIOS } from '@rneui/base'; -import { checkIsUrl, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; +import { checkIsUrl, getHost, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; +import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; let timer: any = null; export default function DiscoverSearch() { const { t } = useLanguage(); - const dispatch = useAppCommonDispatch(); + const discoverGroupList = useDiscoverGroupList(); + const jumpToWebview = useDiscoverJumpWithNetWork(); const iptRef = useRef(); const [value, setValue] = useState(''); const [showRecord, setShowRecord] = useState(true); - const [filterGameList] = useState(GamesList); + const [filteredDiscoverList, setFilteredDiscoverList] = useState([]); const navBack = useCallback(() => { navigationService.goBack(); }, []); + const flatList = useMemo((): DiscoverItem[] => { + const list = [] as DiscoverItem[]; + discoverGroupList.map(group => { + group?.items?.map(item => { + list.push(item); + }); + }); + + return list; + }, [discoverGroupList]); + const clearText = useCallback(() => setValue(''), []); useEffect(() => { @@ -50,27 +55,23 @@ export default function DiscoverSearch() { const onSearch = useCallback(() => { const newValue = value.trim().replace(' ', ''); - const id = Date.now(); - - dispatch(addRecordsItem({ id, title: newValue, url: prefixUrlWithProtocol(newValue) })); - dispatch(createNewTab({ id, url: prefixUrlWithProtocol(newValue) })); - dispatch(changeDrawerOpenStatus(true)); + if (!newValue) return; - return; - // if (checkIsUrl(newValue)) { - // dispatch(addRecordsItem({ title: newValue, url: prefixUrlWithProtocol(newValue) })); - // navigationService.navigate('ViewOnWebView', { - // url: prefixUrlWithProtocol(newValue), - // webViewPageType: 'discover', - // }); - // setShowRecord(true); - // } else { - // // else search in game list - // const filterList = GamesList.filter(item => item.name.replace(' ', '').includes(newValue)); - // setFilterGameList(filterList); - // setShowRecord(false); - // } - }, [dispatch, value]); + if (checkIsUrl(newValue)) { + jumpToWebview({ + item: { + id: Date.now(), + name: getHost(prefixUrlWithProtocol(newValue)), + url: prefixUrlWithProtocol(newValue), + }, + }); + } else { + // else search in Discover list + const filterList = flatList.filter(item => item.title.replace(' ', '').includes(newValue)); + setFilteredDiscoverList(filterList); + setShowRecord(false); + } + }, [flatList, jumpToWebview, value]); useFocusEffect( useCallback(() => { @@ -115,7 +116,7 @@ export default function DiscoverSearch() { {t('Cancel')}
    - {showRecord ? : } + {showRecord ? : }
    ); } diff --git a/packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx b/packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx deleted file mode 100644 index 599d5d8d93..0000000000 --- a/packages/mobile-app-did/js/pages/Discover/SwitchTabsContent/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { TextM } from 'components/CommonText'; -import Svg from 'components/Svg'; -import { useLanguage } from 'i18n/hooks'; -import React from 'react'; -import { ScrollView, TouchableOpacity, View, StyleSheet } from 'react-native'; -import { useDispatch } from 'react-redux'; -import { pTd } from 'utils/unit'; - -export const SwitchTabsContent: React.FC = props => { - const { t } = useLanguage(); - - const dispatch = useDispatch(); - - const closeAllTabs = () => { - dispatch; - }; - - return ( - - - hello - - - - {t('Close All')} - - - - - {t('done')} - - - ); -}; - -const styles = StyleSheet.create({ - container: {}, - handleActionWrap: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - }, - handleActionItem: { - flex: 1, - }, -}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx new file mode 100644 index 0000000000..70dff78030 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -0,0 +1,92 @@ +import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; + +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; +import { TextM, TextS } from 'components/CommonText'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import React, { useCallback } from 'react'; +import { ScrollView, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; +import { pTd } from 'utils/unit'; + +export function DiscoverCmsListSection() { + const GroupList = useDiscoverGroupList(); + const { s3Url } = useCurrentNetworkInfo(); + const discoverJump = useDiscoverJumpWithNetWork(); + + const onJump = useCallback( + (i: DiscoverItem) => { + discoverJump({ + item: { + id: Date.now(), + name: i.title, + url: i?.url ?? i?.description, + }, + }); + }, + [discoverJump], + ); + + return ( + + {GroupList.map((group, index) => ( + + {group.title} + + {group.items.map((item, i) => ( + onJump(item)}> + + + + {item.title} + + + {item.description} + + + + ))} + + + ))} + + ); +} + +const styles = StyleSheet.create({ + wrap: { + ...GStyles.paddingArg(8, 20), + flex: 1, + }, + groupTitle: { + marginBottom: pTd(8), + marginTop: pTd(16), + }, + itemsGroup: { + borderRadius: pTd(6), + backgroundColor: defaultColors.bg1, + overflow: 'hidden', + }, + itemWrap: { + backgroundColor: defaultColors.bg1, + display: 'flex', + flexDirection: 'row', + ...GStyles.paddingArg(12, 16), + width: '100%', + }, + image: { + width: pTd(40), + height: pTd(40), + marginRight: pTd(16), + borderRadius: pTd(20), + }, + right: { + flex: 1, + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordImage/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx similarity index 82% rename from packages/mobile-app-did/js/pages/Discover/components/RecordImage/index.tsx rename to packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx index ff998ae2f5..437bd1ae16 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordImage/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx @@ -5,14 +5,14 @@ import { StyleSheet } from 'react-native'; import { defaultColors } from 'assets/theme'; import { Image } from 'react-native'; -interface CommonAvatarProps { +interface DiscoverWebsiteImageProps { imageUrl?: string; size?: number; style?: any; } -export default function RecordImage(props: CommonAvatarProps) { - const { size = pTd(32), imageUrl } = props; +export default function DiscoverWebsiteImage(props: DiscoverWebsiteImageProps) { + const { size = pTd(32), imageUrl, style } = props; const sizeStyle = { width: size, @@ -22,7 +22,7 @@ export default function RecordImage(props: CommonAvatarProps) { const [isError, setError] = React.useState(false); - if (isError) return ; + if (isError) return ; return ( void; -} - -const GameItem: React.FC = props => { - const { item, onPress } = props; - - return ( - onPress?.()}> - - - - - {item?.label} - - - {item?.introduction} - - - - - ); -}; - -export default memo(GameItem); - -const itemStyle = StyleSheet.create({ - wrap: { - height: pTd(80), - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - right: { - height: pTd(80), - marginLeft: pTd(16), - paddingRight: pTd(16), - flex: 1, - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - borderBottomColor: defaultColors.border6, - borderBottomWidth: StyleSheet.hairlineWidth, - }, - infoWrap: { - flex: 1, - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'flex-start', - }, - gameName: { - lineHeight: pTd(22), - }, - gameInfo: { - lineHeight: pTd(16), - marginTop: pTd(2), - }, -}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/GameSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/GameSection/index.tsx deleted file mode 100644 index ce7219f443..0000000000 --- a/packages/mobile-app-did/js/pages/Discover/components/GameSection/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { useCallback } from 'react'; -import { StyleSheet, View, ScrollView } from 'react-native'; -import GStyles from 'assets/theme/GStyles'; -import { useLanguage } from 'i18n/hooks'; -import GameItem from '../GameItem'; -import { TextL } from 'components/CommonText'; -import { pTd } from 'utils/unit'; -import fonts from 'assets/theme/fonts'; -import NoData from 'components/NoData'; -import { IGameListItemType } from '@portkey-wallet/types/types-ca/discover'; -import { addRecordsItem } from '@portkey-wallet/store/store-ca/discover/slice'; -import navigationService from 'utils/navigationService'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; - -interface GameSectionPropsType { - data: IGameListItemType[]; -} - -export default function GameSection(props: GameSectionPropsType) { - const { t } = useLanguage(); - const { data } = props; - - const dispatch = useAppCommonDispatch(); - - const navigateToSearch = useCallback( - (item: { url: string; title: string }) => { - dispatch(addRecordsItem({ ...item })); - return navigationService.navigate('ViewOnWebView', { - url: item.url, - title: item?.title, - webViewPageType: 'discover', - }); - }, - [dispatch], - ); - - if (data.length === 0) return ; - - return ( - - - {t('Games')} - - {data?.map((item, index) => ( - navigateToSearch(item)} /> - ))} - - ); -} - -const styles = StyleSheet.create({ - sectionWrap: { - ...GStyles.paddingArg(24, 20), - }, - headerWrap: { - height: pTd(22), - }, - header: { - ...fonts.mediumFont, - lineHeight: pTd(24), - }, - cancelButton: { - paddingLeft: pTd(12), - lineHeight: pTd(36), - }, -}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx index fe576918f2..55e19bb172 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx @@ -5,8 +5,8 @@ import React, { memo } from 'react'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { pTd } from 'utils/unit'; import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; -import { getFaviconUrl } from 'utils'; -import RecordImage from '../RecordImage'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; interface TokenListItemType { item: IRecordsItemType; onPress?: () => void; @@ -15,11 +15,9 @@ interface TokenListItemType { const RecordItem: React.FC = props => { const { item, onPress } = props; - console.log('getFaviconUrlgetFaviconUrlgetFaviconUrl', getFaviconUrl(item?.url || '')); - return ( onPress?.()}> - + diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx index ff75dab8bf..04608b4e69 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx @@ -15,24 +15,26 @@ import { clearRecordsList, createNewTab, } from '@portkey-wallet/store/store-ca/discover/slice'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; export default function RecordSection() { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); - const { recordsList } = useAppCASelector(state => state.discover); + const { networkType } = useCurrentNetworkInfo(); + const { discoverMap } = useAppCASelector(state => state.discover); const clearRecord = useCallback(() => { - dispatch(clearRecordsList()); - }, [dispatch]); + dispatch(clearRecordsList({ networkType })); + }, [dispatch, networkType]); const showRecordList = useMemo(() => { - return [...recordsList].reverse(); - }, [recordsList]); + const recordsList = (JSON.parse(JSON.stringify(discoverMap?.[networkType]?.recordsList)) as ITabItem[]) || []; + return recordsList.reverse(); + }, [discoverMap, networkType]); - if (recordsList?.length === 0) return null; - - console.log('showRecordList', showRecordList); + if (showRecordList?.length === 0) return null; return ( @@ -48,8 +50,8 @@ export default function RecordSection() { item={item} onPress={() => { const id = Date.now(); - dispatch(addRecordsItem(item)); - dispatch(createNewTab({ id, url: item.url })); + dispatch(addRecordsItem({ ...item, networkType })); + dispatch(createNewTab({ id, url: item.url, name: '', networkType })); dispatch(changeDrawerOpenStatus(true)); }} /> diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx new file mode 100644 index 0000000000..794102259c --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { StyleSheet, View, ScrollView, TouchableOpacity } from 'react-native'; +import GStyles from 'assets/theme/GStyles'; +import { useLanguage } from 'i18n/hooks'; +import { TextL, TextS } from 'components/CommonText'; +import { pTd } from 'utils/unit'; +import fonts from 'assets/theme/fonts'; +import NoData from 'components/NoData'; +import { FontStyles } from 'assets/theme/styles'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { defaultColors } from 'assets/theme'; +import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; +import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +interface SearchDiscoverSectionProps { + searchedDiscoverList: DiscoverItem[]; +} + +export default function SearchDiscoverSection(props: SearchDiscoverSectionProps) { + const { t } = useLanguage(); + const { searchedDiscoverList } = props; + const { s3Url } = useCurrentNetworkInfo(); + const jumpToWebview = useDiscoverJumpWithNetWork(); + + const onJump = (i: DiscoverItem) => { + jumpToWebview({ + item: { + id: Date.now(), + name: i.title, + url: i?.url ?? i?.description, + }, + }); + }; + + if (searchedDiscoverList.length === 0) return ; + + return ( + + {searchedDiscoverList?.map((item, index) => ( + onJump(item)}> + + + + + {item?.title} + + + {item?.description} + + + + + ))} + + ); +} + +const styles = StyleSheet.create({ + sectionWrap: { + ...GStyles.paddingArg(0, 20), + }, + headerWrap: { + height: pTd(22), + }, + header: { + ...fonts.mediumFont, + lineHeight: pTd(24), + }, + cancelButton: { + paddingLeft: pTd(12), + lineHeight: pTd(36), + }, +}); + +const itemStyle = StyleSheet.create({ + wrap: { + height: pTd(80), + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + right: { + height: pTd(80), + marginLeft: pTd(16), + paddingRight: pTd(16), + flex: 1, + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + borderBottomColor: defaultColors.border6, + borderBottomWidth: StyleSheet.hairlineWidth, + }, + image: { + width: pTd(32), + height: pTd(32), + borderRadius: pTd(16), + }, + infoWrap: { + flex: 1, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'flex-start', + }, + gameName: { + lineHeight: pTd(22), + }, + gameInfo: { + lineHeight: pTd(16), + marginTop: pTd(2), + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx deleted file mode 100644 index a7e5037d90..0000000000 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappItem/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { memo } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { defaultColors } from 'assets/theme'; -import { TextL, TextM, TextS } from 'components/CommonText'; -import Touchable from 'components/Touchable'; -import { pTd } from 'utils/unit'; -import { FontStyles } from 'assets/theme/styles'; -import { formatTransferTime } from 'utils'; -import { DeviceItemType, DeviceType } from '@portkey-wallet/types/types-ca/device'; -import Svg, { IconName } from 'components/Svg'; - -const deviceTypeIconMap: Record = { - [DeviceType.IOS]: 'phone-iOS', - [DeviceType.ANDROID]: 'phone-Android', - [DeviceType.MAC]: 'desk-mac', - [DeviceType.WINDOWS]: 'desk-win', - [DeviceType.OTHER]: 'desk-win', -}; - -interface DeviceItemProps { - onPress?: (e: any) => void; - isCurrent?: boolean; - deviceItem: DeviceItemType; - isShowArrow?: boolean; -} - -const DeviceItemRender = ({ onPress, isCurrent, deviceItem, isShowArrow = true }: DeviceItemProps) => { - return ( - - - - - - - - - {deviceItem.deviceInfo.deviceName} - {isCurrent && ( - - Current - - )} - - - {deviceItem.transactionTime ? formatTransferTime(deviceItem.transactionTime) : ''} - - - - {isShowArrow && } - - - ); -}; -const DeviceItem = memo(DeviceItemRender); - -export default DeviceItem; - -const styles = StyleSheet.create({ - deviceItemWrap: { - height: pTd(72), - paddingHorizontal: pTd(16), - marginBottom: pTd(24), - backgroundColor: defaultColors.bg1, - borderRadius: pTd(6), - flexDirection: 'row', - alignItems: 'center', - }, - leftWrap: { - marginRight: pTd(12), - paddingTop: pTd(17), - height: '100%', - }, - deviceItemInfoWrap: { - flex: 1, - height: '100%', - paddingVertical: pTd(14), - justifyContent: 'space-between', - }, - deviceItemInfo: { - flexDirection: 'row', - alignItems: 'center', - }, - currentWrap: { - marginLeft: pTd(12), - height: pTd(20), - borderWidth: StyleSheet.hairlineWidth, - borderColor: defaultColors.border6, - borderRadius: pTd(10), - paddingHorizontal: pTd(9), - justifyContent: 'center', - }, -}); diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx index 32a74a5e90..28e4943db4 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import PageContainer from 'components/PageContainer'; -import { StyleSheet, View, Image } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { TextL, TextM, TextS } from 'components/CommonText'; @@ -17,7 +17,8 @@ import Touchable from 'components/Touchable'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import fonts from 'assets/theme/fonts'; import NoData from 'components/NoData'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; const DeviceList: React.FC = () => { const { refetch } = useDeviceList(); @@ -43,18 +44,13 @@ const DeviceList: React.FC = () => { scrollViewProps={{ disabled: false }}> {dappList?.map(item => ( - { - test('recordsList will add a new record item', () => { - const payload = { - url: 'https://www.google.com', - title: 'Google', - }; - const res = reducer(initialState, addRecordsItem(payload)); - expect(res.recordsList).toHaveLength(1); - }); - test('recordsList will be adjust the order', () => { - const state = { - ...initialState, - recordsList: [ - { - url: 'https://www.google.com', - title: 'Google', - }, - { - url: 'https://www.apple.com', - title: 'Apple', - }, - ], - }; - const payload = { - url: 'https://www.google.com', - title: 'Google', - }; - const res = reducer(state, addRecordsItem(payload)); - expect(res.recordsList).toHaveLength(2); - expect(res.recordsList[0].title).toBe('Apple'); - }); -}); - -describe('upDateRecordsItem', () => { - const state = { - ...initialState, - recordsList: [ - { - url: 'https://www.google.com', - title: 'Google', - }, - { - url: 'https://www.apple.com', - title: 'Apple', - }, - ], - }; - test('the record item exist, recordsList will update the record item', () => { - const payload = { - url: 'https://www.google.com', - title: 'newGoogleName', - }; - const res = reducer(state, upDateRecordsItem(payload)); - expect(res.recordsList).toHaveLength(2); - expect(res.recordsList[0].title).toBe('newGoogleName'); - }); - test('the record item does not exist, recordsList will update the record item', () => { - const payload = { - url: 'https://www.google1.com', - title: 'Google', - }; - const res = reducer(state, upDateRecordsItem(payload)); - expect(res.recordsList).toHaveLength(2); - expect(res.recordsList[0].title).toBe('Google'); - }); -}); - -describe('clearRecordsList', () => { - test('recordsList will be empty array', () => { - const state = { - ...initialState, - recordsList: [ - { - url: 'https://www.google.com', - title: 'Google', - }, - { - url: 'https://www.apple.com', - title: 'Apple', - }, - ], - tabs: [ - { - tab: 'tab1', - }, - ], - }; - const res = reducer(state, clearRecordsList()); - expect(res.recordsList).toHaveLength(0); - expect(res.tabs).toHaveLength(1); - }); -}); - -describe('resetDiscover', () => { - test('state will be initialState', () => { - const state = { - ...initialState, - recordsList: [ - { - url: 'https://www.google.com', - title: 'Google', - }, - { - url: 'https://www.apple.com', - title: 'Apple', - }, - ], - }; - const res = reducer(state, resetDiscover()); - expect(res).toEqual(initialState); - }); -}); diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index b0d56a204c..3fc4f4e07b 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -1,68 +1,94 @@ import { createSlice } from '@reduxjs/toolkit'; -import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; -import { IDiscoverStateType } from './type'; +import { IDiscoverStateType, IDiscoverNetworkStateType, ITabItem } from './type'; +import { NetworkType } from '@portkey-wallet/types'; -const initialState: IDiscoverStateType = { - isDrawerOpen: false, +const initNetworkData: IDiscoverNetworkStateType = { recordsList: [], whiteList: [], activeTabId: undefined, tabs: [], }; +const initialState: IDiscoverStateType = { + isDrawerOpen: false, + discoverMap: {}, +}; + //it automatically uses the immer library to let you write simpler immutable updates with normal mutative code export const discoverSlice = createSlice({ name: 'discover', initialState, reducers: { - addRecordsItem: (state, { payload }: { payload: IRecordsItemType }) => { - const targetItem = state.recordsList.find(item => item.url === payload.url); + addRecordsItem: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { + const { networkType, url } = payload; + if (!state.discoverMap?.[networkType]) state.discoverMap[networkType] = initNetworkData; + + const targetItem = state.discoverMap?.[networkType]?.recordsList.find(item => item.url === url); + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); if (targetItem) { - const arr = state.recordsList.filter(item => item.url !== payload.url); + const arr = state.discoverMap?.[networkType]?.recordsList.filter(item => item.url !== url) || []; arr.push(targetItem); - state.recordsList = arr; + targetNetworkDiscover.recordsList = arr; } else { - state.recordsList.push({ ...payload }); + targetNetworkDiscover.recordsList.push({ ...payload }); } }, - upDateRecordsItem: (state, { payload }: { payload: IRecordsItemType }) => { - console.log('store', [ - ...state.recordsList.map(item => { - return item.url === payload.url ? { ...item, ...payload } : item; - }), - ]); + upDateRecordsItem: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { + const { networkType } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - state.recordsList = state.recordsList.map(item => { + targetNetworkDiscover.recordsList = targetNetworkDiscover?.recordsList.map(item => { return item.url === payload.url ? { ...item, ...payload } : item; }); }, - clearRecordsList: state => { - state.recordsList = []; + clearRecordsList: (state, { payload }: { payload: { networkType: NetworkType } }) => { + const { networkType } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + + targetNetworkDiscover.recordsList = []; }, - closeAllTabs: state => { - state.tabs = []; - state.activeTabId = undefined; + closeAllTabs: (state, { payload }: { payload: { networkType: NetworkType } }) => { + const { networkType } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + + targetNetworkDiscover.tabs = []; + targetNetworkDiscover.activeTabId = undefined; }, - createNewTab: (state, { payload }) => { - state.activeTabId = payload.id; - state.tabs.push(payload); - console.log('createNewTab', payload); + createNewTab: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { + const { networkType, id } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + if (!targetNetworkDiscover?.tabs) targetNetworkDiscover.tabs = []; + + targetNetworkDiscover.activeTabId = id; + targetNetworkDiscover.tabs.push({ ...payload }); }, - closeExistingTab: (state, { payload }) => { - state.tabs = state.tabs.filter(item => item.id !== payload); + closeExistingTab: (state, { payload }: { payload: { id: number; networkType: NetworkType } }) => { + const { networkType, id } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + + targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.filter(item => item.id !== id); }, - setActiveTab: (state, { payload }) => { - state.activeTabId = payload; + setActiveTab: (state, { payload }: { payload: { id: number | undefined; networkType: NetworkType } }) => { + const { networkType, id } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + + targetNetworkDiscover.activeTabId = id; }, - updateTab: (state, { payload }) => { - state.tabs = state.tabs.map(item => (item.id === payload.id ? { ...item, ...payload } : item)); + updateTab: (state, { payload }: { payload: { networkType: NetworkType; id: number; [key: string]: any } }) => { + const { networkType, id } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + + targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.map(item => + item.id === id ? { ...item, ...payload } : item, + ); }, changeDrawerOpenStatus: (state, { payload }) => { - console.log('payload', payload); state.isDrawerOpen = payload; }, - resetDiscover: () => initialState, + resetDiscover: (state, { payload }: { payload: NetworkType }) => { + state.discoverMap[payload] = initNetworkData; + }, }, }); diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index 63bea4c565..8c6ecef37c 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -1,15 +1,22 @@ -import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; +import { NetworkType } from '@portkey-wallet/types'; export interface ITabItem { - id: string | number; + id: number; name: string; url: string; - screenShotUrl: string; + screenShotUrl?: string; } -export interface IDiscoverStateType { - isDrawerOpen: boolean; - recordsList: IRecordsItemType[]; + +export interface IDiscoverNetworkStateType { + recordsList: ITabItem[]; whiteList: any[]; activeTabId?: number; tabs: ITabItem[]; } + +export interface IDiscoverStateType { + isDrawerOpen: boolean; + discoverMap: { + [key in NetworkType]?: IDiscoverNetworkStateType; + }; +} From ee320f2fe02365f623fd5c29ef317d9c0bf2e96b Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 12 Jun 2023 10:26:24 +0800 Subject: [PATCH 070/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20promise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/dappMobileOperator.ts | 6 +++-- packages/utils/dapp/dappManager.ts | 25 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 3b2d3961bb..6b1297ea27 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -147,8 +147,10 @@ export default class DappMobileOperator extends Operator { if (!params || !params.params || !params.method || !params.contractAddress || !params.chainId) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); - const chainInfo = await this.dappManager.getChainInfo(params.chainId); - const caInfo = await this.dappManager.getCaInfo(params.chainId); + const [chainInfo, caInfo] = await Promise.all([ + this.dappManager.getChainInfo(params.chainId), + this.dappManager.getCaInfo(params.chainId), + ]); if (!chainInfo?.endPoint || !caInfo?.caHash) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid chain id' }); diff --git a/packages/utils/dapp/dappManager.ts b/packages/utils/dapp/dappManager.ts index a5c1696561..bf31884ac4 100644 --- a/packages/utils/dapp/dappManager.ts +++ b/packages/utils/dapp/dappManager.ts @@ -61,24 +61,37 @@ export abstract class DappManager this.store.dispatch(addDapp({ networkType: currentNetwork, dapp: dapp })); } async updateDapp(dapp: DappStoreItem): Promise { - const { currentNetwork } = await this.getWallet(); - const originInfo = await this.getOriginInfo(dapp.origin); + const [{ currentNetwork }, originInfo] = await Promise.all([this.getWallet(), this.getOriginInfo(dapp.origin)]); if (isEqDapp(dapp, originInfo)) return; this.store.dispatch(updateDapp({ origin: dapp.origin, networkType: currentNetwork, dapp: dapp })); } + + async getOriginChainId() { + const [{ originChainId: walletOriginChainId }, currentCAInfo] = await Promise.all([ + this.getWallet(), + this.getCurrentCAInfo(), + ]); + return currentCAInfo?.originChainId || walletOriginChainId || DefaultChainId; + } + async isLogged(): Promise { - const { walletInfo, currentNetwork, originChainId: walletOriginChainId } = await this.getWallet(); - const originChainId = (await this.getCurrentCAInfo())?.originChainId || walletOriginChainId || DefaultChainId; + const [{ walletInfo, currentNetwork }, originChainId] = await Promise.all([ + this.getWallet(), + this.getOriginChainId(), + ]); return !!(originChainId && walletInfo?.caInfo[currentNetwork]?.managerInfo); } + async isActive(origin: string) { return (await this.originIsAuthorized(origin)) && (await this.isLogged()); } + async accounts(origin: string) { - const wallet = await this.getWallet(); - if (!(await this.isActive(origin)) || !wallet.walletInfo?.caInfo) return {}; + const [wallet, active] = await Promise.all([this.getWallet(), this.isActive(origin)]); + if (!active || !wallet.walletInfo?.caInfo) return {}; return handleAccounts(wallet); } + async chainId() { return this.chainIds(); } From be1868c11ea57ffed29a37d1d103d6122108c005 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 11:27:44 +0800 Subject: [PATCH 071/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20div=20to=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/TransactionOverlay/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 178e9ae24a..f427de5eb3 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -173,6 +173,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { const otherTransactionContent = useMemo(() => { const data = transactionInfo.params?.paramsOption || {}; + console.log('================ transactionInfo.params?.paramsOption===================='); + return ( @@ -220,8 +222,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { Card {Object.keys(data).map(item => ( <> -
    {item}
    -
    {data[item]}
    + {item} + {data[item]} ))}
    From d063f04c9f92cc3092796dbc54b9ca5992503bab Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Mon, 12 Jun 2023 12:03:44 +0800 Subject: [PATCH 072/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20check=20rpcUrl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/SendTransactions/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 14a8f8c8a9..3adafc3689 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -17,6 +17,7 @@ import { closePrompt } from 'utils/lib/serviceWorkerAction'; import { callSendMethod } from 'utils/sandboxUtil/sendTransactions'; import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import getTransferFee from './utils/getTransferFee'; +import { ResponseCode } from '@portkey/provider-types'; import './index.less'; export default function SendTransactions() { @@ -180,9 +181,14 @@ export default function SendTransactions() { const sendHandler = useCallback(async () => { try { if (!chainInfo?.endPoint || !wallet?.caHash) { - closePrompt({ ...errorHandler(400001), data: { code: 4002, msg: 'invalid chain id' } }); + closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid chain id' } }); return; } + if (chainInfo?.endPoint !== payload?.params?.rpcUrl) { + closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid rpcUrl' } }); + return; + } + let paramsOption = payload?.params?.paramsOption; const functionName = isCAContract ? payload?.method : 'ManagerForwardCall'; From 8311243b283db0a95a036ff6c7c55ed07e736bf7 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 14:22:41 +0800 Subject: [PATCH 073/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20app=20ve?= =?UTF-8?q?rsion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/android/app/build.gradle | 2 +- packages/mobile-app-did/ios/Portkey/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/android/app/build.gradle b/packages/mobile-app-did/android/app/build.gradle index 1f1366cf3b..bdd1f51266 100644 --- a/packages/mobile-app-did/android/app/build.gradle +++ b/packages/mobile-app-did/android/app/build.gradle @@ -168,7 +168,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 231 - versionName "1.2.8" + versionName "1.3.0" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/packages/mobile-app-did/ios/Portkey/Info.plist b/packages/mobile-app-did/ios/Portkey/Info.plist index 28ec9904d5..297fbb59e5 100644 --- a/packages/mobile-app-did/ios/Portkey/Info.plist +++ b/packages/mobile-app-did/ios/Portkey/Info.plist @@ -32,7 +32,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.2.8 + 1.3.0 CFBundleSignature ???? CFBundleVersion From 0f51108c0a7c55e59f8de2b73ea7df4c91f46458 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 12 Jun 2023 15:25:56 +0800 Subject: [PATCH 074/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20modify=20sell=20?= =?UTF-8?q?limit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Buy/components/BuyFrom/index.tsx | 4 ++-- .../pages/Buy/components/SellFrom/index.tsx | 8 +++---- .../app/web/pages/Buy/const.ts | 10 --------- .../app/web/pages/Buy/index.tsx | 22 ++++++++----------- 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx index cc57cf36c7..479f9ee257 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx @@ -40,7 +40,7 @@ export default function BuyFrom({
    {`I want to pay`}
    handleCurrencyChange(val)} + onChange={handleCurrencyChange} readOnly={false} onKeyDown={handleCurrencyKeyDown} curFiat={curFiat} @@ -53,7 +53,7 @@ export default function BuyFrom({ handleTokenChange(val)} + onChange={handleTokenChange} readOnly={true} onKeyDown={handleTokenKeyDown} curToken={curToken} diff --git a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx index fde787c0da..00d4104ef3 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx @@ -33,11 +33,11 @@ export default function SellFrom({
    {`I want to sell`}
    tokenChange(val)} + onChange={tokenChange} readOnly={false} onKeyDown={handleTokenKeyDown} curToken={curToken} - onSelect={(v) => handleTokenSelect(v)} + onSelect={handleTokenSelect} /> {!!errMsg &&
    {errMsg}
    }
    @@ -45,11 +45,11 @@ export default function SellFrom({
    {`I will receive≈`}
    handleCurrencyChange(val)} + onChange={handleCurrencyChange} readOnly={true} onKeyDown={handleCurrencyKeyDown} curFiat={curFiat} - onSelect={(v) => handleCurrencySelect(v)} + onSelect={handleCurrencySelect} />
    diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index 6883cd7a0e..87c4d91822 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -32,16 +32,6 @@ export const initFiat: PartialFiatType = { currency: 'USD', }; -export type Limit = { - min: number | null; - max: number | null; -}; - -export const initLimit = { - min: null, - max: null, -}; - export const MAX_UPDATE_TIME = 15; export const initCurrency = '200'; export const initCrypto = '300'; diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index c7ecf1b1ff..45f2f4d316 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -12,10 +12,8 @@ import { initCrypto, initCurrency, initFiat, - initLimit, initToken, initValueSave, - Limit, MAX_UPDATE_TIME, PartialFiatType, } from './const'; @@ -50,7 +48,6 @@ export default function Buy() { const [amount, setAmount] = useState(initCurrency); const [receive, setReceive] = useState(''); const [curToken, setCurToken] = useState(initToken); - const [limit, setLimit] = useState(initLimit); const { setLoading } = useLoading(); const [curFiat, setCurFiat] = useState(initFiat); const [rateUpdateTime, setRateUpdateTime] = useState(MAX_UPDATE_TIME); @@ -196,13 +193,11 @@ export default function Buy() { const data = await getCryptoInfo({ fiat }, crypto, network, side); if (side === PaymentTypeEnum.BUY) { if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { - setLimit({ max: data.maxPurchaseAmount, min: data.minPurchaseAmount }); valueSaveRef.current.max = data.maxPurchaseAmount; valueSaveRef.current.min = data.minPurchaseAmount; } } else { if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { - setLimit({ max: data.maxSellAmount, min: data.minSellAmount }); valueSaveRef.current.max = data.maxSellAmount; valueSaveRef.current.min = data.minSellAmount; } @@ -215,7 +210,7 @@ export default function Buy() { (v: string) => { setAmount(v); valueSaveRef.current.amount = v; - const { min, max } = limit; + const { min, max } = valueSaveRef.current; if (max !== null && min !== null) { if (!isValidValue({ amount: v, min, max })) { setErrMsgCase(); @@ -237,7 +232,7 @@ export default function Buy() { side, }); }, - [isValidValue, limit, setErrMsgCase, updateReceive], + [isValidValue, setErrMsgCase, updateReceive], ); const getQuoteAndSetData = useCallback(async () => { @@ -312,7 +307,6 @@ export default function Buy() { const data = await getCryptoInfo({ fiat: v.currency }, crypto, network, side); if (side === PaymentTypeEnum.BUY) { if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { - setLimit({ max: data.maxPurchaseAmount, min: data.minPurchaseAmount }); valueSaveRef.current.max = data.maxPurchaseAmount; valueSaveRef.current.min = data.minPurchaseAmount; @@ -332,7 +326,6 @@ export default function Buy() { } } else { if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { - setLimit({ max: data.maxSellAmount, min: data.minSellAmount }); valueSaveRef.current.max = data.maxSellAmount; valueSaveRef.current.min = data.minSellAmount; setErrMsgCase(); @@ -354,6 +347,9 @@ export default function Buy() { } } catch (error) { console.log('error', error); + setErrMsg(''); + setReceive(''); + setRate(''); } finally { setLoading(false); } @@ -452,12 +448,12 @@ export default function Buy() { {page === PaymentTypeEnum.BUY && ( handleInputChange(val)} + handleCurrencyChange={handleInputChange} handleCurrencyKeyDown={handleKeyDown} handleCurrencySelect={(v) => handleSelect(v, DrawerType.currency)} curFiat={curFiat} tokenVal={receive} - handleTokenChange={(val) => handleInputChange(val)} + handleTokenChange={handleInputChange} handleTokenKeyDown={handleKeyDown} handleTokenSelect={(v) => handleSelect(v, DrawerType.token)} curToken={curToken} @@ -467,12 +463,12 @@ export default function Buy() { {page === PaymentTypeEnum.SELL && ( handleInputChange(val)} + handleTokenChange={handleInputChange} handleTokenKeyDown={handleKeyDown} handleTokenSelect={(v) => handleSelect(v, DrawerType.token)} curToken={curToken} currencyVal={receive} - handleCurrencyChange={(val) => handleInputChange(val)} + handleCurrencyChange={handleInputChange} handleCurrencyKeyDown={handleKeyDown} handleCurrencySelect={(v) => handleSelect(v, DrawerType.currency)} curFiat={curFiat} From d79ff606f8dfc99531a87e3e0eb864a5b7506975 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Mon, 12 Jun 2023 16:04:32 +0800 Subject: [PATCH 075/893] =?UTF-8?q?style:=20=F0=9F=92=84=20UI=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/ConnectWallet/index.tsx | 2 +- .../app/web/pages/SendTransactions/index.less | 62 +++++------- .../app/web/pages/SendTransactions/index.tsx | 96 +++++++++++-------- 3 files changed, 82 insertions(+), 78 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index 4ea23a644b..4b4b0da64b 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -11,7 +11,7 @@ import { closePrompt } from 'utils/lib/serviceWorkerAction'; import './index.less'; const allowItem = [ - 'viewing wallet balance and activity Allow', + 'viewing wallet balance and activity', 'sending requests for transactions', 'moving funds without your permission', ]; diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.less b/packages/web-extension-did/app/web/pages/SendTransactions/index.less index 9ed45980f9..4deea15d84 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.less +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.less @@ -6,19 +6,19 @@ flex-direction: column; font-size: 12px; line-height: 16px; + color: @font-13; + .value { + font-size: 14px; + line-height: 20px; + font-weight: 500; color: @font-11; - .title { - font-size: 14px; - line-height: 20px; - font-weight: 500; - color: @font-11; } .chain { align-self: center; - margin-bottom: 24px; padding: 8px 16px; border-radius: 24px; border: 1px solid @border-2; + font-weight: 500; .aelf-icon, .elf-icon-icon { width: 24px; height: 24px; @@ -31,13 +31,14 @@ } .account { position: relative; - margin: 0 auto 16px; + margin: 24px auto 0; padding: 17px 15px; width: 328px; border: 1px solid @border-2; border-radius: 6px; align-items: center; gap: 44px; + color: @font-1; .address, .name { flex: 1; @@ -61,54 +62,46 @@ border-left: 1px solid @border-2; } } + .amount { + .amount-number { + margin-top: 4px; + } + } .method { - margin-bottom: 16px; + margin-top: 16px; color: @font-13; - .title { - margin-bottom: 4px; + .method-name { + margin-top: 4px; } } .detail { + margin-top: 16px; color: @font-13; .title { margin-bottom: 4px; } .amount { padding: 16px; - margin-bottom: 18px; border: 1px solid @border-2; border-radius: 6px; } .fee, .total { - .fee-amount, .total-amount { - text-align: right; - } - .elf { - margin-left: 4px; - font-size: 14px; - line-height: 20px; - font-weight: 500; - color: @font-11; - white-space: nowrap; + margin-top: 16px; + .amount-show { + margin-top: 4px; } } } .message-wrapper { - color: @font-13; - .title { - margin-bottom: 4px; - } + margin-top: 16px; .message { width: 328px; padding: 16px; - margin-bottom: 18px; + margin-top: 4px; border: 1px solid @border-2; border-radius: 6px; - .title { - margin-bottom: 4px; - } .content { - margin-bottom: 16px; + margin: 4px 0 16px; width: 100%; line-break: anywhere; white-space: pre-wrap; @@ -118,14 +111,9 @@ } } .fee { + margin-top: 16px; .fee-amount { - text-align: right; - .elf { - font-size: 14px; - line-height: 20px; - font-weight: 500; - color: @font-11; - } + margin-top: 4px; } } } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 3adafc3689..e1c3495667 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -46,6 +46,18 @@ export default function SendTransactions() { [passwordSeed, wallet.AESEncryptPrivateKey], ); + const formatAmountInUsdShow = useCallback( + (amount: string | number, decimals: string | number, symbol: string) => { + const value = amountInUsdShow(amount, decimals, symbol); + if (symbol === 'ELF') { + return value === '$ 0' ? '<$ 0.01' : value; + } else { + return value; + } + }, + [amountInUsdShow], + ); + const getFee = useCallback(async () => { if (!privateKey) return; if (!chainInfo?.endPoint || !wallet?.caHash || !chainInfo.caContractAddress) return; @@ -112,71 +124,75 @@ export default function SendTransactions() { return (
    -
    Details
    +
    Details
    -
    Amount
    -
    -
    {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`}
    - {isMainnet &&
    {amountInUsdShow(amount, decimals, symbol)}
    } +
    Amount
    +
    +
    {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`}
    + {isMainnet &&
    {formatAmountInUsdShow(amount, decimals, symbol)}
    }
    -
    +
    Transaction Fee
    -
    -
    {`${formatAmountShow(fee, 8)} ELF`}
    - {isMainnet &&
    {amountInUsdShow(fee, 0, 'ELF')}
    } +
    +
    {`${formatAmountShow(fee, 8)} ELF`}
    + {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    -
    +
    Total (Amount + Transaction Fee)
    -
    - {symbol === 'ELF' ? ( - <> -
    {`${formatAmountShow( - ZERO.plus(divDecimals(amount, decimals)).plus(fee), - 8, - )} ${symbol}`}
    - {isMainnet && ( -
    {amountInUsdShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee).toNumber(), 0, symbol)}
    - )} - - ) : ( - <> -
    {`${formatAmountShow(fee, 8)} ELF`}
    - {isMainnet &&
    {amountInUsdShow(fee, 0, 'ELF')}
    } -
    {`${formatAmountShow(amount)} ${symbol}`}
    - {isMainnet &&
    {amountInUsdShow(amount, 0, symbol)}
    } - - )} -
    + {symbol === 'ELF' ? ( +
    +
    {`${formatAmountShow( + ZERO.plus(divDecimals(amount, decimals)).plus(fee), + 8, + )} ${symbol}`}
    + {isMainnet && ( +
    + {formatAmountInUsdShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee).toNumber(), 0, symbol)} +
    + )} +
    + ) : ( + <> +
    +
    {`${formatAmountShow(fee, 8)} ELF`}
    + {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    } +
    +
    +
    {`${formatAmountShow(amount)} ${symbol}`}
    + {isMainnet &&
    {formatAmountInUsdShow(amount, 0, symbol)}
    } +
    + + )}
    ); - }, [amountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); + }, [formatAmountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); const renderMessage = useMemo(() => { const params = payload?.params?.paramsOption || {}; return (
    -
    Message
    +
    Message
    {Object.keys(params).map((item) => ( <> -
    {item}
    +
    {item}
    {params[item]}
    ))}
    -
    +
    Transaction Fee
    -
    -
    {`${formatAmountShow(fee)} ELF`}
    - {isMainnet &&
    {amountInUsdShow(fee, 0, 'ELF')}
    } +
    +
    {`${formatAmountShow(fee)} ELF`}
    + {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    ); - }, [amountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); + }, [formatAmountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); const sendHandler = useCallback(async () => { try { @@ -229,8 +245,8 @@ export default function SendTransactions() {
    {renderAccountInfo}
    -
    Method
    -
    {payload?.method}
    +
    Method
    +
    {payload?.method}
    {payload?.method.toLowerCase() === 'transfer' ? renderDetail : renderMessage} {errMsg &&
    {errMsg}
    } From e92341c05749ffae6b6f80efe9dcdb824241034f Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 12 Jun 2023 16:58:55 +0800 Subject: [PATCH 076/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20payment?= =?UTF-8?q?=20limit=20show?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Buy/constants.ts | 2 +- packages/mobile-app-did/js/pages/Buy/hooks.tsx | 5 ++++- packages/utils/converter.ts | 8 ++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Buy/constants.ts b/packages/mobile-app-did/js/pages/Buy/constants.ts index 0d538a67f2..3a29d37bf7 100644 --- a/packages/mobile-app-did/js/pages/Buy/constants.ts +++ b/packages/mobile-app-did/js/pages/Buy/constants.ts @@ -10,4 +10,4 @@ export const tokenList: CryptoItemType[] = [ export const MAX_REFRESH_TIME = 15; export const INIT_BUY_AMOUNT = '200'; -export const INIT_SELL_AMOUNT = '300'; +export const INIT_SELL_AMOUNT = '400'; diff --git a/packages/mobile-app-did/js/pages/Buy/hooks.tsx b/packages/mobile-app-did/js/pages/Buy/hooks.tsx index 3b4eee8147..211b18fbb9 100644 --- a/packages/mobile-app-did/js/pages/Buy/hooks.tsx +++ b/packages/mobile-app-did/js/pages/Buy/hooks.tsx @@ -10,6 +10,7 @@ import { ErrorType } from 'types/common'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import isEqual from 'lodash/isEqual'; import { ZERO } from '@portkey-wallet/constants/misc'; +import BigNumber from 'bignumber.js'; export const useReceive = ( type: TypeEnum, @@ -89,7 +90,7 @@ export const useReceive = ( if (amountNum < min || amountNum > max) { setAmountError({ ...INIT_HAS_ERROR, - errorMsg: `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${ + errorMsg: `Limit Amount ${formatAmountShow(min, 4, BigNumber.ROUND_CEIL)}-${formatAmountShow(max, 4)} ${ type === TypeEnum.BUY ? fiat?.currency : token.crypto }`, }); @@ -97,6 +98,8 @@ export const useReceive = ( setReceiveAmount(''); clearRefreshReceive(); return; + } else { + setAmountError(INIT_NONE_ERROR); } } diff --git a/packages/utils/converter.ts b/packages/utils/converter.ts index 3120b46f8d..8c6a977a37 100644 --- a/packages/utils/converter.ts +++ b/packages/utils/converter.ts @@ -131,8 +131,12 @@ export function formatWithCommas({ return amountTrans; } -export const formatAmountShow = (count: number | BigNumber | string, decimal = 4) => { +export const formatAmountShow = ( + count: number | BigNumber | string, + decimal = 4, + roundingMode = BigNumber.ROUND_DOWN, +) => { const bigCount = BigNumber.isBigNumber(count) ? count : new BigNumber(count || ''); if (bigCount.isNaN()) return '0'; - return bigCount.decimalPlaces(decimal, BigNumber.ROUND_DOWN).toFormat(); + return bigCount.decimalPlaces(decimal, roundingMode).toFormat(); }; From 5caaf3b8c7cad4d918c96166f525e155a9063841 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 12 Jun 2023 17:01:21 +0800 Subject: [PATCH 077/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20close=20origin?= =?UTF-8?q?=20page,=20after=20expandHome?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Buy/const.ts | 2 +- packages/web-extension-did/app/web/pages/Buy/index.tsx | 7 +++++-- .../app/web/serviceWorker/ServiceWorkerInstantiate.ts | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index 87c4d91822..8a7d107a2c 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -34,7 +34,7 @@ export const initFiat: PartialFiatType = { export const MAX_UPDATE_TIME = 15; export const initCurrency = '200'; -export const initCrypto = '300'; +export const initCrypto = '400'; export const initValueSave: { amount: string; currency: string; diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 45f2f4d316..1cdfdee043 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -133,6 +133,7 @@ export default function Buy() { const setErrMsgCase = useCallback(() => { const { min, max, currency, crypto, side } = valueSaveRef.current; if (min !== null && max !== null) { + clearInterval(updateTimerRef.current); if (side === PaymentTypeEnum.SELL) { setErrMsg(showLimitText(min, max, crypto)); } @@ -275,13 +276,15 @@ export default function Buy() { setLoading(true); await getQuoteAndSetData(); await updateCrypto(); + + handleInputChange(valueSaveRef.current.amount); } catch (error) { console.log('error', error); } finally { setLoading(false); } }, - [getQuoteAndSetData, setLoading, updateCrypto], + [getQuoteAndSetData, handleInputChange, setLoading, updateCrypto], ); const handleSelect = useCallback( @@ -328,7 +331,7 @@ export default function Buy() { if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { valueSaveRef.current.max = data.maxSellAmount; valueSaveRef.current.min = data.minSellAmount; - setErrMsgCase(); + // setErrMsgCase(); if (isValidValue({ amount, max: data.maxSellAmount, min: data.minSellAmount })) { await getQuoteAndSetData(); diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index c10adb86cc..d1d3a2824b 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -203,7 +203,7 @@ export default class ServiceWorkerInstantiate { this.getSocialLogin(sendResponse, message.payload); break; case WalletMessageTypes.ACH_SELL_REDIRECT: - ServiceWorkerInstantiate.expandHome(message.payload); + this.expandHomeAndCloseOrigin(sendResponse, message.payload); break; default: @@ -349,7 +349,7 @@ export default class ServiceWorkerInstantiate { ); } - static async expandHome(payload: any) { + async expandHomeAndCloseOrigin(sendResponse: SendResponseFun, payload: any) { notificationService.openPrompt( { method: PromptRouteTypes.EXPAND_FULL_SCREEN, @@ -357,6 +357,7 @@ export default class ServiceWorkerInstantiate { }, 'tabs', ); + sendResponse(errorHandler(0)); } /** From 104eb5c728fc187aa09e9b60c32ccaa0398a6c1f Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 12 Jun 2023 17:04:01 +0800 Subject: [PATCH 078/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20unuse=20?= =?UTF-8?q?log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 1 - packages/web-extension-did/app/web/pages/Home/index.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index c665c07cb1..3a11cf4834 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -69,7 +69,6 @@ export const useSellTransfer = () => { } const result = await paymentSellTransfer(achTxAddressReceived); - console.log('🌹 🌹 🌹 paymentSellTransfer', result); if (result.error) { throw result.error; } diff --git a/packages/web-extension-did/app/web/pages/Home/index.tsx b/packages/web-extension-did/app/web/pages/Home/index.tsx index dc13d8395f..6f137fa2f3 100644 --- a/packages/web-extension-did/app/web/pages/Home/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/index.tsx @@ -34,7 +34,6 @@ export default function Home() { // // TODO SELL LOCKED // } if (detail && method === walletMessage.ACH_SELL_REDIRECT && !locked && isSell.current === 0) { - console.log('🌹 🌹 🌹', ''); isSell.current = 1; handleAchSell(detail); } From cbf1f22966411e98c0f77cef2e09f91d599d2f4d Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 12 Jun 2023 17:09:56 +0800 Subject: [PATCH 079/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20getSignature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/dapp.ts | 1 + .../js/dapp/dappMobileOperator.ts | 50 +++++++++++++++++-- .../mobile-app-did/js/dapp/dappOverlay.ts | 21 ++++++++ packages/mobile-app-did/package.json | 8 +-- packages/types/package.json | 2 +- packages/utils/package.json | 6 +-- packages/web-extension-did/package.json | 8 +-- 7 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 packages/constants/constants-ca/dapp.ts diff --git a/packages/constants/constants-ca/dapp.ts b/packages/constants/constants-ca/dapp.ts new file mode 100644 index 0000000000..4dbd57f49b --- /dev/null +++ b/packages/constants/constants-ca/dapp.ts @@ -0,0 +1 @@ +export const CA_METHOD_WHITELIST = ['ManagerForwardCall', 'ManagerTransfer']; diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 6b1297ea27..61edd1ce6a 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -9,6 +9,7 @@ import { SendTransactionParams, NotificationEvents, WalletState, + GetSignatureParams, } from '@portkey/provider-types'; import DappEventBus from './dappEventBus'; import { generateNormalResponse, generateErrorResponse } from '@portkey/provider-utils'; @@ -20,16 +21,21 @@ import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount, getPin } from 'utils/redux'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { isEqDapp } from '@portkey-wallet/utils/dapp/browser'; - +import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; const SEND_METHOD: { [key: string]: true } = { [MethodsBase.SEND_TRANSACTION]: true, [MethodsBase.REQUEST_ACCOUNTS]: true, + [MethodsUnimplemented.GET_WALLET_SIGNATURE]: true, }; -function getContract({ rpcUrl, contractAddress }: { rpcUrl: string; contractAddress: string }) { +function getManager() { const pin = getPin(); if (!pin) return; - const manager = getManagerAccount(pin); + return getManagerAccount(pin); +} + +function getContract({ rpcUrl, contractAddress }: { rpcUrl: string; contractAddress: string }) { + const manager = getManager(); if (!manager) return; return getContractBasic({ rpcUrl, contractAddress, account: manager }); } @@ -109,6 +115,12 @@ export default class DappMobileOperator extends Operator { data: await this.dappManager.walletName(), }); } + case MethodsBase.NETWORK: { + return generateNormalResponse({ + eventName, + data: await this.dappManager.networkType(), + }); + } case MethodsUnimplemented.GET_WALLET_STATE: { const [isActive, isLocked] = await Promise.all([this.isActive(), this.dappManager.isLocked()]); const data: WalletState = { isConnected: isActive, isUnlocked: !isLocked }; @@ -175,6 +187,13 @@ export default class DappMobileOperator extends Operator { functionName = 'ManagerForwardCall'; } + if (!CA_METHOD_WHITELIST.includes(functionName)) + return generateErrorResponse({ + eventName, + code: ResponseCode.CONTRACT_ERROR, + msg: 'method is not in the whitelist', + }); + const data = await contract!.callSendMethod(functionName, '', paramsOption, { onMethod: 'transactionHash' }); if (!data?.error) { return generateNormalResponse({ @@ -196,7 +215,24 @@ export default class DappMobileOperator extends Operator { }); } }; - + protected handleSignature: SendRequest = async (eventName, params) => { + try { + if (!params.data) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); + const manager = getManager(); + if (!manager?.keyPair) return generateErrorResponse({ eventName, code: ResponseCode.INTERNAL_ERROR }); + const data = manager.keyPair.sign(params.data); + return generateNormalResponse({ + eventName, + data, + }); + } catch (error) { + return generateErrorResponse({ + eventName, + code: ResponseCode.CONTRACT_ERROR, + msg: handleErrorMessage(error), + }); + } + }; protected async sendRequest({ eventName, params, @@ -242,6 +278,12 @@ export default class DappMobileOperator extends Operator { params = request.payload; break; } + case MethodsUnimplemented.GET_WALLET_SIGNATURE: { + if (!isActive) return this.unauthenticated(eventName); + callBack = this.handleSignature; + params = request.payload; + break; + } } return this.sendRequest({ eventName, diff --git a/packages/mobile-app-did/js/dapp/dappOverlay.ts b/packages/mobile-app-did/js/dapp/dappOverlay.ts index 40ebc92bef..b2f95bcf43 100644 --- a/packages/mobile-app-did/js/dapp/dappOverlay.ts +++ b/packages/mobile-app-did/js/dapp/dappOverlay.ts @@ -5,6 +5,7 @@ import ActionSheet from 'components/ActionSheet'; export interface IDappOverlay { requestAccounts(dapp: DappStoreItem): Promise; sendTransaction(params: SendTransactionParams): Promise; + wallet_getSignature(params: any): Promise; } export class DappOverlay implements IDappOverlay { @@ -48,4 +49,24 @@ export class DappOverlay implements IDappOverlay { }); }); } + async wallet_getSignature(params: SendTransactionParams): Promise { + return new Promise(resolve => { + // mock approve + ActionSheet.alert({ + title: 'send', + message: JSON.stringify(params), + buttons: [ + { + title: 'OK', + type: 'solid', + onPress: () => resolve(true), + }, + { + title: 'DENIED', + onPress: () => resolve(false), + }, + ], + }); + }); + } } diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index eb42dc0061..dd90465546 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -51,10 +51,10 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/mobile-provider": "^0.0.1-alpha.19", - "@portkey/provider-types": "^0.0.1-alpha.19", - "@portkey/provider-utils": "^0.0.1-alpha.19", - "@portkey/providers": "^0.0.1-alpha.19", + "@portkey/mobile-provider": "^0.0.1-alpha.22", + "@portkey/provider-types": "^0.0.1-alpha.22", + "@portkey/provider-utils": "^0.0.1-alpha.22", + "@portkey/providers": "^0.0.1-alpha.22", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index b90802b866..5f5e17747b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "version": "0.0.1", "type": "commonjs", "dependencies": { - "@portkey/provider-types": "^0.0.1-alpha.19" + "@portkey/provider-types": "^0.0.1-alpha.22" }, "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/utils/package.json b/packages/utils/package.json index 574bea0fdc..f43eb0ee18 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,8 +23,8 @@ "expo-local-authentication": "^12.3.0", "expo-secure-store": "^11.3.0", "@react-native-async-storage/async-storage": "^1.17.6", - "@portkey/provider-types": "^0.0.1-alpha.19", - "@portkey/provider-utils": "^0.0.1-alpha.19", - "@portkey/providers": "^0.0.1-alpha.19" + "@portkey/provider-types": "^0.0.1-alpha.22", + "@portkey/provider-utils": "^0.0.1-alpha.22", + "@portkey/providers": "^0.0.1-alpha.22" } } diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index f76d8d8890..d68943b799 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.21", - "@portkey/provider-utils": "0.0.1-alpha.21", - "@portkey/provider-types": "0.0.1-alpha.21", - "@portkey/providers": "0.0.1-alpha.21", + "@portkey/extension-provider": "0.0.1-alpha.22", + "@portkey/provider-utils": "0.0.1-alpha.22", + "@portkey/provider-types": "0.0.1-alpha.22", + "@portkey/providers": "0.0.1-alpha.22", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", From bf41872caa8189596e49ce043646c7edb6284c0e Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 12 Jun 2023 17:15:28 +0800 Subject: [PATCH 080/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Signature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/dapp/dappOverlay.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/dappOverlay.ts b/packages/mobile-app-did/js/dapp/dappOverlay.ts index b2f95bcf43..368ea256f9 100644 --- a/packages/mobile-app-did/js/dapp/dappOverlay.ts +++ b/packages/mobile-app-did/js/dapp/dappOverlay.ts @@ -1,11 +1,11 @@ import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; -import { SendTransactionParams } from '@portkey/provider-types'; +import { GetSignatureParams, SendTransactionParams } from '@portkey/provider-types'; import ActionSheet from 'components/ActionSheet'; export interface IDappOverlay { requestAccounts(dapp: DappStoreItem): Promise; sendTransaction(params: SendTransactionParams): Promise; - wallet_getSignature(params: any): Promise; + wallet_getSignature(params: GetSignatureParams): Promise; } export class DappOverlay implements IDappOverlay { @@ -49,7 +49,7 @@ export class DappOverlay implements IDappOverlay { }); }); } - async wallet_getSignature(params: SendTransactionParams): Promise { + async wallet_getSignature(params: GetSignatureParams): Promise { return new Promise(resolve => { // mock approve ActionSheet.alert({ From a1a34de7a2e652d6b072e219b0ab2be953a0ea81 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 12 Jun 2023 17:20:40 +0800 Subject: [PATCH 081/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20formatA?= =?UTF-8?q?mountShow=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/converter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/converter.ts b/packages/utils/converter.ts index 8c6a977a37..29534dc458 100644 --- a/packages/utils/converter.ts +++ b/packages/utils/converter.ts @@ -134,7 +134,7 @@ export function formatWithCommas({ export const formatAmountShow = ( count: number | BigNumber | string, decimal = 4, - roundingMode = BigNumber.ROUND_DOWN, + roundingMode: BigNumber.RoundingMode = BigNumber.ROUND_DOWN, ) => { const bigCount = BigNumber.isBigNumber(count) ? count : new BigNumber(count || ''); if (bigCount.isNaN()) return '0'; From 38643ea2e054069b1ea640f82c843706f4a206a5 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 12 Jun 2023 17:24:33 +0800 Subject: [PATCH 082/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20modify=20sell=20?= =?UTF-8?q?min=20limit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Buy/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 1cdfdee043..5f867857d6 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -33,6 +33,7 @@ import BuyFrom from './components/BuyFrom'; import SellFrom from './components/SellFrom'; import { useEffectOnce } from 'react-use'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import BigNumber from 'bignumber.js'; export default function Buy() { const { t } = useTranslation(); @@ -95,7 +96,7 @@ export default function Buy() { const showLimitText = useCallback( (min: string | number, max: string | number, fiat = 'USD') => - `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${fiat} `, + `Limit Amount ${formatAmountShow(min, 4, BigNumber.ROUND_CEIL)}-${formatAmountShow(max)} ${fiat} `, [], ); From e168e558f0d949f4d303640a8b0c43209a4fcf57 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 12 Jun 2023 18:18:24 +0800 Subject: [PATCH 083/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20set=20RequestConf?= =?UTF-8?q?ig=20headers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/utils.ts | 11 ++++++++++- packages/mobile-app-did/js/api/utils.ts | 6 +++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/api/utils.ts b/packages/api/utils.ts index 9bd7e3f9e7..cb346b46d9 100644 --- a/packages/api/utils.ts +++ b/packages/api/utils.ts @@ -12,6 +12,10 @@ export function spliceUrl(baseUrl: string, extendArg: string = '') { } export function getRequestConfig(base: BaseConfig, config?: RequestConfig, defaultConfig?: RequestConfig) { + const headers = { + ...defaultConfig?.headers, + ...config?.headers, + }; if (typeof base === 'string') { if (defaultConfig) { return { @@ -19,6 +23,7 @@ export function getRequestConfig(base: BaseConfig, config?: RequestConfig, defau ...config, baseURL: config?.baseURL === undefined ? defaultConfig.baseURL : config?.baseURL, params: { ...defaultConfig.params, ...config?.params }, + headers, }; } return config; @@ -26,11 +31,15 @@ export function getRequestConfig(base: BaseConfig, config?: RequestConfig, defau const { config: baseConfig } = base || {}; const { params } = config || {}; return { - ...defaultConfig, ...baseConfig, + ...defaultConfig, ...config, baseURL: config?.baseURL === undefined ? defaultConfig?.baseURL : config?.baseURL, params: { ...defaultConfig?.params, ...baseConfig.params, ...params }, + headers: { + ...baseConfig?.headers, + ...headers, + }, }; } } diff --git a/packages/mobile-app-did/js/api/utils.ts b/packages/mobile-app-did/js/api/utils.ts index 077e8aa93b..016bdf1657 100644 --- a/packages/mobile-app-did/js/api/utils.ts +++ b/packages/mobile-app-did/js/api/utils.ts @@ -44,12 +44,16 @@ export function getRequestConfig(base: BaseConfig, config?: RequestConfig) { const { baseConfig } = base || {}; const { query, method, params, data } = config || {}; return { - ...config, ...baseConfig, + ...config, query: (baseConfig.query || '') + (query || ''), method: method ? method : baseConfig.method, params: Object.assign({}, baseConfig.params, params), data: Object.assign({}, baseConfig.data, data), + headers: { + ...baseConfig.headers, + ...config?.headers, + }, }; } } From 347d72417726ecab057290012b3a4c5b9a7d4305 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 18:57:52 +0800 Subject: [PATCH 084/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20update=20graphQl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cms/__generated__/hooks/bottomMenu.ts | 529 ++++++ .../hooks/bottomMenu_aggregated.ts | 233 +++ .../__generated__/hooks/bottomMenu_by_id.ts | 514 ++++++ .../__generated__/hooks/bottomSecondMenu.ts | 527 ++++++ .../hooks/bottomSecondMenu_aggregated.ts | 248 +++ .../hooks/bottomSecondMenu_by_id.ts | 519 ++++++ .../cms/__generated__/hooks/deviceBrand.ts | 131 ++ .../hooks/deviceBrand_aggregated.ts | 173 ++ .../__generated__/hooks/deviceBrand_by_id.ts | 117 ++ .../cms/__generated__/hooks/deviceType.ts | 129 ++ .../hooks/deviceType_aggregated.ts | 209 +++ .../__generated__/hooks/deviceType_by_id.ts | 114 ++ .../cms/__generated__/hooks/discoverGroup.ts | 4 + .../hooks/discoverGroupCustom.ts | 2 + .../hooks/discoverGroup_by_id.ts | 4 + .../cms/__generated__/hooks/discoverItem.ts | 4 + .../hooks/discoverItem_aggregated.ts | 4 + .../__generated__/hooks/discoverItem_by_id.ts | 4 + .../cms/__generated__/hooks/mediaKit.ts | 363 ++++ .../cms/__generated__/hooks/mediaKitPage.ts | 777 +++++++++ .../hooks/mediaKitPage_mediaKit.ts | 755 +++++++++ .../hooks/mediaKitPage_mediaKit_aggregated.ts | 195 +++ .../hooks/mediaKitPage_mediaKit_by_id.ts | 734 ++++++++ .../hooks/mediaKit_aggregated.ts | 221 +++ .../cms/__generated__/hooks/mediaKit_by_id.ts | 350 ++++ .../cms/__generated__/hooks/navigationType.ts | 131 ++ .../hooks/navigationType_aggregated.ts | 209 +++ .../hooks/navigationType_by_id.ts | 123 ++ .../hooks/officialSocialMedia.ts | 383 +++++ .../hooks/officialSocialMedia_aggregated.ts | 229 +++ .../hooks/officialSocialMedia_by_id.ts | 359 ++++ .../cms/__generated__/hooks/topMenu.ts | 527 ++++++ .../__generated__/hooks/topMenu_aggregated.ts | 233 +++ .../cms/__generated__/hooks/topMenu_by_id.ts | 514 ++++++ .../cms/__generated__/hooks/topSecondMenu.ts | 527 ++++++ .../hooks/topSecondMenu_aggregated.ts | 245 +++ .../hooks/topSecondMenu_by_id.ts | 519 ++++++ .../operation/queries/bottomMenu.gql | 169 ++ .../queries/bottomMenu_aggregated.gql | 70 + .../operation/queries/bottomMenu_by_id.gql | 169 ++ .../operation/queries/bottomSecondMenu.gql | 166 ++ .../queries/bottomSecondMenu_aggregated.gql | 76 + .../queries/bottomSecondMenu_by_id.gql | 166 ++ .../operation/queries/deviceBrand.gql | 33 + .../queries/deviceBrand_aggregated.gql | 52 + .../operation/queries/deviceBrand_by_id.gql | 33 + .../operation/queries/deviceType.gql | 33 + .../queries/deviceType_aggregated.gql | 58 + .../operation/queries/deviceType_by_id.gql | 33 + .../operation/queries/discoverGroup.gql | 2 + .../operation/queries/discoverGroup_by_id.gql | 2 + .../operation/queries/discoverItem.gql | 2 + .../queries/discoverItem_aggregated.gql | 2 + .../operation/queries/discoverItem_by_id.gql | 2 + .../__generated__/operation/queries/index.js | 49 + .../operation/queries/mediaKit.gql | 132 ++ .../operation/queries/mediaKitPage.gql | 276 +++ .../queries/mediaKitPage_mediaKit.gql | 242 +++ .../mediaKitPage_mediaKit_aggregated.gql | 46 + .../queries/mediaKitPage_mediaKit_by_id.gql | 242 +++ .../operation/queries/mediaKit_aggregated.gql | 64 + .../operation/queries/mediaKit_by_id.gql | 132 ++ .../operation/queries/navigationType.gql | 33 + .../queries/navigationType_aggregated.gql | 58 + .../queries/navigationType_by_id.gql | 33 + .../operation/queries/officialSocialMedia.gql | 132 ++ .../officialSocialMedia_aggregated.gql | 64 + .../queries/officialSocialMedia_by_id.gql | 132 ++ .../operation/queries/topMenu.gql | 169 ++ .../operation/queries/topMenu_aggregated.gql | 70 + .../operation/queries/topMenu_by_id.gql | 169 ++ .../operation/queries/topSecondMenu.gql | 166 ++ .../queries/topSecondMenu_aggregated.gql | 76 + .../operation/queries/topSecondMenu_by_id.gql | 166 ++ packages/graphql/cms/__generated__/types.ts | 1496 +++++++++++++++-- .../cms/custom/discoverGroupCustom.gql | 1 + packages/graphql/schema/cms_schema.graphql | 719 ++++++++ yarn.lock | 104 +- 78 files changed, 16449 insertions(+), 249 deletions(-) create mode 100644 packages/graphql/cms/__generated__/hooks/bottomMenu.ts create mode 100644 packages/graphql/cms/__generated__/hooks/bottomMenu_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/bottomMenu_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/bottomSecondMenu.ts create mode 100644 packages/graphql/cms/__generated__/hooks/bottomSecondMenu_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/bottomSecondMenu_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/deviceBrand.ts create mode 100644 packages/graphql/cms/__generated__/hooks/deviceBrand_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/deviceBrand_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/deviceType.ts create mode 100644 packages/graphql/cms/__generated__/hooks/deviceType_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/deviceType_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKit.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKitPage.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKit_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/mediaKit_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/navigationType.ts create mode 100644 packages/graphql/cms/__generated__/hooks/navigationType_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/navigationType_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/officialSocialMedia.ts create mode 100644 packages/graphql/cms/__generated__/hooks/officialSocialMedia_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/officialSocialMedia_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/topMenu.ts create mode 100644 packages/graphql/cms/__generated__/hooks/topMenu_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/topMenu_by_id.ts create mode 100644 packages/graphql/cms/__generated__/hooks/topSecondMenu.ts create mode 100644 packages/graphql/cms/__generated__/hooks/topSecondMenu_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/topSecondMenu_by_id.ts create mode 100644 packages/graphql/cms/__generated__/operation/queries/bottomMenu.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/bottomMenu_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/bottomMenu_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/deviceBrand.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/deviceBrand_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/deviceBrand_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/deviceType.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/deviceType_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/deviceType_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKit.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKitPage.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKit_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/mediaKit_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/navigationType.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/navigationType_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/navigationType_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/officialSocialMedia.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/topMenu.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/topMenu_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/topMenu_by_id.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/topSecondMenu.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/topSecondMenu_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/topSecondMenu_by_id.gql diff --git a/packages/graphql/cms/__generated__/hooks/bottomMenu.ts b/packages/graphql/cms/__generated__/hooks/bottomMenu.ts new file mode 100644 index 0000000000..e487cb50e1 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/bottomMenu.ts @@ -0,0 +1,529 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BottomMenuQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; +}>; + +export type BottomMenuQuery = { + __typename?: 'Query'; + bottomMenu: Array<{ + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + children?: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + }>; +}; + +export const BottomMenuDocument = gql` + query bottomMenu( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: navigationType_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: bottomSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: bottomMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: bottomSecondMenu_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: bottomMenu_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + ) { + bottomMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func { + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func { + count + } + } + } +`; + +/** + * __useBottomMenuQuery__ + * + * To run a query within a React component, call `useBottomMenuQuery` and pass it any options that fit your needs. + * When your component renders, `useBottomMenuQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBottomMenuQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * }, + * }); + */ +export function useBottomMenuQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(BottomMenuDocument, options); +} +export function useBottomMenuLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(BottomMenuDocument, options); +} +export type BottomMenuQueryHookResult = ReturnType; +export type BottomMenuLazyQueryHookResult = ReturnType; +export type BottomMenuQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/bottomMenu_aggregated.ts b/packages/graphql/cms/__generated__/hooks/bottomMenu_aggregated.ts new file mode 100644 index 0000000000..3a189ae659 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/bottomMenu_aggregated.ts @@ -0,0 +1,233 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BottomMenu_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type BottomMenu_AggregatedQuery = { + __typename?: 'Query'; + bottomMenu_aggregated: Array<{ + __typename?: 'bottomMenu_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'bottomMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + children?: number | null; + } | null; + countDistinct?: { + __typename?: 'bottomMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + children?: number | null; + } | null; + avg?: { + __typename?: 'bottomMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sum?: { + __typename?: 'bottomMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + avgDistinct?: { + __typename?: 'bottomMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sumDistinct?: { + __typename?: 'bottomMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + min?: { + __typename?: 'bottomMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + max?: { + __typename?: 'bottomMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + }>; +}; + +export const BottomMenu_AggregatedDocument = gql` + query bottomMenu_aggregated( + $groupBy: [String] + $filter: bottomMenu_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + bottomMenu_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + countDistinct { + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + avg { + id + index + sort + type + } + sum { + id + index + sort + type + } + avgDistinct { + id + index + sort + type + } + sumDistinct { + id + index + sort + type + } + min { + id + index + sort + type + } + max { + id + index + sort + type + } + } + } +`; + +/** + * __useBottomMenu_AggregatedQuery__ + * + * To run a query within a React component, call `useBottomMenu_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useBottomMenu_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBottomMenu_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useBottomMenu_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + BottomMenu_AggregatedDocument, + options, + ); +} +export function useBottomMenu_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + BottomMenu_AggregatedDocument, + options, + ); +} +export type BottomMenu_AggregatedQueryHookResult = ReturnType; +export type BottomMenu_AggregatedLazyQueryHookResult = ReturnType; +export type BottomMenu_AggregatedQueryResult = Apollo.QueryResult< + BottomMenu_AggregatedQuery, + BottomMenu_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/bottomMenu_by_id.ts b/packages/graphql/cms/__generated__/hooks/bottomMenu_by_id.ts new file mode 100644 index 0000000000..0d2def2d6a --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/bottomMenu_by_id.ts @@ -0,0 +1,514 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BottomMenu_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type BottomMenu_By_IdQuery = { + __typename?: 'Query'; + bottomMenu_by_id?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + children?: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; +}; + +export const BottomMenu_By_IdDocument = gql` + query bottomMenu_by_id( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: navigationType_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: bottomSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: bottomMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: bottomSecondMenu_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $id: ID! + ) { + bottomMenu_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func { + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func { + count + } + } + } +`; + +/** + * __useBottomMenu_By_IdQuery__ + * + * To run a query within a React component, call `useBottomMenu_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useBottomMenu_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBottomMenu_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * id: // value for 'id' + * }, + * }); + */ +export function useBottomMenu_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(BottomMenu_By_IdDocument, options); +} +export function useBottomMenu_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(BottomMenu_By_IdDocument, options); +} +export type BottomMenu_By_IdQueryHookResult = ReturnType; +export type BottomMenu_By_IdLazyQueryHookResult = ReturnType; +export type BottomMenu_By_IdQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/bottomSecondMenu.ts b/packages/graphql/cms/__generated__/hooks/bottomSecondMenu.ts new file mode 100644 index 0000000000..8236a37eed --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/bottomSecondMenu.ts @@ -0,0 +1,527 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BottomSecondMenuQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; +}>; + +export type BottomSecondMenuQuery = { + __typename?: 'Query'; + bottomSecondMenu: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + }>; +}; + +export const BottomSecondMenuDocument = gql` + query bottomSecondMenu( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: bottomMenu_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: bottomSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: bottomMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: navigationType_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: bottomSecondMenu_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + ) { + bottomSecondMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func { + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } +`; + +/** + * __useBottomSecondMenuQuery__ + * + * To run a query within a React component, call `useBottomSecondMenuQuery` and pass it any options that fit your needs. + * When your component renders, `useBottomSecondMenuQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBottomSecondMenuQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * }, + * }); + */ +export function useBottomSecondMenuQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(BottomSecondMenuDocument, options); +} +export function useBottomSecondMenuLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(BottomSecondMenuDocument, options); +} +export type BottomSecondMenuQueryHookResult = ReturnType; +export type BottomSecondMenuLazyQueryHookResult = ReturnType; +export type BottomSecondMenuQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/bottomSecondMenu_aggregated.ts b/packages/graphql/cms/__generated__/hooks/bottomSecondMenu_aggregated.ts new file mode 100644 index 0000000000..e41211f25f --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/bottomSecondMenu_aggregated.ts @@ -0,0 +1,248 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BottomSecondMenu_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type BottomSecondMenu_AggregatedQuery = { + __typename?: 'Query'; + bottomSecondMenu_aggregated: Array<{ + __typename?: 'bottomSecondMenu_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'bottomSecondMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + parent?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + countDistinct?: { + __typename?: 'bottomSecondMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + parent?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + avg?: { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sum?: { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + avgDistinct?: { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sumDistinct?: { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + min?: { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + max?: { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + }>; +}; + +export const BottomSecondMenu_AggregatedDocument = gql` + query bottomSecondMenu_aggregated( + $groupBy: [String] + $filter: bottomSecondMenu_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + bottomSecondMenu_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + countDistinct { + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + avg { + id + index + parent + sort + type + } + sum { + id + index + parent + sort + type + } + avgDistinct { + id + index + parent + sort + type + } + sumDistinct { + id + index + parent + sort + type + } + min { + id + index + parent + sort + type + } + max { + id + index + parent + sort + type + } + } + } +`; + +/** + * __useBottomSecondMenu_AggregatedQuery__ + * + * To run a query within a React component, call `useBottomSecondMenu_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useBottomSecondMenu_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBottomSecondMenu_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useBottomSecondMenu_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + BottomSecondMenu_AggregatedDocument, + options, + ); +} +export function useBottomSecondMenu_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + BottomSecondMenu_AggregatedQuery, + BottomSecondMenu_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + BottomSecondMenu_AggregatedDocument, + options, + ); +} +export type BottomSecondMenu_AggregatedQueryHookResult = ReturnType; +export type BottomSecondMenu_AggregatedLazyQueryHookResult = ReturnType; +export type BottomSecondMenu_AggregatedQueryResult = Apollo.QueryResult< + BottomSecondMenu_AggregatedQuery, + BottomSecondMenu_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/bottomSecondMenu_by_id.ts b/packages/graphql/cms/__generated__/hooks/bottomSecondMenu_by_id.ts new file mode 100644 index 0000000000..646a588efc --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/bottomSecondMenu_by_id.ts @@ -0,0 +1,519 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BottomSecondMenu_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type BottomSecondMenu_By_IdQuery = { + __typename?: 'Query'; + bottomSecondMenu_by_id?: { + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'bottomSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'bottomMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null; +}; + +export const BottomSecondMenu_By_IdDocument = gql` + query bottomSecondMenu_by_id( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: bottomMenu_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: bottomSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: bottomMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: navigationType_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $id: ID! + ) { + bottomSecondMenu_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func { + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } +`; + +/** + * __useBottomSecondMenu_By_IdQuery__ + * + * To run a query within a React component, call `useBottomSecondMenu_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useBottomSecondMenu_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBottomSecondMenu_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * id: // value for 'id' + * }, + * }); + */ +export function useBottomSecondMenu_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + BottomSecondMenu_By_IdDocument, + options, + ); +} +export function useBottomSecondMenu_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + BottomSecondMenu_By_IdDocument, + options, + ); +} +export type BottomSecondMenu_By_IdQueryHookResult = ReturnType; +export type BottomSecondMenu_By_IdLazyQueryHookResult = ReturnType; +export type BottomSecondMenu_By_IdQueryResult = Apollo.QueryResult< + BottomSecondMenu_By_IdQuery, + BottomSecondMenu_By_IdQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/deviceBrand.ts b/packages/graphql/cms/__generated__/hooks/deviceBrand.ts new file mode 100644 index 0000000000..b4d2a435ab --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/deviceBrand.ts @@ -0,0 +1,131 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type DeviceBrandQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; +}>; + +export type DeviceBrandQuery = { + __typename?: 'Query'; + deviceBrand: Array<{ + __typename?: 'deviceBrand'; + date_created?: any | null; + date_updated?: any | null; + id: string; + label?: string | null; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + }>; +}; + +export const DeviceBrandDocument = gql` + query deviceBrand( + $filter: deviceBrand_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + ) { + deviceBrand(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } + } +`; + +/** + * __useDeviceBrandQuery__ + * + * To run a query within a React component, call `useDeviceBrandQuery` and pass it any options that fit your needs. + * When your component renders, `useDeviceBrandQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDeviceBrandQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * }, + * }); + */ +export function useDeviceBrandQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(DeviceBrandDocument, options); +} +export function useDeviceBrandLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(DeviceBrandDocument, options); +} +export type DeviceBrandQueryHookResult = ReturnType; +export type DeviceBrandLazyQueryHookResult = ReturnType; +export type DeviceBrandQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/deviceBrand_aggregated.ts b/packages/graphql/cms/__generated__/hooks/deviceBrand_aggregated.ts new file mode 100644 index 0000000000..a03e56b286 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/deviceBrand_aggregated.ts @@ -0,0 +1,173 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type DeviceBrand_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type DeviceBrand_AggregatedQuery = { + __typename?: 'Query'; + deviceBrand_aggregated: Array<{ + __typename?: 'deviceBrand_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'deviceBrand_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + label?: number | null; + sort?: number | null; + status?: number | null; + user_created?: number | null; + user_updated?: number | null; + value?: number | null; + } | null; + countDistinct?: { + __typename?: 'deviceBrand_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + label?: number | null; + sort?: number | null; + status?: number | null; + user_created?: number | null; + user_updated?: number | null; + value?: number | null; + } | null; + avg?: { __typename?: 'deviceBrand_aggregated_fields'; id?: number | null; sort?: number | null } | null; + sum?: { __typename?: 'deviceBrand_aggregated_fields'; id?: number | null; sort?: number | null } | null; + avgDistinct?: { __typename?: 'deviceBrand_aggregated_fields'; id?: number | null; sort?: number | null } | null; + sumDistinct?: { __typename?: 'deviceBrand_aggregated_fields'; id?: number | null; sort?: number | null } | null; + min?: { __typename?: 'deviceBrand_aggregated_fields'; id?: number | null; sort?: number | null } | null; + max?: { __typename?: 'deviceBrand_aggregated_fields'; id?: number | null; sort?: number | null } | null; + }>; +}; + +export const DeviceBrand_AggregatedDocument = gql` + query deviceBrand_aggregated( + $groupBy: [String] + $filter: deviceBrand_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + deviceBrand_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + countDistinct { + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + avg { + id + sort + } + sum { + id + sort + } + avgDistinct { + id + sort + } + sumDistinct { + id + sort + } + min { + id + sort + } + max { + id + sort + } + } + } +`; + +/** + * __useDeviceBrand_AggregatedQuery__ + * + * To run a query within a React component, call `useDeviceBrand_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useDeviceBrand_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDeviceBrand_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useDeviceBrand_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + DeviceBrand_AggregatedDocument, + options, + ); +} +export function useDeviceBrand_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + DeviceBrand_AggregatedDocument, + options, + ); +} +export type DeviceBrand_AggregatedQueryHookResult = ReturnType; +export type DeviceBrand_AggregatedLazyQueryHookResult = ReturnType; +export type DeviceBrand_AggregatedQueryResult = Apollo.QueryResult< + DeviceBrand_AggregatedQuery, + DeviceBrand_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/deviceBrand_by_id.ts b/packages/graphql/cms/__generated__/hooks/deviceBrand_by_id.ts new file mode 100644 index 0000000000..f55a2d6c42 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/deviceBrand_by_id.ts @@ -0,0 +1,117 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type DeviceBrand_By_IdQueryVariables = Types.Exact<{ + id: Types.Scalars['ID']; +}>; + +export type DeviceBrand_By_IdQuery = { + __typename?: 'Query'; + deviceBrand_by_id?: { + __typename?: 'deviceBrand'; + date_created?: any | null; + date_updated?: any | null; + id: string; + label?: string | null; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; +}; + +export const DeviceBrand_By_IdDocument = gql` + query deviceBrand_by_id($id: ID!) { + deviceBrand_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } + } +`; + +/** + * __useDeviceBrand_By_IdQuery__ + * + * To run a query within a React component, call `useDeviceBrand_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useDeviceBrand_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDeviceBrand_By_IdQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useDeviceBrand_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(DeviceBrand_By_IdDocument, options); +} +export function useDeviceBrand_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + DeviceBrand_By_IdDocument, + options, + ); +} +export type DeviceBrand_By_IdQueryHookResult = ReturnType; +export type DeviceBrand_By_IdLazyQueryHookResult = ReturnType; +export type DeviceBrand_By_IdQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/deviceType.ts b/packages/graphql/cms/__generated__/hooks/deviceType.ts new file mode 100644 index 0000000000..f5208ab147 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/deviceType.ts @@ -0,0 +1,129 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type DeviceTypeQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; +}>; + +export type DeviceTypeQuery = { + __typename?: 'Query'; + deviceType: Array<{ + __typename?: 'deviceType'; + date_created?: any | null; + date_updated?: any | null; + id: string; + label?: string | null; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + }>; +}; + +export const DeviceTypeDocument = gql` + query deviceType( + $filter: deviceType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + ) { + deviceType(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } + } +`; + +/** + * __useDeviceTypeQuery__ + * + * To run a query within a React component, call `useDeviceTypeQuery` and pass it any options that fit your needs. + * When your component renders, `useDeviceTypeQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDeviceTypeQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * }, + * }); + */ +export function useDeviceTypeQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(DeviceTypeDocument, options); +} +export function useDeviceTypeLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(DeviceTypeDocument, options); +} +export type DeviceTypeQueryHookResult = ReturnType; +export type DeviceTypeLazyQueryHookResult = ReturnType; +export type DeviceTypeQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/deviceType_aggregated.ts b/packages/graphql/cms/__generated__/hooks/deviceType_aggregated.ts new file mode 100644 index 0000000000..09047b1b90 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/deviceType_aggregated.ts @@ -0,0 +1,209 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type DeviceType_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type DeviceType_AggregatedQuery = { + __typename?: 'Query'; + deviceType_aggregated: Array<{ + __typename?: 'deviceType_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'deviceType_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + label?: number | null; + sort?: number | null; + status?: number | null; + user_created?: number | null; + user_updated?: number | null; + value?: number | null; + } | null; + countDistinct?: { + __typename?: 'deviceType_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + label?: number | null; + sort?: number | null; + status?: number | null; + user_created?: number | null; + user_updated?: number | null; + value?: number | null; + } | null; + avg?: { + __typename?: 'deviceType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + sum?: { + __typename?: 'deviceType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + avgDistinct?: { + __typename?: 'deviceType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + sumDistinct?: { + __typename?: 'deviceType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + min?: { + __typename?: 'deviceType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + max?: { + __typename?: 'deviceType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + }>; +}; + +export const DeviceType_AggregatedDocument = gql` + query deviceType_aggregated( + $groupBy: [String] + $filter: deviceType_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + deviceType_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + countDistinct { + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + avg { + id + sort + value + } + sum { + id + sort + value + } + avgDistinct { + id + sort + value + } + sumDistinct { + id + sort + value + } + min { + id + sort + value + } + max { + id + sort + value + } + } + } +`; + +/** + * __useDeviceType_AggregatedQuery__ + * + * To run a query within a React component, call `useDeviceType_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useDeviceType_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDeviceType_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useDeviceType_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + DeviceType_AggregatedDocument, + options, + ); +} +export function useDeviceType_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + DeviceType_AggregatedDocument, + options, + ); +} +export type DeviceType_AggregatedQueryHookResult = ReturnType; +export type DeviceType_AggregatedLazyQueryHookResult = ReturnType; +export type DeviceType_AggregatedQueryResult = Apollo.QueryResult< + DeviceType_AggregatedQuery, + DeviceType_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/deviceType_by_id.ts b/packages/graphql/cms/__generated__/hooks/deviceType_by_id.ts new file mode 100644 index 0000000000..ef511ada46 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/deviceType_by_id.ts @@ -0,0 +1,114 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type DeviceType_By_IdQueryVariables = Types.Exact<{ + id: Types.Scalars['ID']; +}>; + +export type DeviceType_By_IdQuery = { + __typename?: 'Query'; + deviceType_by_id?: { + __typename?: 'deviceType'; + date_created?: any | null; + date_updated?: any | null; + id: string; + label?: string | null; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; +}; + +export const DeviceType_By_IdDocument = gql` + query deviceType_by_id($id: ID!) { + deviceType_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } + } +`; + +/** + * __useDeviceType_By_IdQuery__ + * + * To run a query within a React component, call `useDeviceType_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useDeviceType_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useDeviceType_By_IdQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useDeviceType_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(DeviceType_By_IdDocument, options); +} +export function useDeviceType_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(DeviceType_By_IdDocument, options); +} +export type DeviceType_By_IdQueryHookResult = ReturnType; +export type DeviceType_By_IdLazyQueryHookResult = ReturnType; +export type DeviceType_By_IdQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/discoverGroup.ts b/packages/graphql/cms/__generated__/hooks/discoverGroup.ts index d92c97af53..a8c08fa689 100644 --- a/packages/graphql/cms/__generated__/hooks/discoverGroup.ts +++ b/packages/graphql/cms/__generated__/hooks/discoverGroup.ts @@ -89,6 +89,7 @@ export type DiscoverGroupQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; date_created_func?: { @@ -156,6 +157,7 @@ export type DiscoverGroupQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; imgUrl?: { @@ -389,6 +391,7 @@ export const DiscoverGroupDocument = gql` sort status title + url user_created user_updated } @@ -401,6 +404,7 @@ export const DiscoverGroupDocument = gql` sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/hooks/discoverGroupCustom.ts b/packages/graphql/cms/__generated__/hooks/discoverGroupCustom.ts index d8da3715ef..79b0267bcd 100644 --- a/packages/graphql/cms/__generated__/hooks/discoverGroupCustom.ts +++ b/packages/graphql/cms/__generated__/hooks/discoverGroupCustom.ts @@ -35,6 +35,7 @@ export type DiscoverGroupCustomQuery = { status?: string | null; title?: string | null; description?: string | null; + url?: string | null; imgUrl?: { __typename?: 'directus_files'; filename_disk?: string | null } | null; } | null> | null; }>; @@ -66,6 +67,7 @@ export const DiscoverGroupCustomDocument = gql` status title description + url imgUrl { filename_disk } diff --git a/packages/graphql/cms/__generated__/hooks/discoverGroup_by_id.ts b/packages/graphql/cms/__generated__/hooks/discoverGroup_by_id.ts index 6e9f8bba55..646b82c60d 100644 --- a/packages/graphql/cms/__generated__/hooks/discoverGroup_by_id.ts +++ b/packages/graphql/cms/__generated__/hooks/discoverGroup_by_id.ts @@ -82,6 +82,7 @@ export type DiscoverGroup_By_IdQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; date_created_func?: { @@ -149,6 +150,7 @@ export type DiscoverGroup_By_IdQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; imgUrl?: { @@ -377,6 +379,7 @@ export const DiscoverGroup_By_IdDocument = gql` sort status title + url user_created user_updated } @@ -389,6 +392,7 @@ export const DiscoverGroup_By_IdDocument = gql` sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/hooks/discoverItem.ts b/packages/graphql/cms/__generated__/hooks/discoverItem.ts index 76462c1d5c..beabbb5a1d 100644 --- a/packages/graphql/cms/__generated__/hooks/discoverItem.ts +++ b/packages/graphql/cms/__generated__/hooks/discoverItem.ts @@ -64,6 +64,7 @@ export type DiscoverItemQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; date_created_func?: { @@ -131,6 +132,7 @@ export type DiscoverItemQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; date_created_func?: { @@ -430,6 +432,7 @@ export const DiscoverItemDocument = gql` sort status title + url user_created user_updated } @@ -462,6 +465,7 @@ export const DiscoverItemDocument = gql` sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/hooks/discoverItem_aggregated.ts b/packages/graphql/cms/__generated__/hooks/discoverItem_aggregated.ts index 27d2f721f4..9c245b09c4 100644 --- a/packages/graphql/cms/__generated__/hooks/discoverItem_aggregated.ts +++ b/packages/graphql/cms/__generated__/hooks/discoverItem_aggregated.ts @@ -33,6 +33,7 @@ export type DiscoverItem_AggregatedQuery = { sort?: number | null; status?: number | null; title?: number | null; + url?: number | null; user_created?: number | null; user_updated?: number | null; } | null; @@ -48,6 +49,7 @@ export type DiscoverItem_AggregatedQuery = { sort?: number | null; status?: number | null; title?: number | null; + url?: number | null; user_created?: number | null; user_updated?: number | null; } | null; @@ -128,6 +130,7 @@ export const DiscoverItem_AggregatedDocument = gql` sort status title + url user_created user_updated } @@ -142,6 +145,7 @@ export const DiscoverItem_AggregatedDocument = gql` sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/hooks/discoverItem_by_id.ts b/packages/graphql/cms/__generated__/hooks/discoverItem_by_id.ts index cdfaff13c7..8a9860df4e 100644 --- a/packages/graphql/cms/__generated__/hooks/discoverItem_by_id.ts +++ b/packages/graphql/cms/__generated__/hooks/discoverItem_by_id.ts @@ -57,6 +57,7 @@ export type DiscoverItem_By_IdQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; date_created_func?: { @@ -124,6 +125,7 @@ export type DiscoverItem_By_IdQuery = { sort?: number | null; status?: string | null; title?: string | null; + url?: string | null; user_created?: string | null; user_updated?: string | null; date_created_func?: { @@ -418,6 +420,7 @@ export const DiscoverItem_By_IdDocument = gql` sort status title + url user_created user_updated } @@ -450,6 +453,7 @@ export const DiscoverItem_By_IdDocument = gql` sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/hooks/mediaKit.ts b/packages/graphql/cms/__generated__/hooks/mediaKit.ts new file mode 100644 index 0000000000..d7c6cb9d24 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKit.ts @@ -0,0 +1,363 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKitQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; +}>; + +export type MediaKitQuery = { + __typename?: 'Query'; + mediaKit: Array<{ + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + png?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + }>; +}; + +export const MediaKitDocument = gql` + query mediaKit( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: mediaKit_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + ) { + mediaKit(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + backgroundColor + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + } +`; + +/** + * __useMediaKitQuery__ + * + * To run a query within a React component, call `useMediaKitQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKitQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKitQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * }, + * }); + */ +export function useMediaKitQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(MediaKitDocument, options); +} +export function useMediaKitLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(MediaKitDocument, options); +} +export type MediaKitQueryHookResult = ReturnType; +export type MediaKitLazyQueryHookResult = ReturnType; +export type MediaKitQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/mediaKitPage.ts b/packages/graphql/cms/__generated__/hooks/mediaKitPage.ts new file mode 100644 index 0000000000..40fc64875c --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKitPage.ts @@ -0,0 +1,777 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKitPageQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; + filter7?: Types.InputMaybe; + sort7?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit7?: Types.InputMaybe; + offset7?: Types.InputMaybe; + page7?: Types.InputMaybe; + search7?: Types.InputMaybe; +}>; + +export type MediaKitPageQuery = { + __typename?: 'Query'; + mediaKitPage?: { + __typename?: 'mediaKitPage'; + content: string; + date_created?: any | null; + date_updated?: any | null; + id: string; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + allMediaKitZip?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + mediaKitList?: Array<{ + __typename?: 'mediaKitPage_mediaKit'; + id: string; + mediaKit_id?: { + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + png?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + } | null; + mediaKitPage_id?: { + __typename?: 'mediaKitPage'; + content: string; + date_created?: any | null; + date_updated?: any | null; + id: string; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + allMediaKitZip?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + } | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + mediaKitList?: Array<{ __typename?: 'mediaKitPage_mediaKit'; id: string } | null> | null; + mediaKitList_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + mediaKitList_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; +}; + +export const MediaKitPageDocument = gql` + query mediaKitPage( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: directus_files_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: mediaKit_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: directus_files_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: mediaKitPage_mediaKit_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: mediaKitPage_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + $filter7: mediaKitPage_mediaKit_filter + $sort7: [String] + $limit7: Int + $offset7: Int + $page7: Int + $search7: String + ) { + mediaKitPage { + allMediaKitZip(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + content + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter7, sort: $sort7, limit: $limit7, offset: $offset7, page: $page7, search: $search7) { + id + mediaKit_id(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + backgroundColor + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + mediaKitPage_id( + filter: $filter6 + sort: $sort6 + limit: $limit6 + offset: $offset6 + page: $page6 + search: $search6 + ) { + allMediaKitZip( + filter: $filter4 + sort: $sort4 + limit: $limit4 + offset: $offset4 + page: $page4 + search: $search4 + ) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + modified_by + modified_on + storage + tags + title + type + uploaded_by + uploaded_on + width + } + content + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList( + filter: $filter5 + sort: $sort5 + limit: $limit5 + offset: $offset5 + page: $page5 + search: $search5 + ) { + id + } + mediaKitList_func { + count + } + } + } + mediaKitList_func { + count + } + } + } +`; + +/** + * __useMediaKitPageQuery__ + * + * To run a query within a React component, call `useMediaKitPageQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKitPageQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKitPageQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * filter7: // value for 'filter7' + * sort7: // value for 'sort7' + * limit7: // value for 'limit7' + * offset7: // value for 'offset7' + * page7: // value for 'page7' + * search7: // value for 'search7' + * }, + * }); + */ +export function useMediaKitPageQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(MediaKitPageDocument, options); +} +export function useMediaKitPageLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(MediaKitPageDocument, options); +} +export type MediaKitPageQueryHookResult = ReturnType; +export type MediaKitPageLazyQueryHookResult = ReturnType; +export type MediaKitPageQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit.ts b/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit.ts new file mode 100644 index 0000000000..7ecddf23e5 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit.ts @@ -0,0 +1,755 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKitPage_MediaKitQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; + filter7?: Types.InputMaybe; + sort7?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit7?: Types.InputMaybe; + offset7?: Types.InputMaybe; + page7?: Types.InputMaybe; + search7?: Types.InputMaybe; + filter8?: Types.InputMaybe; + sort8?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit8?: Types.InputMaybe; + offset8?: Types.InputMaybe; + page8?: Types.InputMaybe; + search8?: Types.InputMaybe; +}>; + +export type MediaKitPage_MediaKitQuery = { + __typename?: 'Query'; + mediaKitPage_mediaKit: Array<{ + __typename?: 'mediaKitPage_mediaKit'; + id: string; + mediaKit_id?: { + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + png?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + } | null; + mediaKitPage_id?: { + __typename?: 'mediaKitPage'; + content: string; + date_created?: any | null; + date_updated?: any | null; + id: string; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + allMediaKitZip?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + mediaKitList?: Array<{ + __typename?: 'mediaKitPage_mediaKit'; + id: string; + mediaKit_id?: { + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + } | null; + mediaKitPage_id?: { + __typename?: 'mediaKitPage'; + content: string; + date_created?: any | null; + date_updated?: any | null; + id: string; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + mediaKitList_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + } | null; + }>; +}; + +export const MediaKitPage_MediaKitDocument = gql` + query mediaKitPage_mediaKit( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: mediaKit_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: directus_files_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: mediaKit_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: mediaKitPage_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: mediaKitPage_mediaKit_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + $filter7: mediaKitPage_filter + $sort7: [String] + $limit7: Int + $offset7: Int + $page7: Int + $search7: String + $filter8: mediaKitPage_mediaKit_filter + $sort8: [String] + $limit8: Int + $offset8: Int + $page8: Int + $search8: String + ) { + mediaKitPage_mediaKit( + filter: $filter8 + sort: $sort8 + limit: $limit8 + offset: $offset8 + page: $page8 + search: $search8 + ) { + id + mediaKit_id(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + backgroundColor + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + mediaKitPage_id( + filter: $filter7 + sort: $sort7 + limit: $limit7 + offset: $offset7 + page: $page7 + search: $search7 + ) { + allMediaKitZip( + filter: $filter3 + sort: $sort3 + limit: $limit3 + offset: $offset3 + page: $page3 + search: $search3 + ) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + content + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6) { + id + mediaKit_id( + filter: $filter4 + sort: $sort4 + limit: $limit4 + offset: $offset4 + page: $page4 + search: $search4 + ) { + backgroundColor + date_created + date_updated + id + index + name + sort + status + user_created + user_updated + } + mediaKitPage_id( + filter: $filter5 + sort: $sort5 + limit: $limit5 + offset: $offset5 + page: $page5 + search: $search5 + ) { + content + date_created + date_updated + id + status + title + user_created + user_updated + mediaKitList_func { + count + } + } + } + } + } + } +`; + +/** + * __useMediaKitPage_MediaKitQuery__ + * + * To run a query within a React component, call `useMediaKitPage_MediaKitQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKitPage_MediaKitQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKitPage_MediaKitQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * filter7: // value for 'filter7' + * sort7: // value for 'sort7' + * limit7: // value for 'limit7' + * offset7: // value for 'offset7' + * page7: // value for 'page7' + * search7: // value for 'search7' + * filter8: // value for 'filter8' + * sort8: // value for 'sort8' + * limit8: // value for 'limit8' + * offset8: // value for 'offset8' + * page8: // value for 'page8' + * search8: // value for 'search8' + * }, + * }); + */ +export function useMediaKitPage_MediaKitQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + MediaKitPage_MediaKitDocument, + options, + ); +} +export function useMediaKitPage_MediaKitLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + MediaKitPage_MediaKitDocument, + options, + ); +} +export type MediaKitPage_MediaKitQueryHookResult = ReturnType; +export type MediaKitPage_MediaKitLazyQueryHookResult = ReturnType; +export type MediaKitPage_MediaKitQueryResult = Apollo.QueryResult< + MediaKitPage_MediaKitQuery, + MediaKitPage_MediaKitQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_aggregated.ts b/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_aggregated.ts new file mode 100644 index 0000000000..69fffe7da1 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_aggregated.ts @@ -0,0 +1,195 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKitPage_MediaKit_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type MediaKitPage_MediaKit_AggregatedQuery = { + __typename?: 'Query'; + mediaKitPage_mediaKit_aggregated: Array<{ + __typename?: 'mediaKitPage_mediaKit_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_count'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + countDistinct?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_count'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + avg?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + sum?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + avgDistinct?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + sumDistinct?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + min?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + max?: { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: number | null; + mediaKit_id?: number | null; + mediaKitPage_id?: number | null; + } | null; + }>; +}; + +export const MediaKitPage_MediaKit_AggregatedDocument = gql` + query mediaKitPage_mediaKit_aggregated( + $groupBy: [String] + $filter: mediaKitPage_mediaKit_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + mediaKitPage_mediaKit_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + id + mediaKit_id + mediaKitPage_id + } + countDistinct { + id + mediaKit_id + mediaKitPage_id + } + avg { + id + mediaKit_id + mediaKitPage_id + } + sum { + id + mediaKit_id + mediaKitPage_id + } + avgDistinct { + id + mediaKit_id + mediaKitPage_id + } + sumDistinct { + id + mediaKit_id + mediaKitPage_id + } + min { + id + mediaKit_id + mediaKitPage_id + } + max { + id + mediaKit_id + mediaKitPage_id + } + } + } +`; + +/** + * __useMediaKitPage_MediaKit_AggregatedQuery__ + * + * To run a query within a React component, call `useMediaKitPage_MediaKit_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKitPage_MediaKit_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKitPage_MediaKit_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useMediaKitPage_MediaKit_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions< + MediaKitPage_MediaKit_AggregatedQuery, + MediaKitPage_MediaKit_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + MediaKitPage_MediaKit_AggregatedDocument, + options, + ); +} +export function useMediaKitPage_MediaKit_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + MediaKitPage_MediaKit_AggregatedQuery, + MediaKitPage_MediaKit_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + MediaKitPage_MediaKit_AggregatedDocument, + options, + ); +} +export type MediaKitPage_MediaKit_AggregatedQueryHookResult = ReturnType< + typeof useMediaKitPage_MediaKit_AggregatedQuery +>; +export type MediaKitPage_MediaKit_AggregatedLazyQueryHookResult = ReturnType< + typeof useMediaKitPage_MediaKit_AggregatedLazyQuery +>; +export type MediaKitPage_MediaKit_AggregatedQueryResult = Apollo.QueryResult< + MediaKitPage_MediaKit_AggregatedQuery, + MediaKitPage_MediaKit_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_by_id.ts b/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_by_id.ts new file mode 100644 index 0000000000..ae8ba901d1 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKitPage_mediaKit_by_id.ts @@ -0,0 +1,734 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKitPage_MediaKit_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; + filter7?: Types.InputMaybe; + sort7?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit7?: Types.InputMaybe; + offset7?: Types.InputMaybe; + page7?: Types.InputMaybe; + search7?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type MediaKitPage_MediaKit_By_IdQuery = { + __typename?: 'Query'; + mediaKitPage_mediaKit_by_id?: { + __typename?: 'mediaKitPage_mediaKit'; + id: string; + mediaKit_id?: { + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + png?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + } | null; + mediaKitPage_id?: { + __typename?: 'mediaKitPage'; + content: string; + date_created?: any | null; + date_updated?: any | null; + id: string; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + allMediaKitZip?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + mediaKitList?: Array<{ + __typename?: 'mediaKitPage_mediaKit'; + id: string; + mediaKit_id?: { + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + } | null; + mediaKitPage_id?: { + __typename?: 'mediaKitPage'; + content: string; + date_created?: any | null; + date_updated?: any | null; + id: string; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + mediaKitList_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + } | null; + } | null; +}; + +export const MediaKitPage_MediaKit_By_IdDocument = gql` + query mediaKitPage_mediaKit_by_id( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: mediaKit_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: directus_files_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: mediaKit_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: mediaKitPage_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: mediaKitPage_mediaKit_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + $filter7: mediaKitPage_filter + $sort7: [String] + $limit7: Int + $offset7: Int + $page7: Int + $search7: String + $id: ID! + ) { + mediaKitPage_mediaKit_by_id(id: $id) { + id + mediaKit_id(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + backgroundColor + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + mediaKitPage_id( + filter: $filter7 + sort: $sort7 + limit: $limit7 + offset: $offset7 + page: $page7 + search: $search7 + ) { + allMediaKitZip( + filter: $filter3 + sort: $sort3 + limit: $limit3 + offset: $offset3 + page: $page3 + search: $search3 + ) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + content + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6) { + id + mediaKit_id( + filter: $filter4 + sort: $sort4 + limit: $limit4 + offset: $offset4 + page: $page4 + search: $search4 + ) { + backgroundColor + date_created + date_updated + id + index + name + sort + status + user_created + user_updated + } + mediaKitPage_id( + filter: $filter5 + sort: $sort5 + limit: $limit5 + offset: $offset5 + page: $page5 + search: $search5 + ) { + content + date_created + date_updated + id + status + title + user_created + user_updated + mediaKitList_func { + count + } + } + } + } + } + } +`; + +/** + * __useMediaKitPage_MediaKit_By_IdQuery__ + * + * To run a query within a React component, call `useMediaKitPage_MediaKit_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKitPage_MediaKit_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKitPage_MediaKit_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * filter7: // value for 'filter7' + * sort7: // value for 'sort7' + * limit7: // value for 'limit7' + * offset7: // value for 'offset7' + * page7: // value for 'page7' + * search7: // value for 'search7' + * id: // value for 'id' + * }, + * }); + */ +export function useMediaKitPage_MediaKit_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + MediaKitPage_MediaKit_By_IdDocument, + options, + ); +} +export function useMediaKitPage_MediaKit_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + MediaKitPage_MediaKit_By_IdQuery, + MediaKitPage_MediaKit_By_IdQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + MediaKitPage_MediaKit_By_IdDocument, + options, + ); +} +export type MediaKitPage_MediaKit_By_IdQueryHookResult = ReturnType; +export type MediaKitPage_MediaKit_By_IdLazyQueryHookResult = ReturnType; +export type MediaKitPage_MediaKit_By_IdQueryResult = Apollo.QueryResult< + MediaKitPage_MediaKit_By_IdQuery, + MediaKitPage_MediaKit_By_IdQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/mediaKit_aggregated.ts b/packages/graphql/cms/__generated__/hooks/mediaKit_aggregated.ts new file mode 100644 index 0000000000..4af2ea3641 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKit_aggregated.ts @@ -0,0 +1,221 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKit_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type MediaKit_AggregatedQuery = { + __typename?: 'Query'; + mediaKit_aggregated: Array<{ + __typename?: 'mediaKit_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'mediaKit_aggregated_count'; + backgroundColor?: number | null; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + name?: number | null; + png?: number | null; + sort?: number | null; + status?: number | null; + svg?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + countDistinct?: { + __typename?: 'mediaKit_aggregated_count'; + backgroundColor?: number | null; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + name?: number | null; + png?: number | null; + sort?: number | null; + status?: number | null; + svg?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + avg?: { + __typename?: 'mediaKit_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + sum?: { + __typename?: 'mediaKit_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + avgDistinct?: { + __typename?: 'mediaKit_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + sumDistinct?: { + __typename?: 'mediaKit_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + min?: { + __typename?: 'mediaKit_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + max?: { + __typename?: 'mediaKit_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + }>; +}; + +export const MediaKit_AggregatedDocument = gql` + query mediaKit_aggregated( + $groupBy: [String] + $filter: mediaKit_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + mediaKit_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + backgroundColor + date_created + date_updated + id + index + name + png + sort + status + svg + user_created + user_updated + } + countDistinct { + backgroundColor + date_created + date_updated + id + index + name + png + sort + status + svg + user_created + user_updated + } + avg { + id + index + sort + } + sum { + id + index + sort + } + avgDistinct { + id + index + sort + } + sumDistinct { + id + index + sort + } + min { + id + index + sort + } + max { + id + index + sort + } + } + } +`; + +/** + * __useMediaKit_AggregatedQuery__ + * + * To run a query within a React component, call `useMediaKit_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKit_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKit_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useMediaKit_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + MediaKit_AggregatedDocument, + options, + ); +} +export function useMediaKit_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + MediaKit_AggregatedDocument, + options, + ); +} +export type MediaKit_AggregatedQueryHookResult = ReturnType; +export type MediaKit_AggregatedLazyQueryHookResult = ReturnType; +export type MediaKit_AggregatedQueryResult = Apollo.QueryResult< + MediaKit_AggregatedQuery, + MediaKit_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/mediaKit_by_id.ts b/packages/graphql/cms/__generated__/hooks/mediaKit_by_id.ts new file mode 100644 index 0000000000..1c0c7175a7 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/mediaKit_by_id.ts @@ -0,0 +1,350 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MediaKit_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type MediaKit_By_IdQuery = { + __typename?: 'Query'; + mediaKit_by_id?: { + __typename?: 'mediaKit'; + backgroundColor?: string | null; + date_created?: any | null; + date_updated?: any | null; + id: string; + index?: number | null; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + png?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + } | null; +}; + +export const MediaKit_By_IdDocument = gql` + query mediaKit_by_id( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $id: ID! + ) { + mediaKit_by_id(id: $id) { + backgroundColor + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + } +`; + +/** + * __useMediaKit_By_IdQuery__ + * + * To run a query within a React component, call `useMediaKit_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useMediaKit_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMediaKit_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * id: // value for 'id' + * }, + * }); + */ +export function useMediaKit_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(MediaKit_By_IdDocument, options); +} +export function useMediaKit_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(MediaKit_By_IdDocument, options); +} +export type MediaKit_By_IdQueryHookResult = ReturnType; +export type MediaKit_By_IdLazyQueryHookResult = ReturnType; +export type MediaKit_By_IdQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/navigationType.ts b/packages/graphql/cms/__generated__/hooks/navigationType.ts new file mode 100644 index 0000000000..49b3d9bee3 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/navigationType.ts @@ -0,0 +1,131 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type NavigationTypeQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; +}>; + +export type NavigationTypeQuery = { + __typename?: 'Query'; + navigationType: Array<{ + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + }>; +}; + +export const NavigationTypeDocument = gql` + query navigationType( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + ) { + navigationType(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + } +`; + +/** + * __useNavigationTypeQuery__ + * + * To run a query within a React component, call `useNavigationTypeQuery` and pass it any options that fit your needs. + * When your component renders, `useNavigationTypeQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useNavigationTypeQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * }, + * }); + */ +export function useNavigationTypeQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(NavigationTypeDocument, options); +} +export function useNavigationTypeLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(NavigationTypeDocument, options); +} +export type NavigationTypeQueryHookResult = ReturnType; +export type NavigationTypeLazyQueryHookResult = ReturnType; +export type NavigationTypeQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/navigationType_aggregated.ts b/packages/graphql/cms/__generated__/hooks/navigationType_aggregated.ts new file mode 100644 index 0000000000..ba2cd5b973 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/navigationType_aggregated.ts @@ -0,0 +1,209 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type NavigationType_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type NavigationType_AggregatedQuery = { + __typename?: 'Query'; + navigationType_aggregated: Array<{ + __typename?: 'navigationType_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'navigationType_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + description?: number | null; + id?: number | null; + sort?: number | null; + status?: number | null; + user_created?: number | null; + user_updated?: number | null; + value?: number | null; + } | null; + countDistinct?: { + __typename?: 'navigationType_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + description?: number | null; + id?: number | null; + sort?: number | null; + status?: number | null; + user_created?: number | null; + user_updated?: number | null; + value?: number | null; + } | null; + avg?: { + __typename?: 'navigationType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + sum?: { + __typename?: 'navigationType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + avgDistinct?: { + __typename?: 'navigationType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + sumDistinct?: { + __typename?: 'navigationType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + min?: { + __typename?: 'navigationType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + max?: { + __typename?: 'navigationType_aggregated_fields'; + id?: number | null; + sort?: number | null; + value?: number | null; + } | null; + }>; +}; + +export const NavigationType_AggregatedDocument = gql` + query navigationType_aggregated( + $groupBy: [String] + $filter: navigationType_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + navigationType_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + countDistinct { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + avg { + id + sort + value + } + sum { + id + sort + value + } + avgDistinct { + id + sort + value + } + sumDistinct { + id + sort + value + } + min { + id + sort + value + } + max { + id + sort + value + } + } + } +`; + +/** + * __useNavigationType_AggregatedQuery__ + * + * To run a query within a React component, call `useNavigationType_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useNavigationType_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useNavigationType_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useNavigationType_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + NavigationType_AggregatedDocument, + options, + ); +} +export function useNavigationType_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + NavigationType_AggregatedDocument, + options, + ); +} +export type NavigationType_AggregatedQueryHookResult = ReturnType; +export type NavigationType_AggregatedLazyQueryHookResult = ReturnType; +export type NavigationType_AggregatedQueryResult = Apollo.QueryResult< + NavigationType_AggregatedQuery, + NavigationType_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/navigationType_by_id.ts b/packages/graphql/cms/__generated__/hooks/navigationType_by_id.ts new file mode 100644 index 0000000000..ae344f267c --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/navigationType_by_id.ts @@ -0,0 +1,123 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type NavigationType_By_IdQueryVariables = Types.Exact<{ + id: Types.Scalars['ID']; +}>; + +export type NavigationType_By_IdQuery = { + __typename?: 'Query'; + navigationType_by_id?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; +}; + +export const NavigationType_By_IdDocument = gql` + query navigationType_by_id($id: ID!) { + navigationType_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + } +`; + +/** + * __useNavigationType_By_IdQuery__ + * + * To run a query within a React component, call `useNavigationType_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useNavigationType_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useNavigationType_By_IdQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useNavigationType_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + NavigationType_By_IdDocument, + options, + ); +} +export function useNavigationType_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + NavigationType_By_IdDocument, + options, + ); +} +export type NavigationType_By_IdQueryHookResult = ReturnType; +export type NavigationType_By_IdLazyQueryHookResult = ReturnType; +export type NavigationType_By_IdQueryResult = Apollo.QueryResult< + NavigationType_By_IdQuery, + NavigationType_By_IdQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/officialSocialMedia.ts b/packages/graphql/cms/__generated__/hooks/officialSocialMedia.ts new file mode 100644 index 0000000000..a8a6281a58 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/officialSocialMedia.ts @@ -0,0 +1,383 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type OfficialSocialMediaQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; +}>; + +export type OfficialSocialMediaQuery = { + __typename?: 'Query'; + officialSocialMedia: Array<{ + __typename?: 'officialSocialMedia'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + link: string; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + activeSvg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + }>; +}; + +export const OfficialSocialMediaDocument = gql` + query officialSocialMedia( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: officialSocialMedia_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + ) { + officialSocialMedia( + filter: $filter2 + sort: $sort2 + limit: $limit2 + offset: $offset2 + page: $page2 + search: $search2 + ) { + activeSvg(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + link + name + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + } +`; + +/** + * __useOfficialSocialMediaQuery__ + * + * To run a query within a React component, call `useOfficialSocialMediaQuery` and pass it any options that fit your needs. + * When your component renders, `useOfficialSocialMediaQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useOfficialSocialMediaQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * }, + * }); + */ +export function useOfficialSocialMediaQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + OfficialSocialMediaDocument, + options, + ); +} +export function useOfficialSocialMediaLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + OfficialSocialMediaDocument, + options, + ); +} +export type OfficialSocialMediaQueryHookResult = ReturnType; +export type OfficialSocialMediaLazyQueryHookResult = ReturnType; +export type OfficialSocialMediaQueryResult = Apollo.QueryResult< + OfficialSocialMediaQuery, + OfficialSocialMediaQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/officialSocialMedia_aggregated.ts b/packages/graphql/cms/__generated__/hooks/officialSocialMedia_aggregated.ts new file mode 100644 index 0000000000..9361ae19e7 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/officialSocialMedia_aggregated.ts @@ -0,0 +1,229 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type OfficialSocialMedia_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type OfficialSocialMedia_AggregatedQuery = { + __typename?: 'Query'; + officialSocialMedia_aggregated: Array<{ + __typename?: 'officialSocialMedia_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'officialSocialMedia_aggregated_count'; + activeSvg?: number | null; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + link?: number | null; + name?: number | null; + sort?: number | null; + status?: number | null; + svg?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + countDistinct?: { + __typename?: 'officialSocialMedia_aggregated_count'; + activeSvg?: number | null; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + link?: number | null; + name?: number | null; + sort?: number | null; + status?: number | null; + svg?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + avg?: { + __typename?: 'officialSocialMedia_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + sum?: { + __typename?: 'officialSocialMedia_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + avgDistinct?: { + __typename?: 'officialSocialMedia_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + sumDistinct?: { + __typename?: 'officialSocialMedia_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + min?: { + __typename?: 'officialSocialMedia_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + max?: { + __typename?: 'officialSocialMedia_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + } | null; + }>; +}; + +export const OfficialSocialMedia_AggregatedDocument = gql` + query officialSocialMedia_aggregated( + $groupBy: [String] + $filter: officialSocialMedia_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + officialSocialMedia_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + activeSvg + date_created + date_updated + id + index + link + name + sort + status + svg + user_created + user_updated + } + countDistinct { + activeSvg + date_created + date_updated + id + index + link + name + sort + status + svg + user_created + user_updated + } + avg { + id + index + sort + } + sum { + id + index + sort + } + avgDistinct { + id + index + sort + } + sumDistinct { + id + index + sort + } + min { + id + index + sort + } + max { + id + index + sort + } + } + } +`; + +/** + * __useOfficialSocialMedia_AggregatedQuery__ + * + * To run a query within a React component, call `useOfficialSocialMedia_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useOfficialSocialMedia_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useOfficialSocialMedia_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useOfficialSocialMedia_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions< + OfficialSocialMedia_AggregatedQuery, + OfficialSocialMedia_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + OfficialSocialMedia_AggregatedDocument, + options, + ); +} +export function useOfficialSocialMedia_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + OfficialSocialMedia_AggregatedQuery, + OfficialSocialMedia_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + OfficialSocialMedia_AggregatedDocument, + options, + ); +} +export type OfficialSocialMedia_AggregatedQueryHookResult = ReturnType; +export type OfficialSocialMedia_AggregatedLazyQueryHookResult = ReturnType< + typeof useOfficialSocialMedia_AggregatedLazyQuery +>; +export type OfficialSocialMedia_AggregatedQueryResult = Apollo.QueryResult< + OfficialSocialMedia_AggregatedQuery, + OfficialSocialMedia_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/officialSocialMedia_by_id.ts b/packages/graphql/cms/__generated__/hooks/officialSocialMedia_by_id.ts new file mode 100644 index 0000000000..35191e37e7 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/officialSocialMedia_by_id.ts @@ -0,0 +1,359 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type OfficialSocialMedia_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type OfficialSocialMedia_By_IdQuery = { + __typename?: 'Query'; + officialSocialMedia_by_id?: { + __typename?: 'officialSocialMedia'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + link: string; + name: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + activeSvg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + svg?: { + __typename?: 'directus_files'; + charset?: string | null; + description?: string | null; + duration?: number | null; + embed?: string | null; + filename_disk?: string | null; + filename_download: string; + filesize?: any | null; + folder?: string | null; + height?: number | null; + id: string; + location?: string | null; + metadata?: any | null; + modified_by?: string | null; + modified_on?: any | null; + storage: string; + tags?: any | null; + title?: string | null; + type?: string | null; + uploaded_by?: string | null; + uploaded_on?: any | null; + width?: number | null; + metadata_func?: { __typename?: 'count_functions'; count?: number | null } | null; + modified_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + tags_func?: { __typename?: 'count_functions'; count?: number | null } | null; + uploaded_on_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + } | null; +}; + +export const OfficialSocialMedia_By_IdDocument = gql` + query officialSocialMedia_by_id( + $filter: directus_files_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: directus_files_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $id: ID! + ) { + officialSocialMedia_by_id(id: $id) { + activeSvg(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + link + name + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func { + count + } + modified_by + modified_on + modified_on_func { + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func { + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func { + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + } +`; + +/** + * __useOfficialSocialMedia_By_IdQuery__ + * + * To run a query within a React component, call `useOfficialSocialMedia_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useOfficialSocialMedia_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useOfficialSocialMedia_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * id: // value for 'id' + * }, + * }); + */ +export function useOfficialSocialMedia_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + OfficialSocialMedia_By_IdDocument, + options, + ); +} +export function useOfficialSocialMedia_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + OfficialSocialMedia_By_IdDocument, + options, + ); +} +export type OfficialSocialMedia_By_IdQueryHookResult = ReturnType; +export type OfficialSocialMedia_By_IdLazyQueryHookResult = ReturnType; +export type OfficialSocialMedia_By_IdQueryResult = Apollo.QueryResult< + OfficialSocialMedia_By_IdQuery, + OfficialSocialMedia_By_IdQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/topMenu.ts b/packages/graphql/cms/__generated__/hooks/topMenu.ts new file mode 100644 index 0000000000..7d86937c37 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/topMenu.ts @@ -0,0 +1,527 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type TopMenuQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; +}>; + +export type TopMenuQuery = { + __typename?: 'Query'; + topMenu: Array<{ + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + children?: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + }>; +}; + +export const TopMenuDocument = gql` + query topMenu( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: navigationType_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: topSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: topMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: topSecondMenu_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: topMenu_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + ) { + topMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func { + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func { + count + } + } + } +`; + +/** + * __useTopMenuQuery__ + * + * To run a query within a React component, call `useTopMenuQuery` and pass it any options that fit your needs. + * When your component renders, `useTopMenuQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopMenuQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * }, + * }); + */ +export function useTopMenuQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(TopMenuDocument, options); +} +export function useTopMenuLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(TopMenuDocument, options); +} +export type TopMenuQueryHookResult = ReturnType; +export type TopMenuLazyQueryHookResult = ReturnType; +export type TopMenuQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/topMenu_aggregated.ts b/packages/graphql/cms/__generated__/hooks/topMenu_aggregated.ts new file mode 100644 index 0000000000..1bf6701d0f --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/topMenu_aggregated.ts @@ -0,0 +1,233 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type TopMenu_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type TopMenu_AggregatedQuery = { + __typename?: 'Query'; + topMenu_aggregated: Array<{ + __typename?: 'topMenu_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'topMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + children?: number | null; + } | null; + countDistinct?: { + __typename?: 'topMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + children?: number | null; + } | null; + avg?: { + __typename?: 'topMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sum?: { + __typename?: 'topMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + avgDistinct?: { + __typename?: 'topMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sumDistinct?: { + __typename?: 'topMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + min?: { + __typename?: 'topMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + max?: { + __typename?: 'topMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + sort?: number | null; + type?: number | null; + } | null; + }>; +}; + +export const TopMenu_AggregatedDocument = gql` + query topMenu_aggregated( + $groupBy: [String] + $filter: topMenu_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + topMenu_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + countDistinct { + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + avg { + id + index + sort + type + } + sum { + id + index + sort + type + } + avgDistinct { + id + index + sort + type + } + sumDistinct { + id + index + sort + type + } + min { + id + index + sort + type + } + max { + id + index + sort + type + } + } + } +`; + +/** + * __useTopMenu_AggregatedQuery__ + * + * To run a query within a React component, call `useTopMenu_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useTopMenu_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopMenu_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useTopMenu_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + TopMenu_AggregatedDocument, + options, + ); +} +export function useTopMenu_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + TopMenu_AggregatedDocument, + options, + ); +} +export type TopMenu_AggregatedQueryHookResult = ReturnType; +export type TopMenu_AggregatedLazyQueryHookResult = ReturnType; +export type TopMenu_AggregatedQueryResult = Apollo.QueryResult< + TopMenu_AggregatedQuery, + TopMenu_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/topMenu_by_id.ts b/packages/graphql/cms/__generated__/hooks/topMenu_by_id.ts new file mode 100644 index 0000000000..bbd7883469 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/topMenu_by_id.ts @@ -0,0 +1,514 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type TopMenu_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type TopMenu_By_IdQuery = { + __typename?: 'Query'; + topMenu_by_id?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + children?: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + } | null> | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; +}; + +export const TopMenu_By_IdDocument = gql` + query topMenu_by_id( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: navigationType_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: topSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: topMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: topSecondMenu_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $id: ID! + ) { + topMenu_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func { + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func { + count + } + } + } +`; + +/** + * __useTopMenu_By_IdQuery__ + * + * To run a query within a React component, call `useTopMenu_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useTopMenu_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopMenu_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * id: // value for 'id' + * }, + * }); + */ +export function useTopMenu_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(TopMenu_By_IdDocument, options); +} +export function useTopMenu_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(TopMenu_By_IdDocument, options); +} +export type TopMenu_By_IdQueryHookResult = ReturnType; +export type TopMenu_By_IdLazyQueryHookResult = ReturnType; +export type TopMenu_By_IdQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/topSecondMenu.ts b/packages/graphql/cms/__generated__/hooks/topSecondMenu.ts new file mode 100644 index 0000000000..c24d951e59 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/topSecondMenu.ts @@ -0,0 +1,527 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type TopSecondMenuQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + filter6?: Types.InputMaybe; + sort6?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit6?: Types.InputMaybe; + offset6?: Types.InputMaybe; + page6?: Types.InputMaybe; + search6?: Types.InputMaybe; +}>; + +export type TopSecondMenuQuery = { + __typename?: 'Query'; + topSecondMenu: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + }>; +}; + +export const TopSecondMenuDocument = gql` + query topSecondMenu( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: topMenu_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: topSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: topMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: navigationType_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $filter6: topSecondMenu_filter + $sort6: [String] + $limit6: Int + $offset6: Int + $page6: Int + $search6: String + ) { + topSecondMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func { + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } +`; + +/** + * __useTopSecondMenuQuery__ + * + * To run a query within a React component, call `useTopSecondMenuQuery` and pass it any options that fit your needs. + * When your component renders, `useTopSecondMenuQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopSecondMenuQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * filter6: // value for 'filter6' + * sort6: // value for 'sort6' + * limit6: // value for 'limit6' + * offset6: // value for 'offset6' + * page6: // value for 'page6' + * search6: // value for 'search6' + * }, + * }); + */ +export function useTopSecondMenuQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(TopSecondMenuDocument, options); +} +export function useTopSecondMenuLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(TopSecondMenuDocument, options); +} +export type TopSecondMenuQueryHookResult = ReturnType; +export type TopSecondMenuLazyQueryHookResult = ReturnType; +export type TopSecondMenuQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/topSecondMenu_aggregated.ts b/packages/graphql/cms/__generated__/hooks/topSecondMenu_aggregated.ts new file mode 100644 index 0000000000..01d998911b --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/topSecondMenu_aggregated.ts @@ -0,0 +1,245 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type TopSecondMenu_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type TopSecondMenu_AggregatedQuery = { + __typename?: 'Query'; + topSecondMenu_aggregated: Array<{ + __typename?: 'topSecondMenu_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'topSecondMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + parent?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + countDistinct?: { + __typename?: 'topSecondMenu_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + index?: number | null; + parent?: number | null; + path?: number | null; + sort?: number | null; + status?: number | null; + title?: number | null; + type?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + avg?: { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sum?: { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + avgDistinct?: { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + sumDistinct?: { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + min?: { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + max?: { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: number | null; + index?: number | null; + parent?: number | null; + sort?: number | null; + type?: number | null; + } | null; + }>; +}; + +export const TopSecondMenu_AggregatedDocument = gql` + query topSecondMenu_aggregated( + $groupBy: [String] + $filter: topSecondMenu_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + topSecondMenu_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + countDistinct { + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + avg { + id + index + parent + sort + type + } + sum { + id + index + parent + sort + type + } + avgDistinct { + id + index + parent + sort + type + } + sumDistinct { + id + index + parent + sort + type + } + min { + id + index + parent + sort + type + } + max { + id + index + parent + sort + type + } + } + } +`; + +/** + * __useTopSecondMenu_AggregatedQuery__ + * + * To run a query within a React component, call `useTopSecondMenu_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useTopSecondMenu_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopSecondMenu_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useTopSecondMenu_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + TopSecondMenu_AggregatedDocument, + options, + ); +} +export function useTopSecondMenu_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + TopSecondMenu_AggregatedDocument, + options, + ); +} +export type TopSecondMenu_AggregatedQueryHookResult = ReturnType; +export type TopSecondMenu_AggregatedLazyQueryHookResult = ReturnType; +export type TopSecondMenu_AggregatedQueryResult = Apollo.QueryResult< + TopSecondMenu_AggregatedQuery, + TopSecondMenu_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/topSecondMenu_by_id.ts b/packages/graphql/cms/__generated__/hooks/topSecondMenu_by_id.ts new file mode 100644 index 0000000000..98a6588905 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/topSecondMenu_by_id.ts @@ -0,0 +1,519 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type TopSecondMenu_By_IdQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + filter1?: Types.InputMaybe; + sort1?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit1?: Types.InputMaybe; + offset1?: Types.InputMaybe; + page1?: Types.InputMaybe; + search1?: Types.InputMaybe; + filter2?: Types.InputMaybe; + sort2?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit2?: Types.InputMaybe; + offset2?: Types.InputMaybe; + page2?: Types.InputMaybe; + search2?: Types.InputMaybe; + filter3?: Types.InputMaybe; + sort3?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit3?: Types.InputMaybe; + offset3?: Types.InputMaybe; + page3?: Types.InputMaybe; + search3?: Types.InputMaybe; + filter4?: Types.InputMaybe; + sort4?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit4?: Types.InputMaybe; + offset4?: Types.InputMaybe; + page4?: Types.InputMaybe; + search4?: Types.InputMaybe; + filter5?: Types.InputMaybe; + sort5?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + limit5?: Types.InputMaybe; + offset5?: Types.InputMaybe; + page5?: Types.InputMaybe; + search5?: Types.InputMaybe; + id: Types.Scalars['ID']; +}>; + +export type TopSecondMenu_By_IdQuery = { + __typename?: 'Query'; + topSecondMenu_by_id?: { + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; + children?: Array<{ + __typename?: 'topSecondMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + parent?: { + __typename?: 'topMenu'; + date_created?: any | null; + date_updated?: any | null; + id: string; + index: number; + path?: string | null; + sort?: number | null; + status?: string | null; + title: string; + user_created?: string | null; + user_updated?: string | null; + children_func?: { __typename?: 'count_functions'; count?: number | null } | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null> | null; + } | null; + type?: { + __typename?: 'navigationType'; + date_created?: any | null; + date_updated?: any | null; + description?: string | null; + id: string; + sort?: number | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + value?: number | null; + } | null; + } | null; +}; + +export const TopSecondMenu_By_IdDocument = gql` + query topSecondMenu_by_id( + $filter: navigationType_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + $filter1: topMenu_filter + $sort1: [String] + $limit1: Int + $offset1: Int + $page1: Int + $search1: String + $filter2: navigationType_filter + $sort2: [String] + $limit2: Int + $offset2: Int + $page2: Int + $search2: String + $filter3: topSecondMenu_filter + $sort3: [String] + $limit3: Int + $offset3: Int + $page3: Int + $search3: String + $filter4: topMenu_filter + $sort4: [String] + $limit4: Int + $offset4: Int + $page4: Int + $search4: String + $filter5: navigationType_filter + $sort5: [String] + $limit5: Int + $offset5: Int + $page5: Int + $search5: String + $id: ID! + ) { + topSecondMenu_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1) { + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func { + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5) { + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } +`; + +/** + * __useTopSecondMenu_By_IdQuery__ + * + * To run a query within a React component, call `useTopSecondMenu_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useTopSecondMenu_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTopSecondMenu_By_IdQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * filter1: // value for 'filter1' + * sort1: // value for 'sort1' + * limit1: // value for 'limit1' + * offset1: // value for 'offset1' + * page1: // value for 'page1' + * search1: // value for 'search1' + * filter2: // value for 'filter2' + * sort2: // value for 'sort2' + * limit2: // value for 'limit2' + * offset2: // value for 'offset2' + * page2: // value for 'page2' + * search2: // value for 'search2' + * filter3: // value for 'filter3' + * sort3: // value for 'sort3' + * limit3: // value for 'limit3' + * offset3: // value for 'offset3' + * page3: // value for 'page3' + * search3: // value for 'search3' + * filter4: // value for 'filter4' + * sort4: // value for 'sort4' + * limit4: // value for 'limit4' + * offset4: // value for 'offset4' + * page4: // value for 'page4' + * search4: // value for 'search4' + * filter5: // value for 'filter5' + * sort5: // value for 'sort5' + * limit5: // value for 'limit5' + * offset5: // value for 'offset5' + * page5: // value for 'page5' + * search5: // value for 'search5' + * id: // value for 'id' + * }, + * }); + */ +export function useTopSecondMenu_By_IdQuery( + baseOptions: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + TopSecondMenu_By_IdDocument, + options, + ); +} +export function useTopSecondMenu_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + TopSecondMenu_By_IdDocument, + options, + ); +} +export type TopSecondMenu_By_IdQueryHookResult = ReturnType; +export type TopSecondMenu_By_IdLazyQueryHookResult = ReturnType; +export type TopSecondMenu_By_IdQueryResult = Apollo.QueryResult< + TopSecondMenu_By_IdQuery, + TopSecondMenu_By_IdQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/operation/queries/bottomMenu.gql b/packages/graphql/cms/__generated__/operation/queries/bottomMenu.gql new file mode 100644 index 0000000000..9a85c432f6 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/bottomMenu.gql @@ -0,0 +1,169 @@ +query bottomMenu($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: navigationType_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: bottomSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: bottomMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: bottomSecondMenu_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: bottomMenu_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String){ + bottomMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func{ + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func{ + count + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/bottomMenu_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/bottomMenu_aggregated.gql new file mode 100644 index 0000000000..b19961e162 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/bottomMenu_aggregated.gql @@ -0,0 +1,70 @@ +query bottomMenu_aggregated($groupBy: [String], $filter: bottomMenu_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + bottomMenu_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + countDistinct{ + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + avg{ + id + index + sort + type + } + sum{ + id + index + sort + type + } + avgDistinct{ + id + index + sort + type + } + sumDistinct{ + id + index + sort + type + } + min{ + id + index + sort + type + } + max{ + id + index + sort + type + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/bottomMenu_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/bottomMenu_by_id.gql new file mode 100644 index 0000000000..9dc0f42c65 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/bottomMenu_by_id.gql @@ -0,0 +1,169 @@ +query bottomMenu_by_id($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: navigationType_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: bottomSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: bottomMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: bottomSecondMenu_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $id: ID!){ + bottomMenu_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func{ + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func{ + count + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu.gql b/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu.gql new file mode 100644 index 0000000000..bd1ea985b8 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu.gql @@ -0,0 +1,166 @@ +query bottomSecondMenu($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: bottomMenu_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: bottomSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: bottomMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: navigationType_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: bottomSecondMenu_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String){ + bottomSecondMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func{ + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_aggregated.gql new file mode 100644 index 0000000000..f7245ccdce --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_aggregated.gql @@ -0,0 +1,76 @@ +query bottomSecondMenu_aggregated($groupBy: [String], $filter: bottomSecondMenu_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + bottomSecondMenu_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + countDistinct{ + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + avg{ + id + index + parent + sort + type + } + sum{ + id + index + parent + sort + type + } + avgDistinct{ + id + index + parent + sort + type + } + sumDistinct{ + id + index + parent + sort + type + } + min{ + id + index + parent + sort + type + } + max{ + id + index + parent + sort + type + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_by_id.gql new file mode 100644 index 0000000000..0b6d4629f6 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/bottomSecondMenu_by_id.gql @@ -0,0 +1,166 @@ +query bottomSecondMenu_by_id($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: bottomMenu_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: bottomSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: bottomMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: navigationType_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $id: ID!){ + bottomSecondMenu_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func{ + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/deviceBrand.gql b/packages/graphql/cms/__generated__/operation/queries/deviceBrand.gql new file mode 100644 index 0000000000..b9f88129b9 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/deviceBrand.gql @@ -0,0 +1,33 @@ +query deviceBrand($filter: deviceBrand_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String){ + deviceBrand(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/deviceBrand_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/deviceBrand_aggregated.gql new file mode 100644 index 0000000000..ae4c20879e --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/deviceBrand_aggregated.gql @@ -0,0 +1,52 @@ +query deviceBrand_aggregated($groupBy: [String], $filter: deviceBrand_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + deviceBrand_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + countDistinct{ + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + avg{ + id + sort + } + sum{ + id + sort + } + avgDistinct{ + id + sort + } + sumDistinct{ + id + sort + } + min{ + id + sort + } + max{ + id + sort + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/deviceBrand_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/deviceBrand_by_id.gql new file mode 100644 index 0000000000..33ba70b41a --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/deviceBrand_by_id.gql @@ -0,0 +1,33 @@ +query deviceBrand_by_id($id: ID!){ + deviceBrand_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/deviceType.gql b/packages/graphql/cms/__generated__/operation/queries/deviceType.gql new file mode 100644 index 0000000000..5b83d5f9df --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/deviceType.gql @@ -0,0 +1,33 @@ +query deviceType($filter: deviceType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String){ + deviceType(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/deviceType_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/deviceType_aggregated.gql new file mode 100644 index 0000000000..f57cf08514 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/deviceType_aggregated.gql @@ -0,0 +1,58 @@ +query deviceType_aggregated($groupBy: [String], $filter: deviceType_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + deviceType_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + countDistinct{ + date_created + date_updated + id + label + sort + status + user_created + user_updated + value + } + avg{ + id + sort + value + } + sum{ + id + sort + value + } + avgDistinct{ + id + sort + value + } + sumDistinct{ + id + sort + value + } + min{ + id + sort + value + } + max{ + id + sort + value + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/deviceType_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/deviceType_by_id.gql new file mode 100644 index 0000000000..2b247a1f7e --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/deviceType_by_id.gql @@ -0,0 +1,33 @@ +query deviceType_by_id($id: ID!){ + deviceType_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + label + sort + status + user_created + user_updated + value + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/discoverGroup.gql b/packages/graphql/cms/__generated__/operation/queries/discoverGroup.gql index 31b32da3c4..8a9ca84628 100644 --- a/packages/graphql/cms/__generated__/operation/queries/discoverGroup.gql +++ b/packages/graphql/cms/__generated__/operation/queries/discoverGroup.gql @@ -141,6 +141,7 @@ query discoverGroup($filter: directus_files_filter, $sort: [String], $limit: Int sort status title + url user_created user_updated } @@ -153,6 +154,7 @@ query discoverGroup($filter: directus_files_filter, $sort: [String], $limit: Int sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/operation/queries/discoverGroup_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/discoverGroup_by_id.gql index 86ff875103..f59e38a799 100644 --- a/packages/graphql/cms/__generated__/operation/queries/discoverGroup_by_id.gql +++ b/packages/graphql/cms/__generated__/operation/queries/discoverGroup_by_id.gql @@ -141,6 +141,7 @@ query discoverGroup_by_id($filter: directus_files_filter, $sort: [String], $limi sort status title + url user_created user_updated } @@ -153,6 +154,7 @@ query discoverGroup_by_id($filter: directus_files_filter, $sort: [String], $limi sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/operation/queries/discoverItem.gql b/packages/graphql/cms/__generated__/operation/queries/discoverItem.gql index 1dfad5d47c..3876e26be2 100644 --- a/packages/graphql/cms/__generated__/operation/queries/discoverItem.gql +++ b/packages/graphql/cms/__generated__/operation/queries/discoverItem.gql @@ -145,6 +145,7 @@ query discoverItem($filter: discoverGroup_filter, $sort: [String], $limit: Int, sort status title + url user_created user_updated } @@ -177,6 +178,7 @@ query discoverItem($filter: discoverGroup_filter, $sort: [String], $limit: Int, sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/operation/queries/discoverItem_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/discoverItem_aggregated.gql index abc1cc1366..103c2b6191 100644 --- a/packages/graphql/cms/__generated__/operation/queries/discoverItem_aggregated.gql +++ b/packages/graphql/cms/__generated__/operation/queries/discoverItem_aggregated.gql @@ -13,6 +13,7 @@ query discoverItem_aggregated($groupBy: [String], $filter: discoverItem_filter, sort status title + url user_created user_updated } @@ -27,6 +28,7 @@ query discoverItem_aggregated($groupBy: [String], $filter: discoverItem_filter, sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/operation/queries/discoverItem_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/discoverItem_by_id.gql index f88160cce3..fd869fb773 100644 --- a/packages/graphql/cms/__generated__/operation/queries/discoverItem_by_id.gql +++ b/packages/graphql/cms/__generated__/operation/queries/discoverItem_by_id.gql @@ -145,6 +145,7 @@ query discoverItem_by_id($filter: discoverGroup_filter, $sort: [String], $limit: sort status title + url user_created user_updated } @@ -177,6 +178,7 @@ query discoverItem_by_id($filter: discoverGroup_filter, $sort: [String], $limit: sort status title + url user_created user_updated } diff --git a/packages/graphql/cms/__generated__/operation/queries/index.js b/packages/graphql/cms/__generated__/operation/queries/index.js index fd164ef69d..7e76e89f17 100644 --- a/packages/graphql/cms/__generated__/operation/queries/index.js +++ b/packages/graphql/cms/__generated__/operation/queries/index.js @@ -1,12 +1,55 @@ const fs = require('fs'); const path = require('path'); +module.exports.bottomMenu = fs.readFileSync(path.join(__dirname, 'bottomMenu.gql'), 'utf8'); +module.exports.bottomMenu_by_id = fs.readFileSync(path.join(__dirname, 'bottomMenu_by_id.gql'), 'utf8'); +module.exports.bottomMenu_aggregated = fs.readFileSync(path.join(__dirname, 'bottomMenu_aggregated.gql'), 'utf8'); +module.exports.bottomSecondMenu = fs.readFileSync(path.join(__dirname, 'bottomSecondMenu.gql'), 'utf8'); +module.exports.bottomSecondMenu_by_id = fs.readFileSync(path.join(__dirname, 'bottomSecondMenu_by_id.gql'), 'utf8'); +module.exports.bottomSecondMenu_aggregated = fs.readFileSync( + path.join(__dirname, 'bottomSecondMenu_aggregated.gql'), + 'utf8', +); +module.exports.deviceBrand = fs.readFileSync(path.join(__dirname, 'deviceBrand.gql'), 'utf8'); +module.exports.deviceBrand_by_id = fs.readFileSync(path.join(__dirname, 'deviceBrand_by_id.gql'), 'utf8'); +module.exports.deviceBrand_aggregated = fs.readFileSync(path.join(__dirname, 'deviceBrand_aggregated.gql'), 'utf8'); +module.exports.deviceType = fs.readFileSync(path.join(__dirname, 'deviceType.gql'), 'utf8'); +module.exports.deviceType_by_id = fs.readFileSync(path.join(__dirname, 'deviceType_by_id.gql'), 'utf8'); +module.exports.deviceType_aggregated = fs.readFileSync(path.join(__dirname, 'deviceType_aggregated.gql'), 'utf8'); module.exports.discoverGroup = fs.readFileSync(path.join(__dirname, 'discoverGroup.gql'), 'utf8'); module.exports.discoverGroup_by_id = fs.readFileSync(path.join(__dirname, 'discoverGroup_by_id.gql'), 'utf8'); module.exports.discoverGroup_aggregated = fs.readFileSync(path.join(__dirname, 'discoverGroup_aggregated.gql'), 'utf8'); module.exports.discoverItem = fs.readFileSync(path.join(__dirname, 'discoverItem.gql'), 'utf8'); module.exports.discoverItem_by_id = fs.readFileSync(path.join(__dirname, 'discoverItem_by_id.gql'), 'utf8'); module.exports.discoverItem_aggregated = fs.readFileSync(path.join(__dirname, 'discoverItem_aggregated.gql'), 'utf8'); +module.exports.mediaKit = fs.readFileSync(path.join(__dirname, 'mediaKit.gql'), 'utf8'); +module.exports.mediaKit_by_id = fs.readFileSync(path.join(__dirname, 'mediaKit_by_id.gql'), 'utf8'); +module.exports.mediaKit_aggregated = fs.readFileSync(path.join(__dirname, 'mediaKit_aggregated.gql'), 'utf8'); +module.exports.mediaKitPage = fs.readFileSync(path.join(__dirname, 'mediaKitPage.gql'), 'utf8'); +module.exports.mediaKitPage_mediaKit = fs.readFileSync(path.join(__dirname, 'mediaKitPage_mediaKit.gql'), 'utf8'); +module.exports.mediaKitPage_mediaKit_by_id = fs.readFileSync( + path.join(__dirname, 'mediaKitPage_mediaKit_by_id.gql'), + 'utf8', +); +module.exports.mediaKitPage_mediaKit_aggregated = fs.readFileSync( + path.join(__dirname, 'mediaKitPage_mediaKit_aggregated.gql'), + 'utf8', +); +module.exports.navigationType = fs.readFileSync(path.join(__dirname, 'navigationType.gql'), 'utf8'); +module.exports.navigationType_by_id = fs.readFileSync(path.join(__dirname, 'navigationType_by_id.gql'), 'utf8'); +module.exports.navigationType_aggregated = fs.readFileSync( + path.join(__dirname, 'navigationType_aggregated.gql'), + 'utf8', +); +module.exports.officialSocialMedia = fs.readFileSync(path.join(__dirname, 'officialSocialMedia.gql'), 'utf8'); +module.exports.officialSocialMedia_by_id = fs.readFileSync( + path.join(__dirname, 'officialSocialMedia_by_id.gql'), + 'utf8', +); +module.exports.officialSocialMedia_aggregated = fs.readFileSync( + path.join(__dirname, 'officialSocialMedia_aggregated.gql'), + 'utf8', +); module.exports.socialMedia = fs.readFileSync(path.join(__dirname, 'socialMedia.gql'), 'utf8'); module.exports.socialMedia_by_id = fs.readFileSync(path.join(__dirname, 'socialMedia_by_id.gql'), 'utf8'); module.exports.socialMedia_aggregated = fs.readFileSync(path.join(__dirname, 'socialMedia_aggregated.gql'), 'utf8'); @@ -16,3 +59,9 @@ module.exports.tabMenu_aggregated = fs.readFileSync(path.join(__dirname, 'tabMen module.exports.tabType = fs.readFileSync(path.join(__dirname, 'tabType.gql'), 'utf8'); module.exports.tabType_by_id = fs.readFileSync(path.join(__dirname, 'tabType_by_id.gql'), 'utf8'); module.exports.tabType_aggregated = fs.readFileSync(path.join(__dirname, 'tabType_aggregated.gql'), 'utf8'); +module.exports.topMenu = fs.readFileSync(path.join(__dirname, 'topMenu.gql'), 'utf8'); +module.exports.topMenu_by_id = fs.readFileSync(path.join(__dirname, 'topMenu_by_id.gql'), 'utf8'); +module.exports.topMenu_aggregated = fs.readFileSync(path.join(__dirname, 'topMenu_aggregated.gql'), 'utf8'); +module.exports.topSecondMenu = fs.readFileSync(path.join(__dirname, 'topSecondMenu.gql'), 'utf8'); +module.exports.topSecondMenu_by_id = fs.readFileSync(path.join(__dirname, 'topSecondMenu_by_id.gql'), 'utf8'); +module.exports.topSecondMenu_aggregated = fs.readFileSync(path.join(__dirname, 'topSecondMenu_aggregated.gql'), 'utf8'); diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKit.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKit.gql new file mode 100644 index 0000000000..8b1d138002 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKit.gql @@ -0,0 +1,132 @@ +query mediaKit($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: mediaKit_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String){ + mediaKit(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + backgroundColor + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKitPage.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage.gql new file mode 100644 index 0000000000..91fed78aee --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage.gql @@ -0,0 +1,276 @@ +query mediaKitPage($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: directus_files_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: mediaKit_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: directus_files_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: mediaKitPage_mediaKit_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: mediaKitPage_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String, $filter7: mediaKitPage_mediaKit_filter, $sort7: [String], $limit7: Int, $offset7: Int, $page7: Int, $search7: String){ + mediaKitPage{ + allMediaKitZip(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + content + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter7, sort: $sort7, limit: $limit7, offset: $offset7, page: $page7, search: $search7){ + id + mediaKit_id(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + backgroundColor + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + mediaKitPage_id(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + allMediaKitZip(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + modified_by + modified_on + storage + tags + title + type + uploaded_by + uploaded_on + width + } + content + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + id + } + mediaKitList_func{ + count + } + } + } + mediaKitList_func{ + count + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit.gql new file mode 100644 index 0000000000..137b2169d8 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit.gql @@ -0,0 +1,242 @@ +query mediaKitPage_mediaKit($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: mediaKit_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: directus_files_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: mediaKit_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: mediaKitPage_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: mediaKitPage_mediaKit_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String, $filter7: mediaKitPage_filter, $sort7: [String], $limit7: Int, $offset7: Int, $page7: Int, $search7: String, $filter8: mediaKitPage_mediaKit_filter, $sort8: [String], $limit8: Int, $offset8: Int, $page8: Int, $search8: String){ + mediaKitPage_mediaKit(filter: $filter8, sort: $sort8, limit: $limit8, offset: $offset8, page: $page8, search: $search8){ + id + mediaKit_id(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + backgroundColor + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + mediaKitPage_id(filter: $filter7, sort: $sort7, limit: $limit7, offset: $offset7, page: $page7, search: $search7){ + allMediaKitZip(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + content + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + id + mediaKit_id(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + backgroundColor + date_created + date_updated + id + index + name + sort + status + user_created + user_updated + } + mediaKitPage_id(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + content + date_created + date_updated + id + status + title + user_created + user_updated + mediaKitList_func{ + count + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_aggregated.gql new file mode 100644 index 0000000000..acddaa762b --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_aggregated.gql @@ -0,0 +1,46 @@ +query mediaKitPage_mediaKit_aggregated($groupBy: [String], $filter: mediaKitPage_mediaKit_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + mediaKitPage_mediaKit_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + id + mediaKit_id + mediaKitPage_id + } + countDistinct{ + id + mediaKit_id + mediaKitPage_id + } + avg{ + id + mediaKit_id + mediaKitPage_id + } + sum{ + id + mediaKit_id + mediaKitPage_id + } + avgDistinct{ + id + mediaKit_id + mediaKitPage_id + } + sumDistinct{ + id + mediaKit_id + mediaKitPage_id + } + min{ + id + mediaKit_id + mediaKitPage_id + } + max{ + id + mediaKit_id + mediaKitPage_id + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_by_id.gql new file mode 100644 index 0000000000..5b4bad6062 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKitPage_mediaKit_by_id.gql @@ -0,0 +1,242 @@ +query mediaKitPage_mediaKit_by_id($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: mediaKit_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: directus_files_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: mediaKit_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: mediaKitPage_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: mediaKitPage_mediaKit_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String, $filter7: mediaKitPage_filter, $sort7: [String], $limit7: Int, $offset7: Int, $page7: Int, $search7: String, $id: ID!){ + mediaKitPage_mediaKit_by_id(id: $id){ + id + mediaKit_id(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + backgroundColor + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } + mediaKitPage_id(filter: $filter7, sort: $sort7, limit: $limit7, offset: $offset7, page: $page7, search: $search7){ + allMediaKitZip(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + content + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + status + title + user_created + user_updated + mediaKitList(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + id + mediaKit_id(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + backgroundColor + date_created + date_updated + id + index + name + sort + status + user_created + user_updated + } + mediaKitPage_id(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + content + date_created + date_updated + id + status + title + user_created + user_updated + mediaKitList_func{ + count + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKit_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKit_aggregated.gql new file mode 100644 index 0000000000..e4c6bc6b2c --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKit_aggregated.gql @@ -0,0 +1,64 @@ +query mediaKit_aggregated($groupBy: [String], $filter: mediaKit_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + mediaKit_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + backgroundColor + date_created + date_updated + id + index + name + png + sort + status + svg + user_created + user_updated + } + countDistinct{ + backgroundColor + date_created + date_updated + id + index + name + png + sort + status + svg + user_created + user_updated + } + avg{ + id + index + sort + } + sum{ + id + index + sort + } + avgDistinct{ + id + index + sort + } + sumDistinct{ + id + index + sort + } + min{ + id + index + sort + } + max{ + id + index + sort + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/mediaKit_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/mediaKit_by_id.gql new file mode 100644 index 0000000000..ae96fc60ea --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/mediaKit_by_id.gql @@ -0,0 +1,132 @@ +query mediaKit_by_id($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $id: ID!){ + mediaKit_by_id(id: $id){ + backgroundColor + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + name + png(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/navigationType.gql b/packages/graphql/cms/__generated__/operation/queries/navigationType.gql new file mode 100644 index 0000000000..63f5fc602f --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/navigationType.gql @@ -0,0 +1,33 @@ +query navigationType($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String){ + navigationType(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/navigationType_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/navigationType_aggregated.gql new file mode 100644 index 0000000000..b0a168cafd --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/navigationType_aggregated.gql @@ -0,0 +1,58 @@ +query navigationType_aggregated($groupBy: [String], $filter: navigationType_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + navigationType_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + countDistinct{ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + avg{ + id + sort + value + } + sum{ + id + sort + value + } + avgDistinct{ + id + sort + value + } + sumDistinct{ + id + sort + value + } + min{ + id + sort + value + } + max{ + id + sort + value + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/navigationType_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/navigationType_by_id.gql new file mode 100644 index 0000000000..4af0a9caef --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/navigationType_by_id.gql @@ -0,0 +1,33 @@ +query navigationType_by_id($id: ID!){ + navigationType_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia.gql b/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia.gql new file mode 100644 index 0000000000..a1e14547cd --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia.gql @@ -0,0 +1,132 @@ +query officialSocialMedia($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: officialSocialMedia_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String){ + officialSocialMedia(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + activeSvg(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + link + name + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_aggregated.gql new file mode 100644 index 0000000000..198e4a3313 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_aggregated.gql @@ -0,0 +1,64 @@ +query officialSocialMedia_aggregated($groupBy: [String], $filter: officialSocialMedia_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + officialSocialMedia_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + activeSvg + date_created + date_updated + id + index + link + name + sort + status + svg + user_created + user_updated + } + countDistinct{ + activeSvg + date_created + date_updated + id + index + link + name + sort + status + svg + user_created + user_updated + } + avg{ + id + index + sort + } + sum{ + id + index + sort + } + avgDistinct{ + id + index + sort + } + sumDistinct{ + id + index + sort + } + min{ + id + index + sort + } + max{ + id + index + sort + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_by_id.gql new file mode 100644 index 0000000000..31bb57f837 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/officialSocialMedia_by_id.gql @@ -0,0 +1,132 @@ +query officialSocialMedia_by_id($filter: directus_files_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: directus_files_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $id: ID!){ + officialSocialMedia_by_id(id: $id){ + activeSvg(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + link + name + sort + status + svg(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + charset + description + duration + embed + filename_disk + filename_download + filesize + folder + height + id + location + metadata + metadata_func{ + count + } + modified_by + modified_on + modified_on_func{ + year + month + week + day + weekday + hour + minute + second + } + storage + tags + tags_func{ + count + } + title + type + uploaded_by + uploaded_on + uploaded_on_func{ + year + month + week + day + weekday + hour + minute + second + } + width + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/topMenu.gql b/packages/graphql/cms/__generated__/operation/queries/topMenu.gql new file mode 100644 index 0000000000..9191497910 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/topMenu.gql @@ -0,0 +1,169 @@ +query topMenu($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: navigationType_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: topSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: topMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: topSecondMenu_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: topMenu_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String){ + topMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func{ + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func{ + count + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/topMenu_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/topMenu_aggregated.gql new file mode 100644 index 0000000000..f78f4146c2 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/topMenu_aggregated.gql @@ -0,0 +1,70 @@ +query topMenu_aggregated($groupBy: [String], $filter: topMenu_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + topMenu_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + countDistinct{ + date_created + date_updated + id + index + path + sort + status + title + type + user_created + user_updated + children + } + avg{ + id + index + sort + type + } + sum{ + id + index + sort + type + } + avgDistinct{ + id + index + sort + type + } + sumDistinct{ + id + index + sort + type + } + min{ + id + index + sort + type + } + max{ + id + index + sort + type + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/topMenu_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/topMenu_by_id.gql new file mode 100644 index 0000000000..eff53a67f6 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/topMenu_by_id.gql @@ -0,0 +1,169 @@ +query topMenu_by_id($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: navigationType_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: topSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: topMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: topSecondMenu_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $id: ID!){ + topMenu_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_updated + id + index + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + children_func{ + count + } + } + path + sort + status + title + user_created + user_updated + } + children_func{ + count + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/topSecondMenu.gql b/packages/graphql/cms/__generated__/operation/queries/topSecondMenu.gql new file mode 100644 index 0000000000..ea6a129416 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/topSecondMenu.gql @@ -0,0 +1,166 @@ +query topSecondMenu($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: topMenu_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: topSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: topMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: navigationType_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $filter6: topSecondMenu_filter, $sort6: [String], $limit6: Int, $offset6: Int, $page6: Int, $search6: String){ + topSecondMenu(filter: $filter6, sort: $sort6, limit: $limit6, offset: $offset6, page: $page6, search: $search6){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func{ + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/topSecondMenu_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/topSecondMenu_aggregated.gql new file mode 100644 index 0000000000..6a56e0de95 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/topSecondMenu_aggregated.gql @@ -0,0 +1,76 @@ +query topSecondMenu_aggregated($groupBy: [String], $filter: topSecondMenu_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + topSecondMenu_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + countDistinct{ + date_created + date_updated + id + index + parent + path + sort + status + title + type + user_created + user_updated + } + avg{ + id + index + parent + sort + type + } + sum{ + id + index + parent + sort + type + } + avgDistinct{ + id + index + parent + sort + type + } + sumDistinct{ + id + index + parent + sort + type + } + min{ + id + index + parent + sort + type + } + max{ + id + index + parent + sort + type + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/topSecondMenu_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/topSecondMenu_by_id.gql new file mode 100644 index 0000000000..e5bdf12f7d --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/topSecondMenu_by_id.gql @@ -0,0 +1,166 @@ +query topSecondMenu_by_id($filter: navigationType_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String, $filter1: topMenu_filter, $sort1: [String], $limit1: Int, $offset1: Int, $page1: Int, $search1: String, $filter2: navigationType_filter, $sort2: [String], $limit2: Int, $offset2: Int, $page2: Int, $search2: String, $filter3: topSecondMenu_filter, $sort3: [String], $limit3: Int, $offset3: Int, $page3: Int, $search3: String, $filter4: topMenu_filter, $sort4: [String], $limit4: Int, $offset4: Int, $page4: Int, $search4: String, $filter5: navigationType_filter, $sort5: [String], $limit5: Int, $offset5: Int, $page5: Int, $search5: String, $id: ID!){ + topSecondMenu_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter4, sort: $sort4, limit: $limit4, offset: $offset4, page: $page4, search: $search4){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + path + sort + status + title + type(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + children(filter: $filter3, sort: $sort3, limit: $limit3, offset: $offset3, page: $page3, search: $search3){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + index + parent(filter: $filter1, sort: $sort1, limit: $limit1, offset: $offset1, page: $page1, search: $search1){ + date_created + date_updated + id + index + path + sort + status + title + user_created + user_updated + children_func{ + count + } + } + path + sort + status + title + type(filter: $filter2, sort: $sort2, limit: $limit2, offset: $offset2, page: $page2, search: $search2){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } + } + path + sort + status + title + type(filter: $filter5, sort: $sort5, limit: $limit5, offset: $offset5, page: $page5, search: $search5){ + date_created + date_updated + description + id + sort + status + user_created + user_updated + value + } + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/types.ts b/packages/graphql/cms/__generated__/types.ts index 1de7a9398c..db4b9aa6e2 100644 --- a/packages/graphql/cms/__generated__/types.ts +++ b/packages/graphql/cms/__generated__/types.ts @@ -22,12 +22,37 @@ export type Scalars = { export type Query = { __typename?: 'Query'; + bottomMenu: Array; + bottomMenu_aggregated: Array; + bottomMenu_by_id?: Maybe; + bottomSecondMenu: Array; + bottomSecondMenu_aggregated: Array; + bottomSecondMenu_by_id?: Maybe; + deviceBrand: Array; + deviceBrand_aggregated: Array; + deviceBrand_by_id?: Maybe; + deviceType: Array; + deviceType_aggregated: Array; + deviceType_by_id?: Maybe; discoverGroup: Array; discoverGroup_aggregated: Array; discoverGroup_by_id?: Maybe; discoverItem: Array; discoverItem_aggregated: Array; discoverItem_by_id?: Maybe; + mediaKit: Array; + mediaKitPage?: Maybe; + mediaKitPage_mediaKit: Array; + mediaKitPage_mediaKit_aggregated: Array; + mediaKitPage_mediaKit_by_id?: Maybe; + mediaKit_aggregated: Array; + mediaKit_by_id?: Maybe; + navigationType: Array; + navigationType_aggregated: Array; + navigationType_by_id?: Maybe; + officialSocialMedia: Array; + officialSocialMedia_aggregated: Array; + officialSocialMedia_by_id?: Maybe; socialMedia: Array; socialMedia_aggregated: Array; socialMedia_by_id?: Maybe; @@ -37,6 +62,104 @@ export type Query = { tabType: Array; tabType_aggregated: Array; tabType_by_id?: Maybe; + topMenu: Array; + topMenu_aggregated: Array; + topMenu_by_id?: Maybe; + topSecondMenu: Array; + topSecondMenu_aggregated: Array; + topSecondMenu_by_id?: Maybe; +}; + +export type QueryBottomMenuArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryBottomMenu_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryBottomMenu_By_IdArgs = { + id: Scalars['ID']; +}; + +export type QueryBottomSecondMenuArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryBottomSecondMenu_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryBottomSecondMenu_By_IdArgs = { + id: Scalars['ID']; +}; + +export type QueryDeviceBrandArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryDeviceBrand_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryDeviceBrand_By_IdArgs = { + id: Scalars['ID']; +}; + +export type QueryDeviceTypeArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryDeviceType_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryDeviceType_By_IdArgs = { + id: Scalars['ID']; }; export type QueryDiscoverGroupArgs = { @@ -85,6 +208,98 @@ export type QueryDiscoverItem_By_IdArgs = { id: Scalars['ID']; }; +export type QueryMediaKitArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryMediaKitPage_MediaKitArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryMediaKitPage_MediaKit_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryMediaKitPage_MediaKit_By_IdArgs = { + id: Scalars['ID']; +}; + +export type QueryMediaKit_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryMediaKit_By_IdArgs = { + id: Scalars['ID']; +}; + +export type QueryNavigationTypeArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryNavigationType_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryNavigationType_By_IdArgs = { + id: Scalars['ID']; +}; + +export type QueryOfficialSocialMediaArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryOfficialSocialMedia_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryOfficialSocialMedia_By_IdArgs = { + id: Scalars['ID']; +}; + export type QuerySocialMediaArgs = { filter?: InputMaybe; limit?: InputMaybe; @@ -154,211 +369,958 @@ export type QueryTabType_By_IdArgs = { id: Scalars['ID']; }; -export type Count_Function_Filter_Operators = { - count?: InputMaybe; +export type QueryTopMenuArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; }; -export type Count_Functions = { - __typename?: 'count_functions'; - count?: Maybe; +export type QueryTopMenu_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; }; -export type Date_Filter_Operators = { - _between?: InputMaybe>>; - _eq?: InputMaybe; - _gt?: InputMaybe; - _gte?: InputMaybe; - _in?: InputMaybe>>; - _lt?: InputMaybe; - _lte?: InputMaybe; - _nbetween?: InputMaybe>>; - _neq?: InputMaybe; - _nin?: InputMaybe>>; - _nnull?: InputMaybe; - _null?: InputMaybe; +export type QueryTopMenu_By_IdArgs = { + id: Scalars['ID']; }; -export type Datetime_Function_Filter_Operators = { - day?: InputMaybe; - hour?: InputMaybe; - minute?: InputMaybe; - month?: InputMaybe; - second?: InputMaybe; - week?: InputMaybe; - weekday?: InputMaybe; - year?: InputMaybe; +export type QueryTopSecondMenuArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; }; -export type Datetime_Functions = { - __typename?: 'datetime_functions'; - day?: Maybe; - hour?: Maybe; - minute?: Maybe; - month?: Maybe; - second?: Maybe; - week?: Maybe; - weekday?: Maybe; - year?: Maybe; +export type QueryTopSecondMenu_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; }; -export type Directus_Files = { - __typename?: 'directus_files'; - charset?: Maybe; - description?: Maybe; - duration?: Maybe; - embed?: Maybe; - filename_disk?: Maybe; - filename_download: Scalars['String']; - filesize?: Maybe; - folder?: Maybe; - height?: Maybe; +export type QueryTopSecondMenu_By_IdArgs = { id: Scalars['ID']; - location?: Maybe; - metadata?: Maybe; - metadata_func?: Maybe; - modified_by?: Maybe; - modified_on?: Maybe; - modified_on_func?: Maybe; - storage: Scalars['String']; - tags?: Maybe; - tags_func?: Maybe; - title?: Maybe; - type?: Maybe; - uploaded_by?: Maybe; - uploaded_on?: Maybe; - uploaded_on_func?: Maybe; - width?: Maybe; }; -export type Directus_Files_Filter = { - _and?: InputMaybe>>; - _or?: InputMaybe>>; - charset?: InputMaybe; - description?: InputMaybe; - duration?: InputMaybe; - embed?: InputMaybe; - filename_disk?: InputMaybe; - filename_download?: InputMaybe; - filesize?: InputMaybe; - folder?: InputMaybe; - height?: InputMaybe; - id?: InputMaybe; - location?: InputMaybe; - metadata?: InputMaybe; - metadata_func?: InputMaybe; - modified_by?: InputMaybe; - modified_on?: InputMaybe; - modified_on_func?: InputMaybe; - storage?: InputMaybe; - tags?: InputMaybe; - tags_func?: InputMaybe; - title?: InputMaybe; - type?: InputMaybe; - uploaded_by?: InputMaybe; - uploaded_on?: InputMaybe; +export type BottomMenu = { + __typename?: 'bottomMenu'; + children?: Maybe>>; + children_func?: Maybe; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + index: Scalars['Int']; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title: Scalars['String']; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type BottomMenuChildrenArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type BottomMenuTypeArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type BottomMenu_Aggregated = { + __typename?: 'bottomMenu_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type BottomMenu_Aggregated_Count = { + __typename?: 'bottomMenu_aggregated_count'; + children?: Maybe; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + index?: Maybe; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type BottomMenu_Aggregated_Fields = { + __typename?: 'bottomMenu_aggregated_fields'; + id?: Maybe; + index?: Maybe; + sort?: Maybe; + type?: Maybe; +}; + +export type BottomMenu_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + children?: InputMaybe; + children_func?: InputMaybe; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + index?: InputMaybe; + path?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + type?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type BottomSecondMenu = { + __typename?: 'bottomSecondMenu'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + index: Scalars['Int']; + parent?: Maybe; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title: Scalars['String']; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type BottomSecondMenuParentArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type BottomSecondMenuTypeArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type BottomSecondMenu_Aggregated = { + __typename?: 'bottomSecondMenu_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type BottomSecondMenu_Aggregated_Count = { + __typename?: 'bottomSecondMenu_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + index?: Maybe; + parent?: Maybe; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type BottomSecondMenu_Aggregated_Fields = { + __typename?: 'bottomSecondMenu_aggregated_fields'; + id?: Maybe; + index?: Maybe; + parent?: Maybe; + sort?: Maybe; + type?: Maybe; +}; + +export type BottomSecondMenu_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + index?: InputMaybe; + parent?: InputMaybe; + path?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + type?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type Count_Function_Filter_Operators = { + count?: InputMaybe; +}; + +export type Count_Functions = { + __typename?: 'count_functions'; + count?: Maybe; +}; + +export type Date_Filter_Operators = { + _between?: InputMaybe>>; + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>>; + _lt?: InputMaybe; + _lte?: InputMaybe; + _nbetween?: InputMaybe>>; + _neq?: InputMaybe; + _nin?: InputMaybe>>; + _nnull?: InputMaybe; + _null?: InputMaybe; +}; + +export type Datetime_Function_Filter_Operators = { + day?: InputMaybe; + hour?: InputMaybe; + minute?: InputMaybe; + month?: InputMaybe; + second?: InputMaybe; + week?: InputMaybe; + weekday?: InputMaybe; + year?: InputMaybe; +}; + +export type Datetime_Functions = { + __typename?: 'datetime_functions'; + day?: Maybe; + hour?: Maybe; + minute?: Maybe; + month?: Maybe; + second?: Maybe; + week?: Maybe; + weekday?: Maybe; + year?: Maybe; +}; + +export type DeviceBrand = { + __typename?: 'deviceBrand'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + label?: Maybe; + sort?: Maybe; + status?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; + value?: Maybe; +}; + +export type DeviceBrand_Aggregated = { + __typename?: 'deviceBrand_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type DeviceBrand_Aggregated_Count = { + __typename?: 'deviceBrand_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + label?: Maybe; + sort?: Maybe; + status?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; + value?: Maybe; +}; + +export type DeviceBrand_Aggregated_Fields = { + __typename?: 'deviceBrand_aggregated_fields'; + id?: Maybe; + sort?: Maybe; +}; + +export type DeviceBrand_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + label?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; + value?: InputMaybe; +}; + +export type DeviceType = { + __typename?: 'deviceType'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + label?: Maybe; + sort?: Maybe; + status?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; + value?: Maybe; +}; + +export type DeviceType_Aggregated = { + __typename?: 'deviceType_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type DeviceType_Aggregated_Count = { + __typename?: 'deviceType_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + label?: Maybe; + sort?: Maybe; + status?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; + value?: Maybe; +}; + +export type DeviceType_Aggregated_Fields = { + __typename?: 'deviceType_aggregated_fields'; + id?: Maybe; + sort?: Maybe; + value?: Maybe; +}; + +export type DeviceType_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + label?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; + value?: InputMaybe; +}; + +export type Directus_Files = { + __typename?: 'directus_files'; + charset?: Maybe; + description?: Maybe; + duration?: Maybe; + embed?: Maybe; + filename_disk?: Maybe; + filename_download: Scalars['String']; + filesize?: Maybe; + folder?: Maybe; + height?: Maybe; + id: Scalars['ID']; + location?: Maybe; + metadata?: Maybe; + metadata_func?: Maybe; + modified_by?: Maybe; + modified_on?: Maybe; + modified_on_func?: Maybe; + storage: Scalars['String']; + tags?: Maybe; + tags_func?: Maybe; + title?: Maybe; + type?: Maybe; + uploaded_by?: Maybe; + uploaded_on?: Maybe; + uploaded_on_func?: Maybe; + width?: Maybe; +}; + +export type Directus_Files_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + charset?: InputMaybe; + description?: InputMaybe; + duration?: InputMaybe; + embed?: InputMaybe; + filename_disk?: InputMaybe; + filename_download?: InputMaybe; + filesize?: InputMaybe; + folder?: InputMaybe; + height?: InputMaybe; + id?: InputMaybe; + location?: InputMaybe; + metadata?: InputMaybe; + metadata_func?: InputMaybe; + modified_by?: InputMaybe; + modified_on?: InputMaybe; + modified_on_func?: InputMaybe; + storage?: InputMaybe; + tags?: InputMaybe; + tags_func?: InputMaybe; + title?: InputMaybe; + type?: InputMaybe; + uploaded_by?: InputMaybe; + uploaded_on?: InputMaybe; uploaded_on_func?: InputMaybe; width?: InputMaybe; }; -export type DiscoverGroup = { - __typename?: 'discoverGroup'; +export type DiscoverGroup = { + __typename?: 'discoverGroup'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + index?: Maybe; + items?: Maybe>>; + items_func?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type DiscoverGroupItemsArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type DiscoverGroup_Aggregated = { + __typename?: 'discoverGroup_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type DiscoverGroup_Aggregated_Count = { + __typename?: 'discoverGroup_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + index?: Maybe; + items?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type DiscoverGroup_Aggregated_Fields = { + __typename?: 'discoverGroup_aggregated_fields'; + id?: Maybe; + index?: Maybe; + sort?: Maybe; +}; + +export type DiscoverGroup_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + index?: InputMaybe; + items?: InputMaybe; + items_func?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type DiscoverItem = { + __typename?: 'discoverItem'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + description?: Maybe; + group?: Maybe; + id: Scalars['ID']; + imgUrl?: Maybe; + index?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + url?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type DiscoverItemGroupArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type DiscoverItemImgUrlArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type DiscoverItem_Aggregated = { + __typename?: 'discoverItem_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type DiscoverItem_Aggregated_Count = { + __typename?: 'discoverItem_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + description?: Maybe; + group?: Maybe; + id?: Maybe; + /** Not support svg */ + imgUrl?: Maybe; + index?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + url?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type DiscoverItem_Aggregated_Fields = { + __typename?: 'discoverItem_aggregated_fields'; + group?: Maybe; + id?: Maybe; + index?: Maybe; + sort?: Maybe; +}; + +export type DiscoverItem_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + description?: InputMaybe; + group?: InputMaybe; + id?: InputMaybe; + imgUrl?: InputMaybe; + index?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + url?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type MediaKit = { + __typename?: 'mediaKit'; + backgroundColor?: Maybe; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + index?: Maybe; + name: Scalars['String']; + png?: Maybe; + sort?: Maybe; + status?: Maybe; + svg?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type MediaKitPngArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type MediaKitSvgArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type MediaKitPage = { + __typename?: 'mediaKitPage'; + allMediaKitZip?: Maybe; + content: Scalars['String']; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + mediaKitList?: Maybe>>; + mediaKitList_func?: Maybe; + status?: Maybe; + title: Scalars['String']; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type MediaKitPageAllMediaKitZipArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type MediaKitPageMediaKitListArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type MediaKitPage_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + allMediaKitZip?: InputMaybe; + content?: InputMaybe; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + mediaKitList?: InputMaybe; + mediaKitList_func?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type MediaKitPage_MediaKit = { + __typename?: 'mediaKitPage_mediaKit'; + id: Scalars['ID']; + mediaKitPage_id?: Maybe; + mediaKit_id?: Maybe; +}; + +export type MediaKitPage_MediaKitMediaKitPage_IdArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type MediaKitPage_MediaKitMediaKit_IdArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type MediaKitPage_MediaKit_Aggregated = { + __typename?: 'mediaKitPage_mediaKit_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type MediaKitPage_MediaKit_Aggregated_Count = { + __typename?: 'mediaKitPage_mediaKit_aggregated_count'; + id?: Maybe; + mediaKitPage_id?: Maybe; + mediaKit_id?: Maybe; +}; + +export type MediaKitPage_MediaKit_Aggregated_Fields = { + __typename?: 'mediaKitPage_mediaKit_aggregated_fields'; + id?: Maybe; + mediaKitPage_id?: Maybe; + mediaKit_id?: Maybe; +}; + +export type MediaKitPage_MediaKit_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + id?: InputMaybe; + mediaKitPage_id?: InputMaybe; + mediaKit_id?: InputMaybe; +}; + +export type MediaKit_Aggregated = { + __typename?: 'mediaKit_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type MediaKit_Aggregated_Count = { + __typename?: 'mediaKit_aggregated_count'; + backgroundColor?: Maybe; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + index?: Maybe; + name?: Maybe; + png?: Maybe; + sort?: Maybe; + status?: Maybe; + svg?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type MediaKit_Aggregated_Fields = { + __typename?: 'mediaKit_aggregated_fields'; + id?: Maybe; + index?: Maybe; + sort?: Maybe; +}; + +export type MediaKit_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + backgroundColor?: InputMaybe; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + index?: InputMaybe; + name?: InputMaybe; + png?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + svg?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type NavigationType = { + __typename?: 'navigationType'; date_created?: Maybe; date_created_func?: Maybe; date_updated?: Maybe; date_updated_func?: Maybe; + description?: Maybe; id: Scalars['ID']; - index?: Maybe; - items?: Maybe>>; - items_func?: Maybe; sort?: Maybe; status?: Maybe; - title?: Maybe; user_created?: Maybe; user_updated?: Maybe; + value?: Maybe; }; -export type DiscoverGroupItemsArgs = { - filter?: InputMaybe; - limit?: InputMaybe; - offset?: InputMaybe; - page?: InputMaybe; - search?: InputMaybe; - sort?: InputMaybe>>; -}; - -export type DiscoverGroup_Aggregated = { - __typename?: 'discoverGroup_aggregated'; - avg?: Maybe; - avgDistinct?: Maybe; - count?: Maybe; +export type NavigationType_Aggregated = { + __typename?: 'navigationType_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; countAll?: Maybe; - countDistinct?: Maybe; + countDistinct?: Maybe; group?: Maybe; - max?: Maybe; - min?: Maybe; - sum?: Maybe; - sumDistinct?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; }; -export type DiscoverGroup_Aggregated_Count = { - __typename?: 'discoverGroup_aggregated_count'; +export type NavigationType_Aggregated_Count = { + __typename?: 'navigationType_aggregated_count'; date_created?: Maybe; date_updated?: Maybe; + description?: Maybe; id?: Maybe; - index?: Maybe; - items?: Maybe; sort?: Maybe; status?: Maybe; - title?: Maybe; user_created?: Maybe; user_updated?: Maybe; + value?: Maybe; }; -export type DiscoverGroup_Aggregated_Fields = { - __typename?: 'discoverGroup_aggregated_fields'; +export type NavigationType_Aggregated_Fields = { + __typename?: 'navigationType_aggregated_fields'; id?: Maybe; - index?: Maybe; sort?: Maybe; + value?: Maybe; }; -export type DiscoverGroup_Filter = { - _and?: InputMaybe>>; - _or?: InputMaybe>>; +export type NavigationType_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; date_created?: InputMaybe; date_created_func?: InputMaybe; date_updated?: InputMaybe; date_updated_func?: InputMaybe; + description?: InputMaybe; id?: InputMaybe; - index?: InputMaybe; - items?: InputMaybe; - items_func?: InputMaybe; sort?: InputMaybe; status?: InputMaybe; - title?: InputMaybe; user_created?: InputMaybe; user_updated?: InputMaybe; + value?: InputMaybe; }; -export type DiscoverItem = { - __typename?: 'discoverItem'; +export type Number_Filter_Operators = { + _between?: InputMaybe>>; + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>>; + _lt?: InputMaybe; + _lte?: InputMaybe; + _nbetween?: InputMaybe>>; + _neq?: InputMaybe; + _nin?: InputMaybe>>; + _nnull?: InputMaybe; + _null?: InputMaybe; +}; + +export type OfficialSocialMedia = { + __typename?: 'officialSocialMedia'; + activeSvg?: Maybe; date_created?: Maybe; date_created_func?: Maybe; date_updated?: Maybe; date_updated_func?: Maybe; - description?: Maybe; - group?: Maybe; id: Scalars['ID']; - imgUrl?: Maybe; - index?: Maybe; + index: Scalars['Int']; + link: Scalars['String']; + name: Scalars['String']; sort?: Maybe; status?: Maybe; - title?: Maybe; + svg?: Maybe; user_created?: Maybe; user_updated?: Maybe; }; -export type DiscoverItemGroupArgs = { - filter?: InputMaybe; +export type OfficialSocialMediaActiveSvgArgs = { + filter?: InputMaybe; limit?: InputMaybe; offset?: InputMaybe; page?: InputMaybe; @@ -366,7 +1328,7 @@ export type DiscoverItemGroupArgs = { sort?: InputMaybe>>; }; -export type DiscoverItemImgUrlArgs = { +export type OfficialSocialMediaSvgArgs = { filter?: InputMaybe; limit?: InputMaybe; offset?: InputMaybe; @@ -375,79 +1337,62 @@ export type DiscoverItemImgUrlArgs = { sort?: InputMaybe>>; }; -export type DiscoverItem_Aggregated = { - __typename?: 'discoverItem_aggregated'; - avg?: Maybe; - avgDistinct?: Maybe; - count?: Maybe; +export type OfficialSocialMedia_Aggregated = { + __typename?: 'officialSocialMedia_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; countAll?: Maybe; - countDistinct?: Maybe; + countDistinct?: Maybe; group?: Maybe; - max?: Maybe; - min?: Maybe; - sum?: Maybe; - sumDistinct?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; }; -export type DiscoverItem_Aggregated_Count = { - __typename?: 'discoverItem_aggregated_count'; +export type OfficialSocialMedia_Aggregated_Count = { + __typename?: 'officialSocialMedia_aggregated_count'; + activeSvg?: Maybe; date_created?: Maybe; date_updated?: Maybe; - description?: Maybe; - group?: Maybe; id?: Maybe; - /** Not support svg */ - imgUrl?: Maybe; index?: Maybe; + link?: Maybe; + name?: Maybe; sort?: Maybe; status?: Maybe; - title?: Maybe; + svg?: Maybe; user_created?: Maybe; user_updated?: Maybe; }; -export type DiscoverItem_Aggregated_Fields = { - __typename?: 'discoverItem_aggregated_fields'; - group?: Maybe; +export type OfficialSocialMedia_Aggregated_Fields = { + __typename?: 'officialSocialMedia_aggregated_fields'; id?: Maybe; index?: Maybe; sort?: Maybe; }; -export type DiscoverItem_Filter = { - _and?: InputMaybe>>; - _or?: InputMaybe>>; +export type OfficialSocialMedia_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + activeSvg?: InputMaybe; date_created?: InputMaybe; date_created_func?: InputMaybe; date_updated?: InputMaybe; date_updated_func?: InputMaybe; - description?: InputMaybe; - group?: InputMaybe; id?: InputMaybe; - imgUrl?: InputMaybe; index?: InputMaybe; + link?: InputMaybe; + name?: InputMaybe; sort?: InputMaybe; status?: InputMaybe; - title?: InputMaybe; + svg?: InputMaybe; user_created?: InputMaybe; user_updated?: InputMaybe; }; -export type Number_Filter_Operators = { - _between?: InputMaybe>>; - _eq?: InputMaybe; - _gt?: InputMaybe; - _gte?: InputMaybe; - _in?: InputMaybe>>; - _lt?: InputMaybe; - _lte?: InputMaybe; - _nbetween?: InputMaybe>>; - _neq?: InputMaybe; - _nin?: InputMaybe>>; - _nnull?: InputMaybe; - _null?: InputMaybe; -}; - export type SocialMedia = { __typename?: 'socialMedia'; date_created?: Maybe; @@ -688,3 +1633,192 @@ export type TabType_Filter = { user_updated?: InputMaybe; value?: InputMaybe; }; + +export type TopMenu = { + __typename?: 'topMenu'; + children?: Maybe>>; + children_func?: Maybe; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + index: Scalars['Int']; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title: Scalars['String']; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type TopMenuChildrenArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type TopMenuTypeArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type TopMenu_Aggregated = { + __typename?: 'topMenu_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type TopMenu_Aggregated_Count = { + __typename?: 'topMenu_aggregated_count'; + children?: Maybe; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + index?: Maybe; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type TopMenu_Aggregated_Fields = { + __typename?: 'topMenu_aggregated_fields'; + id?: Maybe; + index?: Maybe; + sort?: Maybe; + type?: Maybe; +}; + +export type TopMenu_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + children?: InputMaybe; + children_func?: InputMaybe; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + index?: InputMaybe; + path?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + type?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + +export type TopSecondMenu = { + __typename?: 'topSecondMenu'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + index: Scalars['Int']; + parent?: Maybe; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title: Scalars['String']; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type TopSecondMenuParentArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type TopSecondMenuTypeArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type TopSecondMenu_Aggregated = { + __typename?: 'topSecondMenu_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type TopSecondMenu_Aggregated_Count = { + __typename?: 'topSecondMenu_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + index?: Maybe; + parent?: Maybe; + path?: Maybe; + sort?: Maybe; + status?: Maybe; + title?: Maybe; + type?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type TopSecondMenu_Aggregated_Fields = { + __typename?: 'topSecondMenu_aggregated_fields'; + id?: Maybe; + index?: Maybe; + parent?: Maybe; + sort?: Maybe; + type?: Maybe; +}; + +export type TopSecondMenu_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + index?: InputMaybe; + parent?: InputMaybe; + path?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + title?: InputMaybe; + type?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; diff --git a/packages/graphql/cms/custom/discoverGroupCustom.gql b/packages/graphql/cms/custom/discoverGroupCustom.gql index ce49c4ab01..531db04cfa 100644 --- a/packages/graphql/cms/custom/discoverGroupCustom.gql +++ b/packages/graphql/cms/custom/discoverGroupCustom.gql @@ -23,6 +23,7 @@ query discoverGroupCustom( status title description + url imgUrl { filename_disk } diff --git a/packages/graphql/schema/cms_schema.graphql b/packages/graphql/schema/cms_schema.graphql index 1527038284..671d032536 100644 --- a/packages/graphql/schema/cms_schema.graphql +++ b/packages/graphql/schema/cms_schema.graphql @@ -1,10 +1,35 @@ type Query { + bottomMenu(filter: bottomMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [bottomMenu!]! + bottomMenu_by_id(id: ID!): bottomMenu + bottomMenu_aggregated(groupBy: [String], filter: bottomMenu_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [bottomMenu_aggregated!]! + bottomSecondMenu(filter: bottomSecondMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [bottomSecondMenu!]! + bottomSecondMenu_by_id(id: ID!): bottomSecondMenu + bottomSecondMenu_aggregated(groupBy: [String], filter: bottomSecondMenu_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [bottomSecondMenu_aggregated!]! + deviceBrand(filter: deviceBrand_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [deviceBrand!]! + deviceBrand_by_id(id: ID!): deviceBrand + deviceBrand_aggregated(groupBy: [String], filter: deviceBrand_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [deviceBrand_aggregated!]! + deviceType(filter: deviceType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [deviceType!]! + deviceType_by_id(id: ID!): deviceType + deviceType_aggregated(groupBy: [String], filter: deviceType_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [deviceType_aggregated!]! discoverGroup(filter: discoverGroup_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [discoverGroup!]! discoverGroup_by_id(id: ID!): discoverGroup discoverGroup_aggregated(groupBy: [String], filter: discoverGroup_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [discoverGroup_aggregated!]! discoverItem(filter: discoverItem_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [discoverItem!]! discoverItem_by_id(id: ID!): discoverItem discoverItem_aggregated(groupBy: [String], filter: discoverItem_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [discoverItem_aggregated!]! + mediaKit(filter: mediaKit_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [mediaKit!]! + mediaKit_by_id(id: ID!): mediaKit + mediaKit_aggregated(groupBy: [String], filter: mediaKit_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [mediaKit_aggregated!]! + mediaKitPage: mediaKitPage + mediaKitPage_mediaKit(filter: mediaKitPage_mediaKit_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [mediaKitPage_mediaKit!]! + mediaKitPage_mediaKit_by_id(id: ID!): mediaKitPage_mediaKit + mediaKitPage_mediaKit_aggregated(groupBy: [String], filter: mediaKitPage_mediaKit_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [mediaKitPage_mediaKit_aggregated!]! + navigationType(filter: navigationType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [navigationType!]! + navigationType_by_id(id: ID!): navigationType + navigationType_aggregated(groupBy: [String], filter: navigationType_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [navigationType_aggregated!]! + officialSocialMedia(filter: officialSocialMedia_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [officialSocialMedia!]! + officialSocialMedia_by_id(id: ID!): officialSocialMedia + officialSocialMedia_aggregated(groupBy: [String], filter: officialSocialMedia_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [officialSocialMedia_aggregated!]! socialMedia(filter: socialMedia_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [socialMedia!]! socialMedia_by_id(id: ID!): socialMedia socialMedia_aggregated(groupBy: [String], filter: socialMedia_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [socialMedia_aggregated!]! @@ -14,6 +39,12 @@ type Query { tabType(filter: tabType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [tabType!]! tabType_by_id(id: ID!): tabType tabType_aggregated(groupBy: [String], filter: tabType_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [tabType_aggregated!]! + topMenu(filter: topMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [topMenu!]! + topMenu_by_id(id: ID!): topMenu + topMenu_aggregated(groupBy: [String], filter: topMenu_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [topMenu_aggregated!]! + topSecondMenu(filter: topSecondMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [topSecondMenu!]! + topSecondMenu_by_id(id: ID!): topSecondMenu + topSecondMenu_aggregated(groupBy: [String], filter: topSecondMenu_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [topSecondMenu_aggregated!]! } """The `Boolean` scalar type represents `true` or `false`.""" @@ -53,6 +84,112 @@ The `String` scalar type represents textual data, represented as UTF-8 character """ scalar String +type bottomMenu { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + index: Int! + path: String + sort: Int + status: String + title: String! + type(filter: navigationType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): navigationType + user_created: String + user_updated: String + children(filter: bottomSecondMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [bottomSecondMenu] + children_func: count_functions +} + +type bottomMenu_aggregated { + group: JSON + countAll: Int + count: bottomMenu_aggregated_count + countDistinct: bottomMenu_aggregated_count + avg: bottomMenu_aggregated_fields + sum: bottomMenu_aggregated_fields + avgDistinct: bottomMenu_aggregated_fields + sumDistinct: bottomMenu_aggregated_fields + min: bottomMenu_aggregated_fields + max: bottomMenu_aggregated_fields +} + +type bottomMenu_aggregated_count { + date_created: Int + date_updated: Int + id: Int + index: Int + path: Int + sort: Int + status: Int + title: Int + type: Int + user_created: Int + user_updated: Int + children: Int +} + +type bottomMenu_aggregated_fields { + id: Float + index: Float + sort: Float + type: Float +} + +type bottomSecondMenu { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + index: Int! + parent(filter: bottomMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): bottomMenu + path: String + sort: Int + status: String + title: String! + type(filter: navigationType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): navigationType + user_created: String + user_updated: String +} + +type bottomSecondMenu_aggregated { + group: JSON + countAll: Int + count: bottomSecondMenu_aggregated_count + countDistinct: bottomSecondMenu_aggregated_count + avg: bottomSecondMenu_aggregated_fields + sum: bottomSecondMenu_aggregated_fields + avgDistinct: bottomSecondMenu_aggregated_fields + sumDistinct: bottomSecondMenu_aggregated_fields + min: bottomSecondMenu_aggregated_fields + max: bottomSecondMenu_aggregated_fields +} + +type bottomSecondMenu_aggregated_count { + date_created: Int + date_updated: Int + id: Int + index: Int + parent: Int + path: Int + sort: Int + status: Int + title: Int + type: Int + user_created: Int + user_updated: Int +} + +type bottomSecondMenu_aggregated_fields { + id: Float + index: Float + parent: Float + sort: Float + type: Float +} + type count_functions { count: Int } @@ -68,6 +205,95 @@ type datetime_functions { second: Int } +type deviceBrand { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + label: String + sort: Int + status: String + user_created: String + user_updated: String + value: String +} + +type deviceBrand_aggregated { + group: JSON + countAll: Int + count: deviceBrand_aggregated_count + countDistinct: deviceBrand_aggregated_count + avg: deviceBrand_aggregated_fields + sum: deviceBrand_aggregated_fields + avgDistinct: deviceBrand_aggregated_fields + sumDistinct: deviceBrand_aggregated_fields + min: deviceBrand_aggregated_fields + max: deviceBrand_aggregated_fields +} + +type deviceBrand_aggregated_count { + date_created: Int + date_updated: Int + id: Int + label: Int + sort: Int + status: Int + user_created: Int + user_updated: Int + value: Int +} + +type deviceBrand_aggregated_fields { + id: Float + sort: Float +} + +type deviceType { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + label: String + sort: Int + status: String + user_created: String + user_updated: String + value: Int +} + +type deviceType_aggregated { + group: JSON + countAll: Int + count: deviceType_aggregated_count + countDistinct: deviceType_aggregated_count + avg: deviceType_aggregated_fields + sum: deviceType_aggregated_fields + avgDistinct: deviceType_aggregated_fields + sumDistinct: deviceType_aggregated_fields + min: deviceType_aggregated_fields + max: deviceType_aggregated_fields +} + +type deviceType_aggregated_count { + date_created: Int + date_updated: Int + id: Int + label: Int + sort: Int + status: Int + user_created: Int + user_updated: Int + value: Int +} + +type deviceType_aggregated_fields { + id: Float + sort: Float + value: Float +} + type directus_files { charset: String description: String @@ -157,6 +383,7 @@ type discoverItem { sort: Int status: String title: String + url: String user_created: String user_updated: String } @@ -187,6 +414,7 @@ type discoverItem_aggregated_count { sort: Int status: Int title: Int + url: Int user_created: Int user_updated: Int } @@ -198,6 +426,200 @@ type discoverItem_aggregated_fields { sort: Float } +type mediaKit { + backgroundColor: String + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + index: Int + name: String! + png(filter: directus_files_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_files + sort: Int + status: String + svg(filter: directus_files_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_files + user_created: String + user_updated: String +} + +type mediaKit_aggregated { + group: JSON + countAll: Int + count: mediaKit_aggregated_count + countDistinct: mediaKit_aggregated_count + avg: mediaKit_aggregated_fields + sum: mediaKit_aggregated_fields + avgDistinct: mediaKit_aggregated_fields + sumDistinct: mediaKit_aggregated_fields + min: mediaKit_aggregated_fields + max: mediaKit_aggregated_fields +} + +type mediaKit_aggregated_count { + backgroundColor: Int + date_created: Int + date_updated: Int + id: Int + index: Int + name: Int + png: Int + sort: Int + status: Int + svg: Int + user_created: Int + user_updated: Int +} + +type mediaKit_aggregated_fields { + id: Float + index: Float + sort: Float +} + +type mediaKitPage { + allMediaKitZip(filter: directus_files_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_files + content: String! + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + status: String + title: String! + user_created: String + user_updated: String + mediaKitList(filter: mediaKitPage_mediaKit_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [mediaKitPage_mediaKit] + mediaKitList_func: count_functions +} + +type mediaKitPage_mediaKit { + id: ID! + mediaKit_id(filter: mediaKit_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): mediaKit + mediaKitPage_id(filter: mediaKitPage_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): mediaKitPage +} + +type mediaKitPage_mediaKit_aggregated { + group: JSON + countAll: Int + count: mediaKitPage_mediaKit_aggregated_count + countDistinct: mediaKitPage_mediaKit_aggregated_count + avg: mediaKitPage_mediaKit_aggregated_fields + sum: mediaKitPage_mediaKit_aggregated_fields + avgDistinct: mediaKitPage_mediaKit_aggregated_fields + sumDistinct: mediaKitPage_mediaKit_aggregated_fields + min: mediaKitPage_mediaKit_aggregated_fields + max: mediaKitPage_mediaKit_aggregated_fields +} + +type mediaKitPage_mediaKit_aggregated_count { + id: Int + mediaKit_id: Int + mediaKitPage_id: Int +} + +type mediaKitPage_mediaKit_aggregated_fields { + id: Float + mediaKit_id: Float + mediaKitPage_id: Float +} + +type navigationType { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + description: String + id: ID! + sort: Int + status: String + user_created: String + user_updated: String + value: Int +} + +type navigationType_aggregated { + group: JSON + countAll: Int + count: navigationType_aggregated_count + countDistinct: navigationType_aggregated_count + avg: navigationType_aggregated_fields + sum: navigationType_aggregated_fields + avgDistinct: navigationType_aggregated_fields + sumDistinct: navigationType_aggregated_fields + min: navigationType_aggregated_fields + max: navigationType_aggregated_fields +} + +type navigationType_aggregated_count { + date_created: Int + date_updated: Int + description: Int + id: Int + sort: Int + status: Int + user_created: Int + user_updated: Int + value: Int +} + +type navigationType_aggregated_fields { + id: Float + sort: Float + value: Float +} + +type officialSocialMedia { + activeSvg(filter: directus_files_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_files + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + index: Int! + link: String! + name: String! + sort: Int + status: String + svg(filter: directus_files_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_files + user_created: String + user_updated: String +} + +type officialSocialMedia_aggregated { + group: JSON + countAll: Int + count: officialSocialMedia_aggregated_count + countDistinct: officialSocialMedia_aggregated_count + avg: officialSocialMedia_aggregated_fields + sum: officialSocialMedia_aggregated_fields + avgDistinct: officialSocialMedia_aggregated_fields + sumDistinct: officialSocialMedia_aggregated_fields + min: officialSocialMedia_aggregated_fields + max: officialSocialMedia_aggregated_fields +} + +type officialSocialMedia_aggregated_count { + activeSvg: Int + date_created: Int + date_updated: Int + id: Int + index: Int + link: Int + name: Int + sort: Int + status: Int + svg: Int + user_created: Int + user_updated: Int +} + +type officialSocialMedia_aggregated_fields { + id: Float + index: Float + sort: Float +} + type socialMedia { date_created: Date date_created_func: datetime_functions @@ -341,6 +763,151 @@ type tabType_aggregated_fields { sort: Float } +type topMenu { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + index: Int! + path: String + sort: Int + status: String + title: String! + type(filter: navigationType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): navigationType + user_created: String + user_updated: String + children(filter: topSecondMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [topSecondMenu] + children_func: count_functions +} + +type topMenu_aggregated { + group: JSON + countAll: Int + count: topMenu_aggregated_count + countDistinct: topMenu_aggregated_count + avg: topMenu_aggregated_fields + sum: topMenu_aggregated_fields + avgDistinct: topMenu_aggregated_fields + sumDistinct: topMenu_aggregated_fields + min: topMenu_aggregated_fields + max: topMenu_aggregated_fields +} + +type topMenu_aggregated_count { + date_created: Int + date_updated: Int + id: Int + index: Int + path: Int + sort: Int + status: Int + title: Int + type: Int + user_created: Int + user_updated: Int + children: Int +} + +type topMenu_aggregated_fields { + id: Float + index: Float + sort: Float + type: Float +} + +type topSecondMenu { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + index: Int! + parent(filter: topMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): topMenu + path: String + sort: Int + status: String + title: String! + type(filter: navigationType_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): navigationType + user_created: String + user_updated: String +} + +type topSecondMenu_aggregated { + group: JSON + countAll: Int + count: topSecondMenu_aggregated_count + countDistinct: topSecondMenu_aggregated_count + avg: topSecondMenu_aggregated_fields + sum: topSecondMenu_aggregated_fields + avgDistinct: topSecondMenu_aggregated_fields + sumDistinct: topSecondMenu_aggregated_fields + min: topSecondMenu_aggregated_fields + max: topSecondMenu_aggregated_fields +} + +type topSecondMenu_aggregated_count { + date_created: Int + date_updated: Int + id: Int + index: Int + parent: Int + path: Int + sort: Int + status: Int + title: Int + type: Int + user_created: Int + user_updated: Int +} + +type topSecondMenu_aggregated_fields { + id: Float + index: Float + parent: Float + sort: Float + type: Float +} + +input bottomMenu_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + index: number_filter_operators + path: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + title: string_filter_operators + type: navigationType_filter + user_created: string_filter_operators + user_updated: string_filter_operators + children: bottomSecondMenu_filter + children_func: count_function_filter_operators + _and: [bottomMenu_filter] + _or: [bottomMenu_filter] +} + +input bottomSecondMenu_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + index: number_filter_operators + parent: bottomMenu_filter + path: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + title: string_filter_operators + type: navigationType_filter + user_created: string_filter_operators + user_updated: string_filter_operators + _and: [bottomSecondMenu_filter] + _or: [bottomSecondMenu_filter] +} + input count_function_filter_operators { count: number_filter_operators } @@ -371,6 +938,38 @@ input datetime_function_filter_operators { second: number_filter_operators } +input deviceBrand_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + label: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + user_created: string_filter_operators + user_updated: string_filter_operators + value: string_filter_operators + _and: [deviceBrand_filter] + _or: [deviceBrand_filter] +} + +input deviceType_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + label: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + user_created: string_filter_operators + user_updated: string_filter_operators + value: number_filter_operators + _and: [deviceType_filter] + _or: [deviceType_filter] +} + input directus_files_filter { charset: string_filter_operators description: string_filter_operators @@ -432,12 +1031,74 @@ input discoverItem_filter { sort: number_filter_operators status: string_filter_operators title: string_filter_operators + url: string_filter_operators user_created: string_filter_operators user_updated: string_filter_operators _and: [discoverItem_filter] _or: [discoverItem_filter] } +input mediaKit_filter { + backgroundColor: string_filter_operators + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + index: number_filter_operators + name: string_filter_operators + png: directus_files_filter + sort: number_filter_operators + status: string_filter_operators + svg: directus_files_filter + user_created: string_filter_operators + user_updated: string_filter_operators + _and: [mediaKit_filter] + _or: [mediaKit_filter] +} + +input mediaKitPage_filter { + allMediaKitZip: directus_files_filter + content: string_filter_operators + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + status: string_filter_operators + title: string_filter_operators + user_created: string_filter_operators + user_updated: string_filter_operators + mediaKitList: mediaKitPage_mediaKit_filter + mediaKitList_func: count_function_filter_operators + _and: [mediaKitPage_filter] + _or: [mediaKitPage_filter] +} + +input mediaKitPage_mediaKit_filter { + id: number_filter_operators + mediaKit_id: mediaKit_filter + mediaKitPage_id: mediaKitPage_filter + _and: [mediaKitPage_mediaKit_filter] + _or: [mediaKitPage_mediaKit_filter] +} + +input navigationType_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + description: string_filter_operators + id: number_filter_operators + sort: number_filter_operators + status: string_filter_operators + user_created: string_filter_operators + user_updated: string_filter_operators + value: number_filter_operators + _and: [navigationType_filter] + _or: [navigationType_filter] +} + input number_filter_operators { _eq: GraphQLStringOrFloat _neq: GraphQLStringOrFloat @@ -453,6 +1114,25 @@ input number_filter_operators { _nbetween: [GraphQLStringOrFloat] } +input officialSocialMedia_filter { + activeSvg: directus_files_filter + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + index: number_filter_operators + link: string_filter_operators + name: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + svg: directus_files_filter + user_created: string_filter_operators + user_updated: string_filter_operators + _and: [officialSocialMedia_filter] + _or: [officialSocialMedia_filter] +} + input socialMedia_filter { date_created: date_filter_operators date_created_func: datetime_function_filter_operators @@ -520,4 +1200,43 @@ input tabType_filter { value: string_filter_operators _and: [tabType_filter] _or: [tabType_filter] +} + +input topMenu_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + index: number_filter_operators + path: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + title: string_filter_operators + type: navigationType_filter + user_created: string_filter_operators + user_updated: string_filter_operators + children: topSecondMenu_filter + children_func: count_function_filter_operators + _and: [topMenu_filter] + _or: [topMenu_filter] +} + +input topSecondMenu_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + index: number_filter_operators + parent: topMenu_filter + path: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + title: string_filter_operators + type: navigationType_filter + user_created: string_filter_operators + user_updated: string_filter_operators + _and: [topSecondMenu_filter] + _or: [topSecondMenu_filter] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4782aaa9b0..ca53198351 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4706,15 +4706,6 @@ resolved "https://registry.yarnpkg.com/@metamask/detect-provider/-/detect-provider-1.2.0.tgz#3667a7531f2a682e3c3a43eaf3a1958bdb42a696" integrity sha512-ocA76vt+8D0thgXZ7LxFPyqw3H7988qblgzddTDA6B8a/yU0uKV42QR/DhA+Jh11rJjxW0jKvwb5htA6krNZDQ== -"@metamask/object-multiplex@^1.1.0": - version "1.2.0" - resolved "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-1.2.0.tgz#38fc15c142f61939391e1b9a8eed679696c7e4f4" - integrity sha512-hksV602d3NWE2Q30Mf2Np1WfVKaGqfJRy9vpHAmelbaD0OkDt06/0KQkRR6UVYdMbTbkuEu8xN5JDUU80inGwQ== - dependencies: - end-of-stream "^1.4.4" - once "^1.4.0" - readable-stream "^2.3.3" - "@metamask/safe-event-emitter@2.0.0", "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" resolved "https://registry.npmmirror.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" @@ -5193,76 +5184,53 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/chain@^0.0.1-alpha.19": - version "0.0.1-alpha.19" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.19.tgz#e9f291ed100c7dfd9353ccb49fe3fa1df9ecc8e9" - integrity sha512-mhxUT9IT6sWfE9DFcJx9NKMhwLk+NsGEceG2t0d6uI9Wm9hTZp78S5RWr/dkG0I/br2Y8xf3ZSDUmU1bdLnzRg== +"@portkey/chain@^0.0.1-alpha.22": + version "0.0.1-alpha.22" + resolved "https://registry.yarnpkg.com/@portkey/chain/-/chain-0.0.1-alpha.22.tgz#484f4ad889b8f3f02aa7b5dae48d3648eb225f93" + integrity sha512-2Z/yI6Ze0Of2HFdReOzVqXgTPULg4uEfKZ1GUqr4piRBYCbBmmrl0nhABuJIxOsNuW+5OQYRwFD5C3SJ4g26xA== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.19" + "@portkey/provider-types" "^0.0.1-alpha.22" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.21": - version "0.0.1-alpha.21" - resolved "https://registry.yarnpkg.com/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.21.tgz#388c6ce3a8832a9bd869854fe7cff46f398eb465" - integrity sha512-Cy+JrhCZde8h10iG9pyXD9uHtv/IVsc7HRLgrC0X1KBNWM60TFxhHsCVHyQcVivvRooFbGDxqlQpGWbwqlJNIw== +"@portkey/extension-provider@0.0.1-alpha.22": + version "0.0.1-alpha.22" + resolved "https://registry.yarnpkg.com/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.22.tgz#29ffc0a057a5fb5deeb538240a28718e15983fb3" + integrity sha512-tKAceyXZ1tD0JuwBC+0T1ZHD2umTmQz3lBQNF/LQ7RtIC5e7MyycXot2aBVYFBh8Ia0f4EjRAnl+EDm4eJII+Q== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.19" - "@portkey/providers" "^0.0.1-alpha.21" + "@portkey/provider-types" "^0.0.1-alpha.22" + "@portkey/providers" "^0.0.1-alpha.22" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/mobile-provider@^0.0.1-alpha.19": - version "0.0.1-alpha.19" - resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.19.tgz#2c20c58aa914145119dc865b644299f37fe7344e" - integrity sha512-aPZP3Ud+xroya1s9bGdQ/RCXsvuzUegw40lMFV2hj8vHMhlFbd90R4U0tlITRJYModxjs8mUB81CMoH+6C+vFg== +"@portkey/mobile-provider@^0.0.1-alpha.22": + version "0.0.1-alpha.22" + resolved "https://registry.yarnpkg.com/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.22.tgz#890de64cca9812bb02ee100d2dd3fcbfa5300215" + integrity sha512-d5pOIIRq4wWIZzEYp1R/dO99V4cEgJE4Sb1jJMJWaXltHCrBdQhxa0WMRm6LppTPTuH07RHlKtglG1ZKy0N+wg== -"@portkey/provider-types@0.0.1-alpha.21", "@portkey/provider-types@^0.0.1-alpha.19": - version "0.0.1-alpha.19" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.19.tgz#d67194cda11641c6b9ad2b10d8421ee159fd214f" - integrity sha512-nNV2tk+ttNTItZwVRuvWh5OlUPPw5rv0C68n/b73BksJ8ChRh7kK0Bmc6mQkKsmx2J6IWTMtMQix+3VbINS9EA== +"@portkey/provider-types@0.0.1-alpha.22", "@portkey/provider-types@^0.0.1-alpha.22": + version "0.0.1-alpha.22" + resolved "https://registry.yarnpkg.com/@portkey/provider-types/-/provider-types-0.0.1-alpha.22.tgz#89aef3b358650bcea3ddccfdc8c865e7bd7e814a" + integrity sha512-0EWV2a2v8+ZvHhs6RLtegMJUYPAHB3aeLWgzKMU5vUqG6OzOtmTAFbTvIBmZcW3xSHxyPDkkvMwjxPiSaSCigw== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.21", "@portkey/provider-utils@^0.0.1-alpha.21": - version "0.0.1-alpha.21" - resolved "https://registry.yarnpkg.com/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.21.tgz#6548ecd3a9d9abd809a968bc0700deae6c8bc0e9" - integrity sha512-BXihRsZyMNrvN2PM8AP0GyglzbtAwULE6wfjqY4bjaOLkvT48jZ/VmQy7nvMpYaQciXEMiL0tUooOCqlQ0W1Sg== - dependencies: - "@portkey/provider-types" "^0.0.1-alpha.19" - -"@portkey/provider-utils@^0.0.1-alpha.19": - version "0.0.1-alpha.19" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.19.tgz#0917943b88e91456e989c51e60f0a186fc6395cc" - integrity sha512-RiTE21bl1fEfvsth97L8gyfRanYyQy1H69ozMdltW+FFb3bd6Skcd1qMJgNQwQVsiVTepjpzvxqJuHWM/N4Wvw== +"@portkey/provider-utils@0.0.1-alpha.22", "@portkey/provider-utils@^0.0.1-alpha.22": + version "0.0.1-alpha.22" + resolved "https://registry.yarnpkg.com/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.22.tgz#a9e25bb0ccec4c11626da2e30b4aae03d249753f" + integrity sha512-n8z4gGeWohRR/o2cDfqyxFCtMyCarXqgwu3pae1dT3FWRgEJi7dO1kPrQ7cK0zS2opltgfr4KbeARYZIwU4rDg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.19" + "@portkey/provider-types" "^0.0.1-alpha.22" -"@portkey/providers@0.0.1-alpha.21", "@portkey/providers@^0.0.1-alpha.21": - version "0.0.1-alpha.21" - resolved "https://registry.yarnpkg.com/@portkey/providers/-/providers-0.0.1-alpha.21.tgz#c233c0deab104dafb83a8975316d5cbd936c9f76" - integrity sha512-0iBi0VD5i6/HJOf+0Xw3e7Lv1fyL8aR/zNcEu/3UYjsxjFGZys55mhK1Pe8XHTRgQPJY6RpU1DTecmquFwG2ew== +"@portkey/providers@0.0.1-alpha.22", "@portkey/providers@^0.0.1-alpha.22": + version "0.0.1-alpha.22" + resolved "https://registry.yarnpkg.com/@portkey/providers/-/providers-0.0.1-alpha.22.tgz#38b35865724dd6936df3f2901371413ca4f6e6c5" + integrity sha512-Rx7Rhuw56ibkB4iS1CglK5kA92iWSB4YOA2ylwZ+bY0jXJf3lw5grrk7s38ur133Ig8D7SW4AZp9jaD+G7TjLg== dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.19" - "@portkey/provider-types" "^0.0.1-alpha.19" - "@portkey/provider-utils" "^0.0.1-alpha.21" + "@portkey/chain" "^0.0.1-alpha.22" + "@portkey/provider-types" "^0.0.1-alpha.22" + "@portkey/provider-utils" "^0.0.1-alpha.22" "@types/readable-stream" "^2.3.15" lodash "^4.17.21" - pump "^3.0.0" - readable-stream "^4.4.0" - -"@portkey/providers@^0.0.1-alpha.19": - version "0.0.1-alpha.19" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.19.tgz#fbaa9ebfed3dd3f1b96945f4bde4bdb100f027e9" - integrity sha512-IJm19Fbl2pBxNCLS9cgd0qQ06ZCyqpdTghlFliD6XppUzy01wpOSiF7NujAWUntMn7lCV6eSuAtoJ4/gW1aEyA== - dependencies: - "@metamask/object-multiplex" "^1.1.0" - "@portkey/chain" "^0.0.1-alpha.19" - "@portkey/provider-types" "^0.0.1-alpha.19" - "@portkey/provider-utils" "^0.0.1-alpha.19" - "@types/readable-stream" "^2.3.15" - lodash "^4.17.21" - pump "^3.0.0" readable-stream "^4.4.0" "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": @@ -13016,7 +12984,7 @@ encoding@^0.1.11, encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -23399,10 +23367,10 @@ react-native-fs@^2.20.0: base-64 "^0.1.0" utf8 "^3.0.0" -react-native-gesture-handler@2.10.2: - version "2.10.2" - resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.10.2.tgz#0d7d82de767a551157eaedc5f74e389c5fc357b9" - integrity sha512-yUwTrgLinaGRdJN3igL5/QP+09B294EKgoCH7QJ4ABKb4W2mUvSDbbuGMaYBNnwMKAD87Ns2q/qibLWy3E3LTw== +react-native-gesture-handler@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.9.0.tgz#2f63812e523c646f25b9ad660fc6f75948e51241" + integrity sha512-a0BcH3Qb1tgVqUutc6d3VuWQkI1AM3+fJx8dkxzZs9t06qA27QgURYFoklpabuWpsUTzuKRpxleykp25E8m7tg== dependencies: "@egjs/hammerjs" "^2.0.17" hoist-non-react-statics "^3.3.0" From fd7bbd9b4e9b8414a7bef3055451782bf80cbbed Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 12 Jun 2023 19:10:38 +0800 Subject: [PATCH 085/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20injectJavaScript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index a7f4af4205..f79d444ea0 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -13,6 +13,7 @@ import { store } from 'store'; import { DappOverlay } from 'dapp/dappOverlay'; import { DappMobileManager } from 'dapp/dappManager'; import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import { isIos } from '@portkey-wallet/utils/mobile/device'; type BrowserTabProps = { uri: string; @@ -34,15 +35,22 @@ const BrowserTab: React.FC = ({ uri }) => { }; }); - const initOperator = useCallback((origin: string) => { - operatorRef.current = new DappMobileOperator({ - origin, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - stream: new MobileStream(webViewRef.current!), - dappManager: new DappMobileManager({ store: store as any }), - dappOverlay: new DappOverlay(), - }); - }, []); + const initOperator = useCallback( + (origin: string) => { + if (!isIos) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + webViewRef.current?.injectJavaScript(entryScriptWeb3!); + } + operatorRef.current = new DappMobileOperator({ + origin, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stream: new MobileStream(webViewRef.current!), + dappManager: new DappMobileManager({ store: store as any }), + dappOverlay: new DappOverlay(), + }); + }, + [entryScriptWeb3], + ); const onLoadStart = useCallback( ({ nativeEvent }: WebViewNavigationEvent) => { @@ -61,14 +69,14 @@ const BrowserTab: React.FC = ({ uri }) => { icon, }); }, []); - + if (!entryScriptWeb3) return null; return ( { operatorRef.current?.handleRequestMessage(nativeEvent.data); }} From 8117c9e606bb407c35178a6aaedde9322dadf649 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 19:32:17 +0800 Subject: [PATCH 086/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20change=20=20pac?= =?UTF-8?q?kage=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index 0f96e39b35..7acafd4e52 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -116,7 +116,7 @@ "react-native-echarts-pro": "^1.8.5", "react-native-flipper": "^0.161.0", "react-native-fs": "^2.20.0", - "react-native-gesture-handler": "2.10.2", + "react-native-gesture-handler": "2.9.0", "react-native-get-random-values": "^1.8.0", "react-native-keyboard-aware-scroll-view": "^0.9.5", "react-native-largelist": "3.1.0-rc.2", From bfc9a3d7699b02bcc7ecb67331e5c262752d8e07 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 19:49:09 +0800 Subject: [PATCH 087/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20discover=20chang?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 48 ++++++----- .../TabsDrawer/TabsDrawerContent.tsx | 3 + .../TabsDrawer/components/card/index.tsx | 9 ++- .../dapp/components/ConnectOverlay/index.tsx | 19 +---- .../dapp/components/DappInfoSection/index.tsx | 54 +++++++++++++ .../js/dapp/components/SignOverlay/index.tsx | 81 +++++++++++++++++++ .../TransactionDataSection/index.tsx | 64 +++++++++++++++ .../components/TransactionOverlay/index.tsx | 32 ++------ packages/mobile-app-did/js/hooks/discover.ts | 7 +- .../components/RecordSection/index.tsx | 4 +- packages/store/store-ca/discover/slice.ts | 15 +++- 11 files changed, 264 insertions(+), 72 deletions(-) create mode 100644 packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx create mode 100644 packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx create mode 100644 packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 1caf49e031..adb15443f7 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -14,24 +14,28 @@ import { DappOverlay } from 'dapp/dappOverlay'; import { DappMobileManager } from 'dapp/dappManager'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; -import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; import { isIos } from '@portkey-wallet/utils/mobile/device'; type BrowserTabProps = { isHidden: boolean; item: ITabItem; + activeTabId: number | undefined; setActiveTabRef: (ref: any) => void; setActiveWebViewRef: (ref: any) => void; }; -const BrowserTab: React.FC = ({ isHidden, item, setActiveTabRef, setActiveWebViewRef }) => { +const BrowserTab: React.FC = ({ + isHidden, + item, + activeTabId, + setActiveTabRef, + setActiveWebViewRef, +}) => { const viewRef = useRef(null); const webViewRef = useRef(null); const operatorRef = useRef(null); const [entryScriptWeb3, setEntryScriptWeb3] = useState(); - const { activeTabId } = useAppCASelector(state => state.discover); useEffectOnce(() => { const getEntryScriptWeb3 = async () => { @@ -94,23 +98,25 @@ const BrowserTab: React.FC = ({ isHidden, item, setActiveTabRef if (!entryScriptWeb3) return null; return ( - { - operatorRef.current?.handleRequestMessage(nativeEvent.data); - }} - onLoadStart={onLoadStart} - onLoad={handleUpdate} - onLoadProgress={({ nativeEvent }) => { - console.log(nativeEvent.progress, '=onLoadProgress'); - }} - onLoadEnd={handleUpdate} - applicationNameForUserAgent={'WebView Portkey did Mobile'} - /> + + { + operatorRef.current?.handleRequestMessage(nativeEvent.data); + }} + onLoadStart={onLoadStart} + onLoad={handleUpdate} + onLoadProgress={({ nativeEvent }) => { + console.log(nativeEvent.progress, '=onLoadProgress'); + }} + onLoadEnd={handleUpdate} + applicationNameForUserAgent={'WebView Portkey did Mobile'} + /> + ); }; diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 50513ecc5b..7cecb240d5 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -44,6 +44,8 @@ const TabsDrawerContent: React.FC = () => { const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); const activeWebviewScreenShot = useCallback(() => { + console.log('==========activeTabRef==========================', activeTabRef); + takeSnapshot(activeTabRef).then( uri => { console.log('Image saved to', uri); @@ -100,6 +102,7 @@ const TabsDrawerContent: React.FC = () => { { - const { - dappInfo: { origin, name, icon }, - onReject, - onApprove, - } = props; + const { dappInfo, onReject, onApprove } = props; const { t } = useLanguage(); const caInfo = useCurrentCaInfo(); const { walletName, currentNetwork } = useWallet(); @@ -83,15 +78,7 @@ const ConnectModal = (props: ConnectModalType) => { return ( - - - - {name || getHost(origin)} - - - {origin} - - + {t('Wallet')} {walletName} diff --git a/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx b/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx new file mode 100644 index 0000000000..bac325e426 --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import { TextL, TextS } from 'components/CommonText'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; + +interface DappInfoSectionType { + dappInfo: DappStoreItem; +} + +export const DappInfoSection = (props: DappInfoSectionType) => { + const { + dappInfo: { origin, name, icon }, + } = props; + + return ( + + + + {name || getHost(origin)} + + + {origin} + + + ); +}; + +export default DappInfoSection; + +const styles = StyleSheet.create({ + contentWrap: { + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + favIcon: { + width: pTd(48), + height: pTd(48), + marginBottom: pTd(8), + marginTop: pTd(24), + borderRadius: pTd(24), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + }, + title: { + marginBottom: pTd(2), + }, +}); diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx new file mode 100644 index 0000000000..a830d2762d --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -0,0 +1,81 @@ +import React, { useMemo } from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { StyleSheet } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { ModalBody } from 'components/ModalBody'; +import GStyles from 'assets/theme/GStyles'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { CommonButtonProps } from 'components/CommonButton'; + +import DappInfoSection from '../DappInfoSection'; +import { GetSignatureParams } from '@portkey/provider-types'; + +interface SignModalPropsType { + dappInfo: DappStoreItem; + signInfo: GetSignatureParams; + onReject: () => void; + onSign: () => void; +} +const SignModal = (props: SignModalPropsType) => { + const { dappInfo, onReject, onSign } = props; + const { t } = useLanguage(); + + const buttonList = useMemo( + () => [ + { + title: t('Reject'), + type: 'outline' as CommonButtonProps['type'], + onPress: () => { + onReject?.(); + OverlayModal.hide(); + }, + }, + { + title: t('Approve'), + type: 'primary' as CommonButtonProps['type'], + onPress: () => { + onSign?.(); + OverlayModal.hide(); + }, + }, + ], + [onReject, onSign, t], + ); + + return ( + + + + ); +}; + +export const showSignModal = (props: SignModalPropsType) => { + OverlayModal.show(, { + position: 'bottom', + enabledNestScrollView: true, + }); +}; + +export default { + showSignModal, +}; + +const styles = StyleSheet.create({ + contentWrap: { + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + title: { + marginBottom: pTd(2), + }, + method: { + borderRadius: pTd(6), + marginTop: pTd(24), + textAlign: 'center', + color: defaultColors.primaryColor, + backgroundColor: defaultColors.bg9, + ...GStyles.paddingArg(2, 8), + }, +}); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx new file mode 100644 index 0000000000..d370ee6636 --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx @@ -0,0 +1,64 @@ +import React, { useMemo } from 'react'; +import { StyleSheet, View, ViewStyle } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import { TextM, TextS } from 'components/CommonText'; +import Svg from 'components/Svg'; +import { FontStyles } from 'assets/theme/styles'; + +interface TransactionDataSectionType { + dataInfo: { [key: string]: any }; + collapsed: boolean; + style?: ViewStyle; +} + +export const TransactionDataSection = (props: TransactionDataSectionType) => { + const { dataInfo, collapsed, style = {} } = props; + + const topSection = useMemo( + () => ( + + Data + + + ), + [collapsed], + ); + + return ( + + {topSection} + {Object.entries(dataInfo).map(([key, value], index) => ( + + {key} + {value} + + ))} + + ); +}; + +export default TransactionDataSection; + +const styles = StyleSheet.create({ + card: { + width: pTd(335), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border6, + paddingLeft: pTd(12), + paddingRight: pTd(12), + }, + topSection: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + dataInfoGroup: { + marginTop: pTd(16), + }, + dataValue: { + marginTop: pTd(4), + }, +}); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index f427de5eb3..2d6f29ba4a 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -24,9 +24,8 @@ import { usePin } from 'hooks/store'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount } from 'utils/redux'; import { customFetch } from '@portkey-wallet/utils/fetch'; -import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import DappInfoSection from '../DappInfoSection'; interface TransactionModalPropsType { dappInfo: DappStoreItem; @@ -35,12 +34,7 @@ interface TransactionModalPropsType { onSign: () => void; } const ConnectModal = (props: TransactionModalPropsType) => { - const { - dappInfo: { origin, name, icon }, - transactionInfo, - onReject, - onSign, - } = props; + const { dappInfo, transactionInfo, onReject, onSign } = props; const { t } = useLanguage(); const gStyles = useGStyles(); const isMainnet = useIsMainnet(); @@ -173,8 +167,6 @@ const ConnectModal = (props: TransactionModalPropsType) => { const otherTransactionContent = useMemo(() => { const data = transactionInfo.params?.paramsOption || {}; - console.log('================ transactionInfo.params?.paramsOption===================='); - return ( @@ -219,7 +211,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { - Card + Data {Object.keys(data).map(item => ( <> {item} @@ -297,13 +289,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { - - - {name || getHost(origin)} - - - {origin} - + {transactionInfo.method} {isTransfer ? transferContent : otherTransactionContent} @@ -328,15 +314,7 @@ const styles = StyleSheet.create({ paddingLeft: pTd(20), paddingRight: pTd(20), }, - favIcon: { - width: pTd(48), - height: pTd(48), - borderRadius: pTd(24), - borderWidth: StyleSheet.hairlineWidth, - borderColor: defaultColors.border6, - marginBottom: pTd(8), - marginTop: pTd(24), - }, + title: { marginBottom: pTd(2), }, diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 6336b42b84..ad28173094 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -5,6 +5,7 @@ import { setActiveTab, addRecordsItem, createNewTab, + initNetworkDiscoverMap, } from '@portkey-wallet/store/store-ca/discover/slice'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; @@ -15,11 +16,15 @@ export const useDiscoverJumpWithNetWork = () => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); + const { discoverMap } = useAppCASelector(state => state.discover); + const discoverJump = ({ item }: { item: ITabItem }) => { - dispatch(changeDrawerOpenStatus(true)); + if (!discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); + dispatch(createNewTab({ ...item, networkType })); dispatch(setActiveTab({ ...item, networkType })); dispatch(addRecordsItem({ ...item, networkType })); + dispatch(changeDrawerOpenStatus(true)); }; return discoverJump; diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx index 04608b4e69..6d4e0df40a 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx @@ -30,8 +30,8 @@ export default function RecordSection() { }, [dispatch, networkType]); const showRecordList = useMemo(() => { - const recordsList = (JSON.parse(JSON.stringify(discoverMap?.[networkType]?.recordsList)) as ITabItem[]) || []; - return recordsList.reverse(); + const recordsList = (discoverMap?.[networkType]?.recordsList as ITabItem[]) || []; + return recordsList.map(ele => ele).reverse(); }, [discoverMap, networkType]); if (showRecordList?.length === 0) return null; diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 3fc4f4e07b..847543a242 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -19,6 +19,9 @@ export const discoverSlice = createSlice({ name: 'discover', initialState, reducers: { + initNetworkDiscoverMap: (state, { payload }: { payload: NetworkType }) => { + state.discoverMap[payload] = JSON.parse(JSON.stringify(initNetworkData)); + }, addRecordsItem: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { const { networkType, url } = payload; if (!state.discoverMap?.[networkType]) state.discoverMap[networkType] = initNetworkData; @@ -58,10 +61,14 @@ export const discoverSlice = createSlice({ createNewTab: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { const { networkType, id } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - if (!targetNetworkDiscover?.tabs) targetNetworkDiscover.tabs = []; + + if (!targetNetworkDiscover?.tabs) { + targetNetworkDiscover.tabs = [{ ...payload }]; + } else { + targetNetworkDiscover.tabs.push({ ...payload }); + } targetNetworkDiscover.activeTabId = id; - targetNetworkDiscover.tabs.push({ ...payload }); }, closeExistingTab: (state, { payload }: { payload: { id: number; networkType: NetworkType } }) => { const { networkType, id } = payload; @@ -79,6 +86,7 @@ export const discoverSlice = createSlice({ const { networkType, id } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + console.log('updateTab', payload); targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.map(item => item.id === id ? { ...item, ...payload } : item, ); @@ -87,12 +95,13 @@ export const discoverSlice = createSlice({ state.isDrawerOpen = payload; }, resetDiscover: (state, { payload }: { payload: NetworkType }) => { - state.discoverMap[payload] = initNetworkData; + state.discoverMap[payload] = JSON.parse(JSON.stringify(initNetworkData)); }, }, }); export const { + initNetworkDiscoverMap, addRecordsItem, upDateRecordsItem, clearRecordsList, From 9e971ff30fce7bd7862fd2ddd67e8a392ca2e6bf Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 20:03:18 +0800 Subject: [PATCH 088/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20delete=20extensi?= =?UTF-8?q?on=20discovergroup=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/store/Provider/Updater.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/store/Provider/Updater.tsx b/packages/web-extension-did/app/web/store/Provider/Updater.tsx index 14f271d7ce..a4c0945824 100644 --- a/packages/web-extension-did/app/web/store/Provider/Updater.tsx +++ b/packages/web-extension-did/app/web/store/Provider/Updater.tsx @@ -16,7 +16,7 @@ import { useCheckManager } from '@portkey-wallet/hooks/hooks-ca/graphql'; import { useCheckUpdate } from 'hooks/useCheckUpdate'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; import { useLocation } from 'react-router'; -import { useSocialMediaList, useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; keepAliveOnPages({}); @@ -56,6 +56,5 @@ export default function Updater() { }, [onLocking]); usePhoneCountryCode(true); useSocialMediaList(true); - useDiscoverGroupList(true); return null; } From b56e20b43062828029cef14cbd629921076c735e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 12 Jun 2023 20:08:23 +0800 Subject: [PATCH 089/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20app=20updater=20d?= =?UTF-8?q?iscoverGroup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/Updater/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index a99f85b362..301e1183e7 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -15,7 +15,7 @@ import { useCheckManagerOnLogout } from 'hooks/useLogOut'; import socket from '@portkey-wallet/socket/socket-did'; import CommonToast from 'components/CommonToast'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; -import { useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useDiscoverGroupList, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; @@ -57,6 +57,6 @@ export default function Updater() { usePhoneCountryCode(true); useSocialMediaList(true); useTabMenuList(true); - // useDiscoverGroupList(true); + useDiscoverGroupList(true); return null; } From 23423010d74d55d562cf4ea62b84bed7dd7448e5 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 12:00:35 +0800 Subject: [PATCH 090/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20compatible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TabsDrawer/TabsDrawerContent.tsx | 2 +- .../TabsDrawer/components/Card/index.tsx | 83 +++++++ .../components/TabsOverlay/index.tsx | 210 ++++++++++++++++++ packages/mobile-app-did/js/hooks/discover.ts | 2 +- packages/store/store-ca/discover/slice.ts | 12 +- packages/store/store-ca/discover/type.ts | 2 +- 6 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 7cecb240d5..66837215eb 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -35,7 +35,7 @@ const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { isDrawerOpen, discoverMap } = useAppCASelector(state => state.discover); + const { isDrawerOpen, discoverMap = {} } = useAppCASelector(state => state.discover); const { activeTabId, tabs } = discoverMap[networkType] ?? {}; const [activeTabRef, setActiveTabRef] = React.useState(null); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx new file mode 100644 index 0000000000..10689c755d --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx @@ -0,0 +1,83 @@ +import GStyles from 'assets/theme/GStyles'; +import { TextM } from 'components/CommonText'; +import { TouchableOpacity } from 'react-native'; +import React from 'react'; +import { StyleSheet, View, Image } from 'react-native'; +import { pTd } from 'utils/unit'; + +import Svg from 'components/Svg'; + +import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { closeExistingTab, setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { defaultColors } from 'assets/theme'; + +interface ICardsProps { + item: ITabItem; +} + +const Card: React.FC = (props: ICardsProps) => { + const { item } = props; + + const dispatch = useAppCommonDispatch(); + const { networkType } = useCurrentNetworkInfo(); + + return ( + + + + + {item?.name ?? getHost(item?.url)} + + dispatch(closeExistingTab({ id: item?.id, networkType }))}> + + + + dispatch(setActiveTab({ id: item.id, networkType }))}> + + + + ); +}; + +export default Card; + +const tabShowItemStyle = StyleSheet.create({ + cardWrap: { + borderRadius: pTd(8), + width: pTd(160), + height: pTd(214), + marginTop: pTd(24), + shadowOffset: { width: 10, height: 10 }, + backgroundColor: defaultColors.bg1, + shadowColor: defaultColors.shadow1, + shadowOpacity: 0.2, + shadowRadius: 10, + elevation: 2, + }, + header: { + ...GStyles.paddingArg(6, 8), + height: pTd(32), + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + title: { + flex: 1, + marginLeft: pTd(8), + marginRight: pTd(8), + }, + screenshot: { + width: pTd(160), + height: pTd(182), + }, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx new file mode 100644 index 0000000000..3dbf0fd184 --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -0,0 +1,210 @@ +import React, { Dispatch, SetStateAction, useCallback } from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { StyleSheet, TouchableOpacity, View, Share } from 'react-native'; +import { TextL, TextS } from 'components/CommonText'; +import { defaultColors } from 'assets/theme'; +import fonts from 'assets/theme/fonts'; +import { pTd } from 'utils/unit'; +import Svg from 'components/Svg'; +import { useLanguage } from 'i18n/hooks'; +import GStyles from 'assets/theme/GStyles'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { FontStyles } from 'assets/theme/styles'; +import { setStringAsync } from 'expo-clipboard'; +import CommonToast from 'components/CommonToast'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; + +import { isIOS } from '@rneui/base'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; + +enum HANDLE_TYPE { + REFRESH = 'Refresh', + COPY = 'Copy URL', + SHARE = 'Share', + CLOSE = 'Close', + CANCEL = 'Cancel', + SWITCH = 'Switch', +} + +const handleArray = [ + { title: HANDLE_TYPE.REFRESH, icon: 'refresh1' }, + { title: HANDLE_TYPE.COPY, icon: 'copy1' }, + { title: HANDLE_TYPE.SHARE, icon: 'share' }, + { title: HANDLE_TYPE.SWITCH, icon: 'switch' }, +] as const; + +const BrowserEditModal = ({ + browserInfo, + activeWebViewRef, + activeWebviewScreenShot, + setPreActiveTabId, +}: { + browserInfo: ITabItem; + activeWebViewRef: any; + activeWebviewScreenShot: () => void; + setPreActiveTabId: Dispatch>; +}) => { + const { t } = useLanguage(); + const dispatch = useAppCommonDispatch(); + const { networkType } = useCurrentNetworkInfo(); + + const handleUrl = useCallback( + async (type: HANDLE_TYPE) => { + let isCopy = false; + + switch (type) { + case HANDLE_TYPE.REFRESH: + activeWebViewRef?.current?.reload?.(); + OverlayModal.hide(); + break; + + case HANDLE_TYPE.COPY: + isCopy = await setStringAsync(browserInfo?.url || ''); + isCopy && CommonToast.success(t('Copy Success')); + break; + + case HANDLE_TYPE.SHARE: + await Share.share({ + message: isIOS ? browserInfo?.name ?? browserInfo.url : browserInfo?.url, + url: browserInfo?.url ?? browserInfo?.name ?? '', + title: browserInfo?.name ?? browserInfo.url, + }).catch(shareError => { + console.log(shareError); + }); + break; + + case HANDLE_TYPE.CLOSE: + OverlayModal.hide(); + break; + + case HANDLE_TYPE.CANCEL: + OverlayModal.hide(); + break; + + case HANDLE_TYPE.SWITCH: + OverlayModal.hide(); + activeWebviewScreenShot(); + dispatch(setActiveTab({ id: undefined, networkType })); + setPreActiveTabId(Number(browserInfo?.id)); + + break; + + default: + break; + } + }, + [ + activeWebViewRef, + browserInfo.url, + browserInfo?.name, + browserInfo?.id, + t, + activeWebviewScreenShot, + dispatch, + networkType, + setPreActiveTabId, + ], + ); + + return ( + + + + + {browserInfo?.name} + + + handleUrl(HANDLE_TYPE.CANCEL)}> + + + + + {handleArray.map((ele, index) => ( + handleUrl(ele.title)}> + + + + + {ele.title} + + + ))} + + + handleUrl(HANDLE_TYPE.CANCEL)}> + {t('Cancel')} + + + ); +}; + +export const showBrowserModal = (props: { + browserInfo: ITabItem; + activeWebViewRef: any; + activeWebviewScreenShot: () => void; + setPreActiveTabId: Dispatch>; +}) => { + OverlayModal.show(, { + position: 'bottom', + containerStyle: { backgroundColor: defaultColors.bg6 }, + }); +}; + +export default { + showBrowserModal, +}; + +const styles = StyleSheet.create({ + modalStyle: { + ...GStyles.paddingArg(16, 20), + backgroundColor: defaultColors.bg6, + width: screenWidth, + }, + title: { + textAlign: 'left', + height: pTd(22), + lineHeight: pTd(22), + marginVertical: pTd(13), + paddingLeft: pTd(8), + ...fonts.mediumFont, + }, + listWrap: { + marginTop: pTd(24), + marginBottom: pTd(24), + paddingLeft: pTd(12), + display: 'flex', + flexWrap: 'wrap', + flexDirection: 'row', + }, + listItem: { + marginRight: pTd(34), + borderRadius: pTd(6), + overflow: 'hidden', + }, + listItemNoMarginRight: { + marginRight: 0, + }, + svgWrap: { + backgroundColor: defaultColors.bg1, + }, + itemTitle: { + textAlign: 'center', + marginTop: pTd(8), + }, + divider: { + width: '100%', + height: StyleSheet.hairlineWidth, + backgroundColor: defaultColors.bg7, + }, + cancelButton: { + height: pTd(44), + fontSize: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index ad28173094..5f967a9550 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -19,7 +19,7 @@ export const useDiscoverJumpWithNetWork = () => { const { discoverMap } = useAppCASelector(state => state.discover); const discoverJump = ({ item }: { item: ITabItem }) => { - if (!discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); + if (!discoverMap || !discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); dispatch(createNewTab({ ...item, networkType })); dispatch(setActiveTab({ ...item, networkType })); diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 847543a242..37d15f66a9 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -20,10 +20,15 @@ export const discoverSlice = createSlice({ initialState, reducers: { initNetworkDiscoverMap: (state, { payload }: { payload: NetworkType }) => { - state.discoverMap[payload] = JSON.parse(JSON.stringify(initNetworkData)); + state.discoverMap = { + ...(state.discoverMap || {}), + [payload]: JSON.parse(JSON.stringify(initNetworkData)), + }; }, addRecordsItem: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { const { networkType, url } = payload; + if (!state.discoverMap) return; + if (!state.discoverMap?.[networkType]) state.discoverMap[networkType] = initNetworkData; const targetItem = state.discoverMap?.[networkType]?.recordsList.find(item => item.url === url); @@ -95,7 +100,10 @@ export const discoverSlice = createSlice({ state.isDrawerOpen = payload; }, resetDiscover: (state, { payload }: { payload: NetworkType }) => { - state.discoverMap[payload] = JSON.parse(JSON.stringify(initNetworkData)); + state.discoverMap = { + ...(state.discoverMap || {}), + [payload]: JSON.parse(JSON.stringify(initNetworkData)), + }; }, }, }); diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index 8c6ecef37c..6d642c37a1 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -16,7 +16,7 @@ export interface IDiscoverNetworkStateType { export interface IDiscoverStateType { isDrawerOpen: boolean; - discoverMap: { + discoverMap?: { [key in NetworkType]?: IDiscoverNetworkStateType; }; } From 80d6c68a0b2cf112ca02a22d344eb9480aba1a73 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 13 Jun 2023 12:28:46 +0800 Subject: [PATCH 091/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20sell=20?= =?UTF-8?q?timer=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/const.ts | 4 + .../app/web/pages/Buy/index.tsx | 231 +++++++----------- 2 files changed, 98 insertions(+), 137 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index 8a7d107a2c..c17305bb03 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -44,6 +44,8 @@ export const initValueSave: { min: number | null; max: number | null; side: PaymentTypeEnum; + receive: string; + isShowErrMsg: boolean; } = { amount: initCurrency, currency: 'USD', @@ -53,6 +55,8 @@ export const initValueSave: { min: null, max: null, side: PaymentTypeEnum.BUY, + receive: '', + isShowErrMsg: false, }; export interface IFetchOrderQuote { diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 5f867857d6..dd6cc68fe5 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -75,22 +75,16 @@ export default function Buy() { max: null, min: null, side, + receive: '', + isShowErrMsg: false, }; - updateReceive({ - crypto, - network, - fiat, - country, - amount, - side, - }); - updateCrypto(fiat); + updateCrypto(); } else { updateCrypto(); - updateReceive(); } return () => { clearInterval(updateTimerRef.current); + updateTimerRef.current = undefined; }; }); @@ -121,11 +115,14 @@ export default function Buy() { cryptoQuantity?: string; }) => { if (valueSaveRef.current.side === PaymentTypeEnum.SELL && fiatQuantity && rampFee) { - const receive = Number(fiatQuantity) - Number(rampFee); - setReceive(formatAmountShow(receive, 4)); + const receive = formatAmountShow(Number(fiatQuantity) - Number(rampFee), 4); + setReceive(receive); + valueSaveRef.current.receive = receive; } if (valueSaveRef.current.side === PaymentTypeEnum.BUY) { - setReceive(formatAmountShow(cryptoQuantity || '', 4)); + const receive = formatAmountShow(cryptoQuantity || '', 4); + setReceive(receive); + valueSaveRef.current.receive = receive; } }, [], @@ -135,17 +132,21 @@ export default function Buy() { const { min, max, currency, crypto, side } = valueSaveRef.current; if (min !== null && max !== null) { clearInterval(updateTimerRef.current); + updateTimerRef.current = undefined; if (side === PaymentTypeEnum.SELL) { setErrMsg(showLimitText(min, max, crypto)); } if (side === PaymentTypeEnum.BUY) { setErrMsg(showLimitText(min, max, currency)); } + valueSaveRef.current.isShowErrMsg = true; + setReceive(''); + valueSaveRef.current.receive = ''; } }, [showLimitText]); - const { updateReceive, handleSetTimer } = useMemo(() => { - const updateReceive = async ( + const updateReceive = useCallback( + async ( params = { crypto: valueSaveRef.current.crypto, network: valueSaveRef.current.network, @@ -155,7 +156,6 @@ export default function Buy() { side: valueSaveRef.current.side, }, ) => { - clearInterval(updateTimerRef.current); try { const rst = await getOrderQuote(params); if (params.amount !== valueSaveRef.current.amount) return; @@ -164,68 +164,86 @@ export default function Buy() { setReceiveCase({ fiatQuantity, rampFee, cryptoQuantity }); setRate(cryptoPrice); setErrMsg(''); - handleSetTimer(); } catch (error) { setReceive(''); + valueSaveRef.current.receive = ''; setRate(''); - setErrMsgCase(); - setRateUpdateTime(MAX_UPDATE_TIME); - updateTimeRef.current = MAX_UPDATE_TIME; + setErrMsg(''); + console.log('error', error); } - }; + }, + [setReceiveCase], + ); - const handleSetTimer = () => { - const timer = setInterval(() => { - --updateTimeRef.current; - if (updateTimeRef.current === 0) { - updateReceive(); - updateTimeRef.current = MAX_UPDATE_TIME; - } - updateTimerRef.current = timer; - setRateUpdateTime(updateTimeRef.current); - }, 1000); - }; - return { updateReceive, handleSetTimer }; - }, [setErrMsgCase, setReceiveCase]); + const handleSetTimer = useCallback(() => { + const timer = setInterval(() => { + updateTimerRef.current = timer; + --updateTimeRef.current; - const updateCrypto = useCallback( - async (fiat = curFiat.currency || 'USD') => { - const { crypto, network, side } = valueSaveRef.current; - const data = await getCryptoInfo({ fiat }, crypto, network, side); - if (side === PaymentTypeEnum.BUY) { - if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { - valueSaveRef.current.max = data.maxPurchaseAmount; - valueSaveRef.current.min = data.minPurchaseAmount; - } + if (updateTimeRef.current === 0) { + updateReceive(); + updateTimeRef.current = MAX_UPDATE_TIME; + } + + setRateUpdateTime(updateTimeRef.current); + }, 1000); + }, [updateReceive]); + + const stopInterval = useCallback(() => { + clearInterval(updateTimerRef.current); + updateTimerRef.current = undefined; + setRate(''); + }, []); + + const resetTimer = useCallback(() => { + clearInterval(updateTimerRef.current); + updateTimerRef.current = undefined; + updateTimeRef.current = MAX_UPDATE_TIME; + setRateUpdateTime(MAX_UPDATE_TIME); + handleSetTimer(); + }, [handleSetTimer]); + + const updateCrypto = useCallback(async () => { + const { currency, crypto, network, side } = valueSaveRef.current; + const data = await getCryptoInfo({ fiat: currency }, crypto, network, side); + if (side === PaymentTypeEnum.BUY) { + if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { + valueSaveRef.current.max = data.maxPurchaseAmount; + valueSaveRef.current.min = data.minPurchaseAmount; + } + } else { + if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { + valueSaveRef.current.max = data.maxSellAmount; + valueSaveRef.current.min = data.minSellAmount; + } + } + const { amount, min, max } = valueSaveRef.current; + if (min && max) { + if (!isValidValue({ amount, min, max })) { + setErrMsgCase(); + stopInterval(); } else { - if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { - valueSaveRef.current.max = data.maxSellAmount; - valueSaveRef.current.min = data.minSellAmount; + await updateReceive(); + if (!updateTimerRef.current && valueSaveRef.current.receive) { + resetTimer(); } } - }, - [curFiat.currency], - ); + } + }, [isValidValue, resetTimer, setErrMsgCase, stopInterval, updateReceive]); const handleInputChange = useCallback( - (v: string) => { + async (v: string) => { setAmount(v); valueSaveRef.current.amount = v; const { min, max } = valueSaveRef.current; - if (max !== null && min !== null) { - if (!isValidValue({ amount: v, min, max })) { - setErrMsgCase(); - clearInterval(updateTimerRef.current); - setRateUpdateTime(MAX_UPDATE_TIME); - updateTimeRef.current = MAX_UPDATE_TIME; - setReceive(''); - setRate(''); - return; - } + if (max && min && !isValidValue({ amount: v, min, max })) { + setErrMsgCase(); + stopInterval(); + return; } const { crypto, network, country, currency, side } = valueSaveRef.current; - updateReceive({ + await updateReceive({ crypto, network, fiat: currency, @@ -233,31 +251,16 @@ export default function Buy() { amount: v, side, }); + if (!updateTimerRef.current && valueSaveRef.current.receive) { + resetTimer(); + } }, - [isValidValue, setErrMsgCase, updateReceive], + [isValidValue, resetTimer, setErrMsgCase, stopInterval, updateReceive], ); - const getQuoteAndSetData = useCallback(async () => { - const { crypto, currency, country, network, amount, side } = valueSaveRef.current; - const rst = await getOrderQuote({ - crypto, - network, - fiat: currency, - country, - amount, - side, - }); - const { cryptoPrice, cryptoQuantity, fiatQuantity, rampFee } = rst; - setReceiveCase({ fiatQuantity, rampFee, cryptoQuantity }); - setRate(cryptoPrice); - setRateUpdateTime(MAX_UPDATE_TIME); - updateTimeRef.current = MAX_UPDATE_TIME; - handleSetTimer(); - }, [handleSetTimer, setReceiveCase]); - const handlePageChange = useCallback( async (e: RadioChangeEvent) => { - clearInterval(updateTimerRef.current); + stopInterval(); setPage(e.target.value); // BUY valueSaveRef.current = initValueSave; @@ -272,26 +275,24 @@ export default function Buy() { setCurFiat(initFiat); setErrMsg(''); setReceive(''); + valueSaveRef.current.receive = ''; setRate(''); try { setLoading(true); - await getQuoteAndSetData(); await updateCrypto(); - - handleInputChange(valueSaveRef.current.amount); } catch (error) { console.log('error', error); } finally { setLoading(false); } }, - [getQuoteAndSetData, handleInputChange, setLoading, updateCrypto], + [setLoading, stopInterval, updateCrypto], ); const handleSelect = useCallback( async (v: PartialFiatType, drawerType: DrawerType) => { if (drawerType === DrawerType.token) { - // setCurToken(v); + // only elf for now } else { if (v.currency && v.country) { setCurFiat(v); @@ -300,66 +301,22 @@ export default function Buy() { } else { return; } - if (v.currency === curFiat.currency) return; + + setErrMsg(''); + setReceive(''); + valueSaveRef.current.receive = ''; + setRate(''); try { - clearInterval(updateTimerRef.current); - setErrMsg(''); - setReceive(''); - setRate(''); setLoading(true); - const { crypto, network, amount, side } = valueSaveRef.current; - const data = await getCryptoInfo({ fiat: v.currency }, crypto, network, side); - if (side === PaymentTypeEnum.BUY) { - if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { - valueSaveRef.current.max = data.maxPurchaseAmount; - valueSaveRef.current.min = data.minPurchaseAmount; - - if (isValidValue({ amount, min: data.minPurchaseAmount, max: data.maxPurchaseAmount })) { - await getQuoteAndSetData(); - setErrMsg(''); - } else { - setErrMsgCase(); - setReceive(''); - setRate(''); - } - } else { - // not maxPurchaseAmount and minPurchaseAmount - setErrMsg(''); - setReceive(''); - setRate(''); - } - } else { - if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { - valueSaveRef.current.max = data.maxSellAmount; - valueSaveRef.current.min = data.minSellAmount; - // setErrMsgCase(); - - if (isValidValue({ amount, max: data.maxSellAmount, min: data.minSellAmount })) { - await getQuoteAndSetData(); - setErrMsg(''); - } else { - setErrMsgCase(); - setReceive(''); - setRate(''); - } - } else { - // not maxSellAmount and minSellAmount - setErrMsg(''); - setReceive(''); - setRate(''); - } - } + await updateCrypto(); } catch (error) { console.log('error', error); - setErrMsg(''); - setReceive(''); - setRate(''); } finally { setLoading(false); } } }, - [curFiat.currency, getQuoteAndSetData, isValidValue, setErrMsgCase, setLoading], + [setLoading, updateCrypto], ); const { From 5129c2b43cc2c003c39c82e0004b600fdda4f164 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 13 Jun 2023 13:06:05 +0800 Subject: [PATCH 092/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20signature=20&=20?= =?UTF-8?q?network?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/Prompt/routes/index.tsx | 5 + .../approval/ApprovalController.ts | 11 ++ .../methodController/AELFMethodController.ts | 62 +++++++++++ .../app/web/pages/ConnectWallet/index.less | 5 + .../app/web/pages/ConnectWallet/index.tsx | 2 +- .../app/web/pages/GetSignature/index.less | 71 ++++++++++++ .../app/web/pages/GetSignature/index.tsx | 101 ++++++++++++++++++ 7 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 packages/web-extension-did/app/web/pages/GetSignature/index.less create mode 100644 packages/web-extension-did/app/web/pages/GetSignature/index.tsx diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index 6c11212c94..88d57d9aef 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -50,6 +50,7 @@ import Permission from 'pages/Permission'; import ConnectWallet from 'pages/ConnectWallet'; import ConnectedSites from 'pages/WalletSecurity/ConnectedSites'; import SendTransactions from 'pages/SendTransactions'; +import GetSignature from 'pages/GetSignature'; export const PageRouter = () => { const { isNotLessThan768 } = useCommonState(); @@ -181,6 +182,10 @@ export const PageRouter = () => { path: '/send-transactions', element: , }, + { + path: '/get-signature', + element: , + }, { path: '*', diff --git a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts index f4149347c5..29ab693cc0 100644 --- a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts +++ b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts @@ -111,4 +111,15 @@ export default class ApprovalController { search: JSON.stringify(params), }); } + + /** + * Obtain authorization to get signature + * + */ + async authorizedToGetSignature(params: any): Promise { + return this.notificationService.openPrompt({ + method: PromptRouteTypes.GET_SIGNATURE, + search: JSON.stringify(params), + }); + } } diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index f1b1190bdc..91d6dbad77 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -10,6 +10,7 @@ import { MethodsBase, ResponseCode, MethodsUnimplemented } from '@portkey/provid import { ExtensionDappManager } from './ExtensionDappManager'; import { getSWReduxState } from 'utils/lib/SWGetReduxStore'; import ApprovalController from 'controllers/approval/ApprovalController'; +import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; const storeInSW = { getState: getSWReduxState, @@ -25,6 +26,8 @@ const aelfMethodList = [ MethodsBase.CHAINS_INFO, MethodsBase.REQUEST_ACCOUNTS, MethodsBase.SEND_TRANSACTION, + MethodsUnimplemented.GET_WALLET_SIGNATURE, + MethodsBase.NETWORK, MethodsUnimplemented.GET_WALLET_STATE, MethodsUnimplemented.GET_WALLET_NAME, ]; @@ -72,6 +75,12 @@ export default class AELFMethodController { case MethodsBase.REQUEST_ACCOUNTS: this.requestAccounts(sendResponse, message.payload); break; + case MethodsBase.NETWORK: + this.getNetwork(sendResponse, message.payload); + break; + case MethodsUnimplemented.GET_WALLET_SIGNATURE: + this.getSignature(sendResponse, message.payload); + break; case MethodsUnimplemented.GET_WALLET_STATE: this.getWalletState(sendResponse, message.payload); break; @@ -206,6 +215,18 @@ export default class AELFMethodController { }, }); + const isForward = chainInfo?.caContractAddress !== payload?.contractAddress; + const mth = isForward ? 'ManagerForwardCall' : payload?.method; + + if (!CA_METHOD_WHITELIST.includes(mth)) + return sendResponse({ + ...errorHandler(400001), + data: { + code: ResponseCode.CONTRACT_ERROR, + msg: 'method is not in the whitelist', + }, + }); + const result = await this.approvalController.authorizedToSendTransactions({ origin, payload: message.payload, @@ -226,4 +247,45 @@ export default class AELFMethodController { }); sendResponse(result); }; + + getSignature: RequestCommonHandler = async (sendResponse, message) => { + if (!message?.payload?.data) + return sendResponse({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); + + if (!(await this.dappManager.isActive(message.origin))) + return sendResponse({ + ...errorHandler(200004), + data: { + code: ResponseCode.UNAUTHENTICATED, + }, + }); + + const result = await this.approvalController.authorizedToGetSignature({ + origin, + payload: { + data: message.payload.data, + origin: message.origin, + }, + }); + if (result.error === 200003) + return sendResponse({ + ...errorHandler(200003), + data: { + code: ResponseCode.USER_DENIED, + }, + }); + if (result.error) + return sendResponse({ + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }); + sendResponse(result); + }; + + getNetwork: RequestCommonHandler = async (sendResponse) => { + const networkType = await this.dappManager.networkType(); + sendResponse({ ...errorHandler(0), data: networkType }); + }; } diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less index fc90143e5d..7565f51046 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less @@ -1,4 +1,5 @@ @import '../../assets/theme/color.less'; +@import '../../assets/theme/constants.less'; .connect-wallet { margin: 40px auto; @@ -19,6 +20,10 @@ border-radius: 50%; margin-right: 8px; } + .origin { + line-break: anywhere; + .text-overflow(1) + } } .title { margin-bottom: 16px; diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index 4b4b0da64b..66dbde826a 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -34,7 +34,7 @@ export default function ConnectWallet() { loadEle={detail.appName?.[0]} errorEle={detail.appName?.[0]} /> - {detail.appHref} + {detail.appHref}
    ), [detail], diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.less b/packages/web-extension-did/app/web/pages/GetSignature/index.less new file mode 100644 index 0000000000..468b7d0b3a --- /dev/null +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.less @@ -0,0 +1,71 @@ +@import '../../assets/theme/color.less'; +@import '../../assets/theme/constants.less'; + +.get-signature { + width: 328px; + margin: 40px auto 16px; + flex-direction: column; + font-size: 12px; + line-height: 16px; + color: @font-13; + .site { + align-self: center; + margin-bottom: 24px; + width: fit-content; + padding: 8px 16px; + border: 1px solid @border-2; + border-radius: 24px; + font-size: 14px; + line-height: 20px; + color: @font-13; + .icon { + flex-shrink: 0; + width: 24px; + border-radius: 50%; + margin-right: 8px; + } + .origin { + font-size: 14px; + line-height: 20px; + color: @font-13; + line-break: anywhere; + .text-overflow(1) + } + } + .title { + font-size: 24px; + line-height: 28px; + font-weight: 700; + color: @font-11; + } + .message { + margin-top: 16px; + .data { + margin-top: 4px; + padding: 16px; + border: 1px solid @border-2; + border-radius: 6px; + } + } + .btn { + width: 100%; + margin-top: 40px; + .@{app-prefix}-btn { + width: 156px; + height: 48px; + font-size: 14px; + line-height: 22px; + text-align: center; + border-radius: 24px; + } + .@{app-prefix}-btn-text { + border: 1px solid @border-3; + color: @font-9; + background-color: @bg-11; + } + .@{app-prefix}-btn-primary { + color: @font-5; + background-color: @bg-7; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx new file mode 100644 index 0000000000..afebda10c1 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx @@ -0,0 +1,101 @@ +import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { handleErrorMessage } from '@portkey-wallet/utils'; +import aes from '@portkey-wallet/utils/aes'; +import { Button, message } from 'antd'; +import { useTranslation } from 'react-i18next'; +import usePromptSearch from 'hooks/usePromptSearch'; +import { useCallback, useMemo } from 'react'; +import { useDapp, useUserInfo, useWalletInfo } from 'store/Provider/hooks'; +import errorHandler from 'utils/errorHandler'; +import { closePrompt } from 'utils/lib/serviceWorkerAction'; +import { ResponseCode } from '@portkey/provider-types'; +import { getWallet } from '@portkey-wallet/utils/aelf'; +import ImgLoading from 'components/ImgLoading'; +import './index.less'; + +export default function GetSignature() { + const { payload } = usePromptSearch<{ + payload: { + data: string; + origin: string; + }; + }>(); + const wallet = useCurrentWalletInfo(); + const { t } = useTranslation(); + const { passwordSeed } = useUserInfo(); + const { currentNetwork } = useWalletInfo(); + const { dappMap } = useDapp(); + const privateKey = useMemo( + () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), + [passwordSeed, wallet.AESEncryptPrivateKey], + ); + const curDapp = useMemo( + () => dappMap[currentNetwork]?.find((item) => item.origin === payload?.origin), + [currentNetwork, dappMap, payload?.origin], + ); + + const renderSite = useMemo( + () => + curDapp && ( +
    + + {curDapp.origin} +
    + ), + [curDapp], + ); + + const sendHandler = useCallback(async () => { + try { + if (!payload?.data) { + closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); + return; + } + if (!privateKey) throw 'Invalid user information, please check'; + + const manager = getWallet(privateKey); + if (!manager?.keyPair) { + closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.INTERNAL_ERROR, msg: 'invalid error' } }); + return; + } + + const data = manager.keyPair.sign(payload?.data); + closePrompt({ + ...errorHandler(0), + data, + }); + } catch (error) { + console.error(error, 'error===detail'); + message.error(handleErrorMessage(error)); + } + }, [payload, privateKey]); + + return ( +
    + {renderSite} +
    {t('Sign Message')}
    +
    +
    Message
    +
    {payload?.data}
    +
    +
    + + +
    +
    + ); +} From 3e81a7b5108aafc9ffa7318d0aa6c6b55381fa33 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 13 Jun 2023 13:19:42 +0800 Subject: [PATCH 093/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20resetDis?= =?UTF-8?q?cover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/hooks/hooks-ca/index.ts b/packages/hooks/hooks-ca/index.ts index f3fe5ad782..9f833d2b59 100644 --- a/packages/hooks/hooks-ca/index.ts +++ b/packages/hooks/hooks-ca/index.ts @@ -5,7 +5,6 @@ import { useAppCommonDispatch } from '../index'; import { resetAssets } from '@portkey-wallet/store/store-ca/assets/slice'; import { resetRecent } from '@portkey-wallet/store/store-ca/recent/slice'; import { resetActivity } from '@portkey-wallet/store/store-ca/activity/slice'; -import { resetDiscover } from '@portkey-wallet/store/store-ca/discover/slice'; import { resetContact } from '@portkey-wallet/store/store-ca/contact/actions'; import { resetGuardiansState } from '@portkey-wallet/store/store-ca/guardians/actions'; import { resetPayment } from '@portkey-wallet/store/store-ca/payment/actions'; @@ -21,6 +20,5 @@ export function useResetStore() { dispatch(resetGuardiansState()); dispatch(resetContact()); dispatch(resetPayment()); - dispatch(resetDiscover()); }, [dispatch]); } From 472019df0a04ea8ce244186e6cf3eb46286d9b0f Mon Sep 17 00:00:00 2001 From: Portkey-David <120542595+Portkey-David@users.noreply.github.com> Date: Tue, 13 Jun 2023 13:22:16 +0800 Subject: [PATCH 094/893] Delete packages/mobile-app-did/js/components/TabsDrawer/components/card directory --- .../TabsDrawer/components/card/index.tsx | 83 ------------------- 1 file changed, 83 deletions(-) delete mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx deleted file mode 100644 index 10689c755d..0000000000 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/card/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import GStyles from 'assets/theme/GStyles'; -import { TextM } from 'components/CommonText'; -import { TouchableOpacity } from 'react-native'; -import React from 'react'; -import { StyleSheet, View, Image } from 'react-native'; -import { pTd } from 'utils/unit'; - -import Svg from 'components/Svg'; - -import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -import { closeExistingTab, setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; -import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; -import { defaultColors } from 'assets/theme'; - -interface ICardsProps { - item: ITabItem; -} - -const Card: React.FC = (props: ICardsProps) => { - const { item } = props; - - const dispatch = useAppCommonDispatch(); - const { networkType } = useCurrentNetworkInfo(); - - return ( - - - - - {item?.name ?? getHost(item?.url)} - - dispatch(closeExistingTab({ id: item?.id, networkType }))}> - - - - dispatch(setActiveTab({ id: item.id, networkType }))}> - - - - ); -}; - -export default Card; - -const tabShowItemStyle = StyleSheet.create({ - cardWrap: { - borderRadius: pTd(8), - width: pTd(160), - height: pTd(214), - marginTop: pTd(24), - shadowOffset: { width: 10, height: 10 }, - backgroundColor: defaultColors.bg1, - shadowColor: defaultColors.shadow1, - shadowOpacity: 0.2, - shadowRadius: 10, - elevation: 2, - }, - header: { - ...GStyles.paddingArg(6, 8), - height: pTd(32), - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }, - title: { - flex: 1, - marginLeft: pTd(8), - marginRight: pTd(8), - }, - screenshot: { - width: pTd(160), - height: pTd(182), - }, -}); From eb019cb082f754da7d80df304f52c00e70451a29 Mon Sep 17 00:00:00 2001 From: Portkey-David <120542595+Portkey-David@users.noreply.github.com> Date: Tue, 13 Jun 2023 13:22:35 +0800 Subject: [PATCH 095/893] Delete packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay directory --- .../components/tabsOverlay/index.tsx | 210 ------------------ 1 file changed, 210 deletions(-) delete mode 100644 packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx deleted file mode 100644 index 3dbf0fd184..0000000000 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/tabsOverlay/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import React, { Dispatch, SetStateAction, useCallback } from 'react'; -import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, TouchableOpacity, View, Share } from 'react-native'; -import { TextL, TextS } from 'components/CommonText'; -import { defaultColors } from 'assets/theme'; -import fonts from 'assets/theme/fonts'; -import { pTd } from 'utils/unit'; -import Svg from 'components/Svg'; -import { useLanguage } from 'i18n/hooks'; -import GStyles from 'assets/theme/GStyles'; -import { screenWidth } from '@portkey-wallet/utils/mobile/device'; -import { FontStyles } from 'assets/theme/styles'; -import { setStringAsync } from 'expo-clipboard'; -import CommonToast from 'components/CommonToast'; -import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; - -import { isIOS } from '@rneui/base'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; -import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; - -enum HANDLE_TYPE { - REFRESH = 'Refresh', - COPY = 'Copy URL', - SHARE = 'Share', - CLOSE = 'Close', - CANCEL = 'Cancel', - SWITCH = 'Switch', -} - -const handleArray = [ - { title: HANDLE_TYPE.REFRESH, icon: 'refresh1' }, - { title: HANDLE_TYPE.COPY, icon: 'copy1' }, - { title: HANDLE_TYPE.SHARE, icon: 'share' }, - { title: HANDLE_TYPE.SWITCH, icon: 'switch' }, -] as const; - -const BrowserEditModal = ({ - browserInfo, - activeWebViewRef, - activeWebviewScreenShot, - setPreActiveTabId, -}: { - browserInfo: ITabItem; - activeWebViewRef: any; - activeWebviewScreenShot: () => void; - setPreActiveTabId: Dispatch>; -}) => { - const { t } = useLanguage(); - const dispatch = useAppCommonDispatch(); - const { networkType } = useCurrentNetworkInfo(); - - const handleUrl = useCallback( - async (type: HANDLE_TYPE) => { - let isCopy = false; - - switch (type) { - case HANDLE_TYPE.REFRESH: - activeWebViewRef?.current?.reload?.(); - OverlayModal.hide(); - break; - - case HANDLE_TYPE.COPY: - isCopy = await setStringAsync(browserInfo?.url || ''); - isCopy && CommonToast.success(t('Copy Success')); - break; - - case HANDLE_TYPE.SHARE: - await Share.share({ - message: isIOS ? browserInfo?.name ?? browserInfo.url : browserInfo?.url, - url: browserInfo?.url ?? browserInfo?.name ?? '', - title: browserInfo?.name ?? browserInfo.url, - }).catch(shareError => { - console.log(shareError); - }); - break; - - case HANDLE_TYPE.CLOSE: - OverlayModal.hide(); - break; - - case HANDLE_TYPE.CANCEL: - OverlayModal.hide(); - break; - - case HANDLE_TYPE.SWITCH: - OverlayModal.hide(); - activeWebviewScreenShot(); - dispatch(setActiveTab({ id: undefined, networkType })); - setPreActiveTabId(Number(browserInfo?.id)); - - break; - - default: - break; - } - }, - [ - activeWebViewRef, - browserInfo.url, - browserInfo?.name, - browserInfo?.id, - t, - activeWebviewScreenShot, - dispatch, - networkType, - setPreActiveTabId, - ], - ); - - return ( - - - - - {browserInfo?.name} - - - handleUrl(HANDLE_TYPE.CANCEL)}> - - - - - {handleArray.map((ele, index) => ( - handleUrl(ele.title)}> - - - - - {ele.title} - - - ))} - - - handleUrl(HANDLE_TYPE.CANCEL)}> - {t('Cancel')} - - - ); -}; - -export const showBrowserModal = (props: { - browserInfo: ITabItem; - activeWebViewRef: any; - activeWebviewScreenShot: () => void; - setPreActiveTabId: Dispatch>; -}) => { - OverlayModal.show(, { - position: 'bottom', - containerStyle: { backgroundColor: defaultColors.bg6 }, - }); -}; - -export default { - showBrowserModal, -}; - -const styles = StyleSheet.create({ - modalStyle: { - ...GStyles.paddingArg(16, 20), - backgroundColor: defaultColors.bg6, - width: screenWidth, - }, - title: { - textAlign: 'left', - height: pTd(22), - lineHeight: pTd(22), - marginVertical: pTd(13), - paddingLeft: pTd(8), - ...fonts.mediumFont, - }, - listWrap: { - marginTop: pTd(24), - marginBottom: pTd(24), - paddingLeft: pTd(12), - display: 'flex', - flexWrap: 'wrap', - flexDirection: 'row', - }, - listItem: { - marginRight: pTd(34), - borderRadius: pTd(6), - overflow: 'hidden', - }, - listItemNoMarginRight: { - marginRight: 0, - }, - svgWrap: { - backgroundColor: defaultColors.bg1, - }, - itemTitle: { - textAlign: 'center', - marginTop: pTd(8), - }, - divider: { - width: '100%', - height: StyleSheet.hairlineWidth, - backgroundColor: defaultColors.bg7, - }, - cancelButton: { - height: pTd(44), - fontSize: pTd(16), - }, -}); From 9bc3ba8e47d3a29a88adcd8010943c88d5d7c503 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 13:27:18 +0800 Subject: [PATCH 096/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20resetDis?= =?UTF-8?q?cover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/hooks/hooks-ca/index.ts b/packages/hooks/hooks-ca/index.ts index f3fe5ad782..9f833d2b59 100644 --- a/packages/hooks/hooks-ca/index.ts +++ b/packages/hooks/hooks-ca/index.ts @@ -5,7 +5,6 @@ import { useAppCommonDispatch } from '../index'; import { resetAssets } from '@portkey-wallet/store/store-ca/assets/slice'; import { resetRecent } from '@portkey-wallet/store/store-ca/recent/slice'; import { resetActivity } from '@portkey-wallet/store/store-ca/activity/slice'; -import { resetDiscover } from '@portkey-wallet/store/store-ca/discover/slice'; import { resetContact } from '@portkey-wallet/store/store-ca/contact/actions'; import { resetGuardiansState } from '@portkey-wallet/store/store-ca/guardians/actions'; import { resetPayment } from '@portkey-wallet/store/store-ca/payment/actions'; @@ -21,6 +20,5 @@ export function useResetStore() { dispatch(resetGuardiansState()); dispatch(resetContact()); dispatch(resetPayment()); - dispatch(resetDiscover()); }, [dispatch]); } From 50cac6cddb809f3f98c21c137fe659357a511315 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 13 Jun 2023 13:57:46 +0800 Subject: [PATCH 097/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20modify=20the?= =?UTF-8?q?=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controllers/methodController/AELFMethodController.ts | 4 ++-- .../app/web/pages/SendTransactions/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 91d6dbad77..79b82e35cb 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -216,9 +216,9 @@ export default class AELFMethodController { }); const isForward = chainInfo?.caContractAddress !== payload?.contractAddress; - const mth = isForward ? 'ManagerForwardCall' : payload?.method; + const method = isForward ? 'ManagerForwardCall' : payload?.method; - if (!CA_METHOD_WHITELIST.includes(mth)) + if (!CA_METHOD_WHITELIST.includes(method)) return sendResponse({ ...errorHandler(400001), data: { diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index e1c3495667..361421359a 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -61,7 +61,7 @@ export default function SendTransactions() { const getFee = useCallback(async () => { if (!privateKey) return; if (!chainInfo?.endPoint || !wallet?.caHash || !chainInfo.caContractAddress) return; - const mth = isCAContract ? payload?.method : 'ManagerForwardCall'; + const method = isCAContract ? payload?.method : 'ManagerForwardCall'; const paramsOption = isCAContract ? payload?.params?.paramsOption : { @@ -73,7 +73,7 @@ export default function SendTransactions() { const fee = await getTransferFee({ rpcUrl: chainInfo.endPoint, chainType: 'aelf', - methodName: mth, + methodName: method, paramsOption, privateKey, contractAddress: chainInfo.caContractAddress, From d94e0b4b6180eb608faed32d2c9a0fbf4bd8d152 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 13 Jun 2023 14:01:28 +0800 Subject: [PATCH 098/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20check=20sell=20t?= =?UTF-8?q?ips=20and=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/Preview/index.tsx | 2 +- .../web-extension-did/app/web/pages/Buy/index.tsx | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index cdc12c372a..e8477f4a5f 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -162,7 +162,7 @@ export default function Preview() {
    } /> diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index dd6cc68fe5..ca509543b1 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -326,6 +326,16 @@ export default function Buy() { const currentNetwork = useCurrentNetworkInfo(); const wallet = useCurrentWalletInfo(); + const setInsufficientFundsMsg = useCallback(() => { + stopInterval(); + + setErrMsg('Insufficient funds'); + valueSaveRef.current.isShowErrMsg = true; + + setReceive(''); + valueSaveRef.current.receive = ''; + }, [stopInterval]); + const handleNext = useCallback(async () => { if (valueSaveRef.current.side === PaymentTypeEnum.SELL) { if (!currentChain) return; @@ -344,7 +354,8 @@ export default function Buy() { if ( ZERO.plus(divDecimals(balance, 8)).isLessThanOrEqualTo(ZERO.plus(DEFAULT_FEE).plus(valueSaveRef.current.amount)) ) { - return message.error('balance is not enough'); // TODO SELL + setInsufficientFundsMsg(); + return; } } From 84178a80156ec66091b89b1f9b9d262aba142116 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 13 Jun 2023 14:05:26 +0800 Subject: [PATCH 099/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20modify=20the?= =?UTF-8?q?=20tip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controllers/methodController/AELFMethodController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 79b82e35cb..a0366bb475 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -223,7 +223,7 @@ export default class AELFMethodController { ...errorHandler(400001), data: { code: ResponseCode.CONTRACT_ERROR, - msg: 'method is not in the whitelist', + msg: 'The current method is not supported', }, }); From 062a5657ab50ee205f4015d260dd11e7129b386d Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 13 Jun 2023 14:10:33 +0800 Subject: [PATCH 100/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20dependence?= =?UTF-8?q?=20for=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Buy/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index ca509543b1..b02abbc2d6 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -371,7 +371,7 @@ export default function Buy() { tokenInfo: state ? state.tokenInfo : null, }, }); - }, [accountTokenList, currentChain, currentNetwork.walletType, navigate, state, wallet]); + }, [accountTokenList, currentChain, currentNetwork.walletType, navigate, setInsufficientFundsMsg, state, wallet]); const handleBack = useCallback(() => { if (state && state.tokenInfo) { From 987301ebd487d9dbeed7f4f3db34da512c5af273 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 13 Jun 2023 14:11:50 +0800 Subject: [PATCH 101/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20sell=20Toa?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 81 +++++++++++-------- .../js/components/Loading/index.tsx | 3 +- .../ViewOnWebView/hooks/useHandleAchSell.ts | 7 +- .../js/pages/Activity/ViewOnWebView/index.tsx | 4 +- .../Buy/BuyHome/components/BuyForm/index.tsx | 19 ++--- .../Buy/BuyHome/components/SellForm/index.tsx | 35 ++++---- .../mobile-app-did/js/pages/Buy/hooks.tsx | 7 +- 7 files changed, 84 insertions(+), 72 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 3a11cf4834..9e44faed74 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -43,46 +43,59 @@ export const useSellTransfer = () => { async ({ merchantName, orderId, paymentSellTransfer }: SellTransferParams) => { if (!isMainnet || merchantName !== ACH_MERCHANT_NAME) return; - const clientId = randomId(); - await signalrSell.doOpen({ - url: `${request.defaultConfig.baseURL}/ca`, - clientId, - }); + let achTxAddressReceived: AchTxAddressReceivedType; + try { + const clientId = randomId(); + await signalrSell.doOpen({ + url: `${request.defaultConfig.baseURL}/ca`, + clientId, + }); - const timerPromise = new Promise(resolve => - setTimeout(() => { - resolve(null); - }, SELL_SOCKET_TIMEOUT), - ); - const signalrSellPromise = new Promise(resolve => { - const { remove } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, data => { - resolve(data); - remove(); + const timerPromise = new Promise(resolve => + setTimeout(() => { + resolve(null); + }, SELL_SOCKET_TIMEOUT), + ); + const signalrSellPromise = new Promise(resolve => { + const { remove } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, data => { + resolve(data); + remove(); + }); + signalrSell.requestAchTxAddress(clientId, orderId); }); - signalrSell.requestAchTxAddress(clientId, orderId); - }); - const achTxAddressReceived = await Promise.race([timerPromise, signalrSellPromise]); - signalrSell.stop(); - if (achTxAddressReceived === null) { - throw new Error('requestAchTxAddress timeout'); + const signalrSellResult = await Promise.race([timerPromise, signalrSellPromise]); + signalrSell.stop(); + if (signalrSellResult === null) { + throw new Error('Transaction failed.'); + } + achTxAddressReceived = signalrSellResult; + } catch (error) { + throw { + code: 'TIMEOUT', + message: 'Transaction failed.', + }; } - const result = await paymentSellTransfer(achTxAddressReceived); - if (result.error) { - throw result.error; - } - if (!result.transactionId) { - throw new Error('transaction is error'); - } + try { + const result = await paymentSellTransfer(achTxAddressReceived); + if (result.error || !result.transactionId) { + throw new Error('Transaction failed.'); + } - await request.payment.updateAlchemyOrderTxHash({ - params: { - merchantName: ACH_MERCHANT_NAME, - orderId, - txHash: result.transactionId, - }, - }); + await request.payment.updateAlchemyOrderTxHash({ + params: { + merchantName: ACH_MERCHANT_NAME, + orderId, + txHash: result.transactionId, + }, + }); + } catch (error) { + throw { + code: 'NO_TX_HASH', + message: 'Transaction failed. Please contact the team for assistance.', + }; + } }, [isMainnet], ); diff --git a/packages/mobile-app-did/js/components/Loading/index.tsx b/packages/mobile-app-did/js/components/Loading/index.tsx index d2b29a94ce..b71d163bb3 100644 --- a/packages/mobile-app-did/js/components/Loading/index.tsx +++ b/packages/mobile-app-did/js/components/Loading/index.tsx @@ -82,7 +82,8 @@ const styles = StyleSheet.create({ }, loadingWrap: { width: pTd(224), - height: pTd(120), + minHeight: pTd(120), + padding: pTd(16), backgroundColor: defaultColors.bg1, borderRadius: pTd(6), }, diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index c0b07fe1a2..6135811658 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -67,17 +67,16 @@ export const useHandleAchSell = () => { return useCallback( async (orderId: string) => { try { - Loading.show(); + Loading.show({ text: 'Payment is being processed and may take around 10 seconds to complete.' }); await sellTransfer({ merchantName: ACH_MERCHANT_NAME, orderId, paymentSellTransfer, }); - // TODO: add success Toast + CommonToast.success('Transaction completed.'); } catch (error) { console.log('error', error); - // TODO: add Error Toast - CommonToast.fail('Transfer Error'); + CommonToast.failError(error); } finally { Loading.hide(); } diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx index 575248761c..e998181927 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx @@ -13,6 +13,7 @@ import { upDateRecordsItem } from '@portkey-wallet/store/store-ca/discover/slice import navigationService from 'utils/navigationService'; import { ACH_REDIRECT_URL, ACH_WITHDRAW_URL } from 'constants/common'; import { useHandleAchSell } from './hooks/useHandleAchSell'; +import CommonToast from 'components/CommonToast'; const safeAreaColorMap = { white: defaultColors.bg1, @@ -77,10 +78,9 @@ const ViewOnWebView: React.FC = () => { navigationService.navigate('Tab'); const { orderNo } = (params as AchSellParams) || {}; if (!orderNo) { - // TODO: add Toast + CommonToast.failError('Transaction failed.'); return; } - handleAchSell(orderNo); } } diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx index 6363edabf6..68524174d1 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx @@ -26,6 +26,8 @@ import { INIT_BUY_AMOUNT, tokenList } from 'pages/Buy/constants'; import Loading from 'components/Loading'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; import { useReceive } from 'pages/Buy/hooks'; +import BigNumber from 'bignumber.js'; +import { ZERO } from '@portkey-wallet/constants/misc'; export default function BuyForm() { const { buyFiatList: fiatList } = usePayment(); @@ -35,11 +37,9 @@ export default function BuyForm() { ); const [token, setToken] = useState(tokenList[0]); const [amount, setAmount] = useState(INIT_BUY_AMOUNT); - const [amountLocalError, setAmountLocalError] = useState(INIT_NONE_ERROR); const limitAmountRef = useRef(); - const refreshReceiveRef = useRef<() => void>(); const cryptoListRef = useRef(); const isRefreshReceiveValid = useRef(false); const cryptoListCurrency = useRef(); @@ -76,8 +76,8 @@ export default function BuyForm() { } limitAmountRef.current = { - min: cryptoInfo.minPurchaseAmount, - max: cryptoInfo.maxPurchaseAmount, + min: Number(ZERO.plus(cryptoInfo.minPurchaseAmount).decimalPlaces(4, BigNumber.ROUND_UP).valueOf()), + max: Number(ZERO.plus(cryptoInfo.maxPurchaseAmount).decimalPlaces(4, BigNumber.ROUND_DOWN).valueOf()), }; }, [fiat, token]); @@ -89,6 +89,7 @@ export default function BuyForm() { amountError: amountFetchError, isAllowAmount, } = useReceive(TypeEnum.BUY, amount, fiat, token, '', '', limitAmountRef, isRefreshReceiveValid); + const refreshReceiveRef = useRef(); refreshReceiveRef.current = refreshReceive; const amountError = useMemo(() => { @@ -122,13 +123,13 @@ export default function BuyForm() { }, []); const onNext = useCallback(async () => { - if (limitAmountRef.current === undefined) return; + if (!limitAmountRef.current || !refreshReceiveRef.current) return; const amountNum = Number(amount); const { min, max } = limitAmountRef.current; if (amountNum < min || amountNum > max) { setAmountLocalError({ ...INIT_HAS_ERROR, - errorMsg: `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${fiat?.currency}`, + errorMsg: `Limit Amount ${formatAmountShow(min, 4)}-${formatAmountShow(max, 4)} ${fiat?.currency}`, }); return; } @@ -137,9 +138,9 @@ export default function BuyForm() { if (isRefreshReceiveValid.current === false) { Loading.show(); - const rst = await refreshReceive(); + const rst = await refreshReceiveRef.current(); Loading.hide(); - if (rst === undefined) return; + if (!rst) return; _rate = rst.rate; _receiveAmount = rst.receiveAmount; } @@ -151,7 +152,7 @@ export default function BuyForm() { receiveAmount: _receiveAmount, rate: _rate, }); - }, [amount, fiat, rate, receiveAmount, refreshReceive, token]); + }, [amount, fiat, rate, receiveAmount, token]); return ( diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index 3692bcf537..a7fbcb5851 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -34,6 +34,7 @@ import { getELFChainBalance } from '@portkey-wallet/utils/balance'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { ZERO } from '@portkey-wallet/constants/misc'; import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; +import BigNumber from 'bignumber.js'; export default function SellForm() { const { sellFiatList: fiatList } = usePayment(); @@ -44,6 +45,7 @@ export default function SellForm() { const [token, setToken] = useState(tokenList[0]); const [amount, setAmount] = useState(INIT_SELL_AMOUNT); const [amountLocalError, setAmountLocalError] = useState(INIT_NONE_ERROR); + const { accountToken } = useAssets(); const aelfToken = useMemo( () => accountToken.accountTokenList.find(item => item.symbol === 'ELF' && item.chainId === 'AELF'), @@ -54,7 +56,6 @@ export default function SellForm() { const wallet = useCurrentWalletInfo(); const limitAmountRef = useRef(); - const refreshReceiveRef = useRef<() => void>(); const cryptoListRef = useRef(); const isRefreshReceiveValid = useRef(false); const cryptoListCurrency = useRef(); @@ -85,16 +86,14 @@ export default function SellForm() { item => item.crypto === token.crypto && item.network === token.network && Number(item.sellEnable) === 1, ); - console.log('cryptoInfo', cryptoInfo); - if (cryptoInfo === undefined || cryptoInfo.minSellAmount === null || cryptoInfo.maxSellAmount === null) { limitAmountRef.current = undefined; return; } limitAmountRef.current = { - min: cryptoInfo.minSellAmount, - max: cryptoInfo.maxSellAmount, + min: Number(ZERO.plus(cryptoInfo.minSellAmount).decimalPlaces(4, BigNumber.ROUND_UP).valueOf()), + max: Number(ZERO.plus(cryptoInfo.maxSellAmount).decimalPlaces(4, BigNumber.ROUND_DOWN).valueOf()), }; }, [fiat, token]); @@ -106,6 +105,7 @@ export default function SellForm() { amountError: amountFetchError, isAllowAmount, } = useReceive(TypeEnum.SELL, amount, fiat, token, '', '', limitAmountRef, isRefreshReceiveValid); + const refreshReceiveRef = useRef(); refreshReceiveRef.current = refreshReceive; const amountError = useMemo(() => { @@ -142,13 +142,13 @@ export default function SellForm() { }, []); const onNext = useCallback(async () => { - if (limitAmountRef.current === undefined) return; + if (!limitAmountRef.current || !refreshReceiveRef.current) return; const amountNum = Number(amount); const { min, max } = limitAmountRef.current; if (amountNum < min || amountNum > max) { setAmountLocalError({ ...INIT_HAS_ERROR, - errorMsg: `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${fiat?.currency}`, + errorMsg: `Limit Amount ${formatAmountShow(min, 4)}-${formatAmountShow(max, 4)} ${token.crypto}`, }); return; } @@ -159,10 +159,14 @@ export default function SellForm() { const { endPoint } = chainInfo || {}; if (!tokenContractAddress || decimals === undefined || !symbol || !chainId) return; if (!pin || !endPoint) return; - if (ZERO.plus(amount).isLessThanOrEqualTo(DEFAULT_FEE)) return; try { Loading.show(); + if (ZERO.plus(amount).isLessThanOrEqualTo(DEFAULT_FEE)) { + throw new Error('Insufficient funds'); + } + const isRefreshReceiveValidValue = isRefreshReceiveValid.current; + const account = getManagerAccount(pin); if (!account) return; @@ -175,18 +179,17 @@ export default function SellForm() { const balance = await getELFChainBalance(tokenContract, symbol, wallet?.[chainId]?.caAddress || ''); if (divDecimals(balance, decimals).minus(DEFAULT_FEE).isLessThan(amount)) { - // TODO: add Toast - return; + throw new Error('Insufficient funds'); } - if (isRefreshReceiveValid.current === false) { - const rst = await refreshReceive(); - if (rst === undefined) return; + if (isRefreshReceiveValidValue === false) { + const rst = await refreshReceiveRef.current(); + if (!rst) return; _rate = rst.rate; _receiveAmount = rst.receiveAmount; } } catch (error) { - // TODO: add Toast + setAmountLocalError({ ...INIT_HAS_ERROR, errorMsg: 'Insufficient funds' }); console.log('error', error); return; } finally { @@ -201,13 +204,13 @@ export default function SellForm() { receiveAmount: _receiveAmount, rate: _rate, }); - }, [amount, rate, receiveAmount, aelfToken, chainInfo, pin, fiat, token, wallet, refreshReceive]); + }, [amount, rate, receiveAmount, aelfToken, chainInfo, pin, fiat, token, wallet]); return ( max) { setAmountError({ ...INIT_HAS_ERROR, - errorMsg: `Limit Amount ${formatAmountShow(min, 4, BigNumber.ROUND_CEIL)}-${formatAmountShow(max, 4)} ${ + errorMsg: `Limit Amount ${formatAmountShow(min, 4)}-${formatAmountShow(max, 4)} ${ type === TypeEnum.BUY ? fiat?.currency : token.crypto }`, }); @@ -133,7 +132,6 @@ export const useReceive = ( } if (isRefreshReceiveValid) isRefreshReceiveValid.current = true; - const _rate = Number(rst.cryptoPrice).toFixed(2) + ''; let _receiveAmount = ''; if (type === TypeEnum.BUY) { @@ -151,9 +149,6 @@ export const useReceive = ( }; } catch (error) { console.log('error', error); - // CommonToast.failError(error); - // TODO: add error - // CommonToast.failError('get order error'); } }, [amount, clearRefreshReceive, fiat, isRefreshReceiveValid, limitAmountRef, registerRefreshReceive, token, type]); refreshReceiveRef.current = refreshReceive; From 765c007a94f30353c164766ac36afa8e083dc73a Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 15:50:51 +0800 Subject: [PATCH 102/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20transaction=20mo?= =?UTF-8?q?dal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/ModalBody/index.tsx | 1 + .../TabsDrawer/components/Card/index.tsx | 1 - .../js/dapp/components/SignOverlay/index.tsx | 17 +- .../TransactionDataSection/index.tsx | 33 +- .../components/TransactionOverlay/index.tsx | 290 +++++++----------- .../js/pages/Discover/DiscoverHome/index.tsx | 1 - packages/utils/dapp/browser.ts | 12 +- 7 files changed, 164 insertions(+), 191 deletions(-) diff --git a/packages/mobile-app-did/js/components/ModalBody/index.tsx b/packages/mobile-app-did/js/components/ModalBody/index.tsx index 43886b5588..ebc6590579 100644 --- a/packages/mobile-app-did/js/components/ModalBody/index.tsx +++ b/packages/mobile-app-did/js/components/ModalBody/index.tsx @@ -107,6 +107,7 @@ export const styles = StyleSheet.create({ width: pTd(48), }, buttonGroup: { + backgroundColor: defaultColors.bg1, position: 'absolute', bottom: 0, paddingRight: pTd(20), diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx index 10689c755d..e9d0d7a5fe 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx @@ -4,7 +4,6 @@ import { TouchableOpacity } from 'react-native'; import React from 'react'; import { StyleSheet, View, Image } from 'react-native'; import { pTd } from 'utils/unit'; - import Svg from 'components/Svg'; import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx index a830d2762d..5d0c794bf6 100644 --- a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import OverlayModal from 'components/OverlayModal'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; @@ -11,6 +11,8 @@ import { CommonButtonProps } from 'components/CommonButton'; import DappInfoSection from '../DappInfoSection'; import { GetSignatureParams } from '@portkey/provider-types'; +import TransactionDataSection from '../TransactionDataSection'; +import { TextL, TextXXXL } from 'components/CommonText'; interface SignModalPropsType { dappInfo: DappStoreItem; @@ -19,7 +21,7 @@ interface SignModalPropsType { onSign: () => void; } const SignModal = (props: SignModalPropsType) => { - const { dappInfo, onReject, onSign } = props; + const { dappInfo, signInfo, onReject, onSign } = props; const { t } = useLanguage(); const buttonList = useMemo( @@ -46,7 +48,11 @@ const SignModal = (props: SignModalPropsType) => { return ( - + + + Sign Message + + ); }; @@ -78,4 +84,9 @@ const styles = StyleSheet.create({ backgroundColor: defaultColors.bg9, ...GStyles.paddingArg(2, 8), }, + signTitle: { + marginTop: pTd(24), + marginBottom: pTd(24), + textAlign: 'center', + }, }); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx index d370ee6636..8a339d2e0e 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { StyleSheet, View, ViewStyle } from 'react-native'; import { defaultColors } from 'assets/theme'; import fonts from 'assets/theme/fonts'; @@ -6,22 +6,25 @@ import { pTd } from 'utils/unit'; import { TextM, TextS } from 'components/CommonText'; import Svg from 'components/Svg'; import { FontStyles } from 'assets/theme/styles'; +import GStyles from 'assets/theme/GStyles'; +import Touchable from 'components/Touchable'; interface TransactionDataSectionType { dataInfo: { [key: string]: any }; - collapsed: boolean; style?: ViewStyle; } export const TransactionDataSection = (props: TransactionDataSectionType) => { - const { dataInfo, collapsed, style = {} } = props; + const { dataInfo, style = {} } = props; + + const [collapsed, setCollapsed] = useState(true); const topSection = useMemo( () => ( - + setCollapsed(pre => !pre)}> Data - + ), [collapsed], ); @@ -29,12 +32,13 @@ export const TransactionDataSection = (props: TransactionDataSectionType) => { return ( {topSection} - {Object.entries(dataInfo).map(([key, value], index) => ( - - {key} - {value} - - ))} + {!collapsed && + Object.entries(dataInfo).map(([key, value], index) => ( + + {key} + {value} + + ))} ); }; @@ -46,17 +50,18 @@ const styles = StyleSheet.create({ width: pTd(335), borderWidth: StyleSheet.hairlineWidth, borderColor: defaultColors.border6, - paddingLeft: pTd(12), - paddingRight: pTd(12), + borderRadius: pTd(6), }, topSection: { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', + ...GStyles.paddingArg(16), }, dataInfoGroup: { - marginTop: pTd(16), + marginBottom: pTd(16), + ...GStyles.paddingArg(0, 16), }, dataValue: { marginTop: pTd(4), diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 2d6f29ba4a..96e44f875b 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -1,15 +1,15 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, View, Text } from 'react-native'; +import { StyleSheet, View, Text, ScrollView } from 'react-native'; import { defaultColors } from 'assets/theme'; import fonts from 'assets/theme/fonts'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; import { ModalBody } from 'components/ModalBody'; -import { TextL, TextM, TextS } from 'components/CommonText'; -import { useCurrentCaInfo, useCurrentWalletInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { TextM, TextS } from 'components/CommonText'; +import { useCurrentWalletInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; -import { formatAmountShow } from '@portkey-wallet/utils/converter'; +import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; @@ -24,8 +24,11 @@ import { usePin } from 'hooks/store'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount } from 'utils/redux'; import { customFetch } from '@portkey-wallet/utils/fetch'; -import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import DappInfoSection from '../DappInfoSection'; +import TransactionDataSection from '../TransactionDataSection'; +import { ELF_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; +import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; interface TransactionModalPropsType { dappInfo: DappStoreItem; @@ -48,14 +51,13 @@ const ConnectModal = (props: TransactionModalPropsType) => { const chainInfo = useCurrentChain(transactionInfo.chainId); const [tokenPriceObject, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); - const currentCaInfo = useCurrentCaInfo(); - const currentCaAddress = currentCaInfo?.[transactionInfo.chainId]?.caAddress; const isCAContract = useMemo( () => chainInfo?.caContractAddress === transactionInfo?.contractAddress, [chainInfo?.caContractAddress, transactionInfo?.contractAddress], ); const [fee, setFee] = useState(''); + const [noEnoughFee, setNoEnoughFee] = useState(false); const isTransfer = useMemo(() => transactionInfo.method.toLowerCase() === 'transfer', [transactionInfo.method]); @@ -81,20 +83,30 @@ const ConnectModal = (props: TransactionModalPropsType) => { [onReject, onSign, t], ); + const formatAmountInUsdShow = useCallback( + (amount: string | number, decimals: string | number, symbol: string) => { + const value = amountInUsdShow(amount, decimals, symbol); + if (symbol === 'ELF') { + return value === '$ 0' ? '<$ 0.01' : value; + } else { + return value; + } + }, + [amountInUsdShow], + ); + const transferContent = useMemo(() => { const { symbol, amount } = transactionInfo?.params?.paramsOption || {}; + const decimals = symbol === 'ELF' ? 8 : 0; return ( <> - {`- ${formatAmountShow(amount)} ${symbol}`} + {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`} - {!isMainnet && ( - {`-$ ${formatAmountShow( - ZERO.plus(amount).multipliedBy(tokenPriceObject[symbol]), - )}`} + {isMainnet && ( + {`formatAmountInUsdShow(amount, decimals, symbol)`} )} - {/* From */} @@ -127,102 +139,91 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Transaction Fee')} - {`${formatAmountShow(fee)} ELF`} - - - - {amountInUsdShow(fee, 0, 'ELF')} + {`${formatAmountShow( + divDecimals(fee, ELF_DECIMAL), + 8, + )} ELF`} + {isMainnet && ( + + + + {fee === '0' ? '$ 0' : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).valueOf(), 0, 'ELF')} + + + )} - {/* Total + {/* total */} + {symbol === 'ELF' ? ( - <> - {`${formatAmountShow(ZERO.plus(amount).plus(fee))} ${symbol}`} - {isMainnet && {amountInUsdShow(ZERO.plus(amount).plus(fee).toNumber(), 0, symbol)}} - + + + {t('Total')} + {`${formatAmountShow( + divDecimals(ZERO.plus(amount).plus(fee), decimals), + 8, + )} ${symbol}`} + + {isMainnet && ( + + + + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount).plus(fee)).toNumber(), 0, symbol)} + + + )} + ) : ( - <> - {`${formatAmountShow(fee)} ELF`} - {isMainnet && {amountInUsdShow(fee, 0, 'ELF')}} - {`${formatAmountShow(amount)} ${symbol}`} - {isMainnet && {amountInUsdShow(amount, 0, symbol)}} - - )} */} + + + {t('Total')} + {`${formatAmountShow( + divDecimals(ZERO.plus(amount), decimals), + 8, + )} ELF`} + + {isMainnet && ( + + + + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount), ELF_DECIMAL).toNumber(), 0, ELF_SYMBOL)} + + + )} + + + {`${formatAmountShow( + divDecimals(ZERO.plus(amount), decimals), + 8, + )} ${symbol}`} + + {isMainnet && ( + + + + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount), decimals).toNumber(), 0, symbol)} + + + )} + + )} + {noEnoughFee && Insufficient funds for transaction fee} ); }, [ - amountInUsdShow, fee, + formatAmountInUsdShow, isMainnet, + noEnoughFee, t, - tokenPriceObject, transactionInfo.chainId, transactionInfo?.params?.paramsOption, wallet, walletName, ]); - const otherTransactionContent = useMemo(() => { - const data = transactionInfo.params?.paramsOption || {}; - - return ( - - - {/* From */} - - - {t('From')} - {walletName} - - - - - {formatStr2EllipsisStr( - addressFormat(wallet?.[transactionInfo?.chainId]?.caAddress, transactionInfo.chainId), - )} - - - - {/* network */} - - - - {t('Network')} - - {formatChainInfoToShow(transactionInfo.chainId)} - - - - - {/* transactionFee */} - - - - {t('Transaction Fee')} - {`${formatAmountShow(fee)} ELF`} - - - - {amountInUsdShow(fee, 0, 'ELF')} - - - - - - Data - {Object.keys(data).map(item => ( - <> - {item} - {data[item]} - - ))} - - - ); - }, [amountInUsdShow, fee, t, transactionInfo.chainId, transactionInfo.params?.paramsOption, wallet, walletName]); - // get fee const getFee = useCallback(async () => { if (!chainInfo || !pin) return; @@ -257,8 +258,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { setFee(TransactionFee?.ELF); } catch (e) { - console.log(e); setFee('0'); + setNoEnoughFee(true); console.log('get fee error', e); } }, [ @@ -287,12 +288,18 @@ const ConnectModal = (props: TransactionModalPropsType) => { return ( - - - - {transactionInfo.method} - {isTransfer ? transferContent : otherTransactionContent} - + + + {transactionInfo?.method} + + {transferContent} + {isTransfer && ( + + )} + ); @@ -315,9 +322,6 @@ const styles = StyleSheet.create({ paddingRight: pTd(20), }, - title: { - marginBottom: pTd(2), - }, method: { borderRadius: pTd(6), marginTop: pTd(24), @@ -326,6 +330,18 @@ const styles = StyleSheet.create({ backgroundColor: defaultColors.bg9, ...GStyles.paddingArg(2, 8), }, + transactionDataSection: { + marginTop: pTd(16), + }, + scrollSection: { + height: screenHeight / 2, + paddingLeft: pTd(20), + }, + error: { + color: defaultColors.error, + textAlign: 'left', + marginTop: pTd(8), + }, }); const transferGroupStyle = StyleSheet.create({ @@ -348,32 +364,6 @@ const transferGroupStyle = StyleSheet.create({ paddingRight: pTd(16), borderRadius: pTd(6), }, - buttonWrapStyle: { - justifyContent: 'flex-end', - paddingBottom: pTd(12), - paddingTop: pTd(12), - }, - errorMessage: { - lineHeight: pTd(16), - color: defaultColors.error, - marginTop: pTd(4), - paddingLeft: pTd(8), - }, - wrap: { - height: pTd(56), - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - }, - borderTop: { - borderTopColor: defaultColors.border6, - borderTopWidth: StyleSheet.hairlineWidth, - }, - title: { - flex: 1, - color: defaultColors.font3, - }, tokenNum: { textAlign: 'right', color: defaultColors.font5, @@ -404,24 +394,12 @@ const transferGroupStyle = StyleSheet.create({ width: '100%', lineHeight: pTd(20), }, - titles1: { - marginTop: pTd(56), - }, - values1: { - marginTop: pTd(4), - }, divider: { marginTop: pTd(24), width: '100%', height: StyleSheet.hairlineWidth, backgroundColor: defaultColors.border6, }, - titles2: { - marginTop: pTd(25), - }, - values2: { - marginTop: pTd(4), - }, card: { marginTop: pTd(24), borderRadius: pTd(6), @@ -432,24 +410,6 @@ const transferGroupStyle = StyleSheet.create({ section: { ...GStyles.paddingArg(16, 12), }, - marginTop16: { - marginTop: pTd(16), - }, - marginTop4: { - marginTop: pTd(4), - }, - marginTop0: { - marginTop: 0, - }, - marginLeft8: { - marginLeft: pTd(8), - }, - space: { - flex: 1, - }, - button: { - marginBottom: pTd(30), - }, lightGrayFontColor: { color: defaultColors.font3, }, @@ -459,19 +419,7 @@ const transferGroupStyle = StyleSheet.create({ fontBold: { ...fonts.mediumFont, }, - greenFontColor: { - color: defaultColors.font10, - }, - alignItemsCenter: { - alignItems: 'center', - }, - alignItemsEnd: { - alignItems: 'flex-end', - }, - leftTitle: { - width: pTd(120), - lineHeight: pTd(20), + marginTop0: { + marginTop: 0, }, }); - -const otherTransactionGroupStyle = StyleSheet.create({}); diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index b3c3a8b4f8..5641ec9814 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -2,7 +2,6 @@ import React, { useCallback } from 'react'; import { StyleSheet, View } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import navigationService from 'utils/navigationService'; -import { useLanguage } from 'i18n/hooks'; import SimulatedInputBox from '../components/SimulatedInputBox'; import { DiscoverCmsListSection } from '../components/DiscoverCmsListSection'; import { defaultColors } from 'assets/theme'; diff --git a/packages/utils/dapp/browser.ts b/packages/utils/dapp/browser.ts index fda7084013..50b3b9c10c 100644 --- a/packages/utils/dapp/browser.ts +++ b/packages/utils/dapp/browser.ts @@ -17,10 +17,20 @@ export function getUrlObj(url: string) { * @param url * @returns */ -const isIp = (url: string): boolean => { +export const isIp = (url: string): boolean => { return /((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))/.test(url); }; +/** + * check if url is ip + * @param url + * @returns + */ +export const isDangerousLink = (url: string): boolean => { + if (isIp(url)) return true; + return /^(?:http:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!&',;=.+]+$/g.test(url); +}; + /** * Returns URL prefixed with protocol * From 347e2016c130558888ccba82b0d21fc9f85c97c7 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 13 Jun 2023 15:59:21 +0800 Subject: [PATCH 103/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20sell=20tex?= =?UTF-8?q?t=20and=20change=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/components/ScreenLoading/index.less | 9 +++- .../web/components/ScreenLoading/index.tsx | 7 ++- .../web/pages/Buy/hooks/useHandleAchSell.ts | 8 ++-- .../app/web/pages/Buy/index.tsx | 2 +- .../app/web/pages/Home/index.tsx | 45 ++++--------------- .../app/web/store/Provider/hooks.ts | 3 +- .../app/web/store/reducers/user/slice.ts | 2 + 7 files changed, 30 insertions(+), 46 deletions(-) diff --git a/packages/web-extension-did/app/web/components/ScreenLoading/index.less b/packages/web-extension-did/app/web/components/ScreenLoading/index.less index 9ebf18cde0..3b4f5f81b8 100644 --- a/packages/web-extension-did/app/web/components/ScreenLoading/index.less +++ b/packages/web-extension-did/app/web/components/ScreenLoading/index.less @@ -10,11 +10,11 @@ .loading-indicator { width: 224px; - height: 120px; + min-height: 120px; padding: 20px; background-color: @bg-11; border-radius: 6px; - + .loading { width: 44px; height: 44px; @@ -25,9 +25,14 @@ font-size: 14px; line-height: 20px; color: @font-11; + } + .ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .center { + text-align: center; + } } } diff --git a/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx b/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx index e6da96640c..fbb97fb8e8 100644 --- a/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx +++ b/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx @@ -1,10 +1,11 @@ import Loading from 'components/Loading'; import { useAppSelector } from 'store/Provider/hooks'; import './index.less'; +import clsx from 'clsx'; export default function ScreenLoading() { const { - loadingInfo: { isLoading, loadingText }, + loadingInfo: { isLoading, loadingText, isEllipsis }, } = useAppSelector((state) => state.userInfo); console.log(isLoading, 'isLoading==='); @@ -22,7 +23,9 @@ export default function ScreenLoading() { }>
    -
    {loadingText ? loadingText : 'Loading...'}
    +
    + {loadingText ? loadingText : 'Loading...'} +
    )} diff --git a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts index bb8affcc20..323962096e 100644 --- a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts +++ b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts @@ -69,15 +69,15 @@ export const useHandleAchSell = () => { return useCallback( async (orderId: string) => { try { - setLoading(true); + setLoading(true, 'Payment is being processed and may take around 10 seconds to complete.', false); await sellTransfer({ merchantName: ACH_MERCHANT_NAME, orderId, paymentSellTransfer, }); - } catch (error) { - console.log('error', error); - message.error('Transfer Error'); + message.success('Transaction completed.'); + } catch (error: any) { + message.error(error.message); } finally { setLoading(false); } diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index b02abbc2d6..4c8c1fe72e 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, Radio, RadioChangeEvent, message } from 'antd'; +import { Button, Radio, RadioChangeEvent } from 'antd'; import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import { useLocation, useNavigate } from 'react-router'; diff --git a/packages/web-extension-did/app/web/pages/Home/index.tsx b/packages/web-extension-did/app/web/pages/Home/index.tsx index 6f137fa2f3..0063cc4eed 100644 --- a/packages/web-extension-did/app/web/pages/Home/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/index.tsx @@ -1,70 +1,43 @@ import clsx from 'clsx'; import PortKeyHeader from 'pages/components/PortKeyHeader'; -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback, useRef } from 'react'; import { useNavigate, useLocation } from 'react-router'; -import { useCommonState, useLoading } from 'store/Provider/hooks'; -import popupHandler from 'utils/popupHandler'; -import { getLocalStorage } from 'utils/storage/chromeStorage'; +import { useCommonState } from 'store/Provider/hooks'; import MyBalance from './components/MyBalance'; import './index.less'; import qs from 'query-string'; import { useHandleAchSell } from 'pages/Buy/hooks/useHandleAchSell'; import { useStorage } from 'hooks/useStorage'; import walletMessage from 'messages/walletMessage'; +import { useEffectOnce } from 'react-use'; export default function Home() { const navigate = useNavigate(); - const { isPopupInit, isPrompt, isNotLessThan768 } = useCommonState(); + const { isPrompt, isNotLessThan768 } = useCommonState(); const onUserClick = useCallback(() => { const url = isNotLessThan768 ? `/setting/wallet` : `/setting`; navigate(url); }, [isNotLessThan768, navigate]); - const { setLoading } = useLoading(); const { search } = useLocation(); const isSell = useRef(0); // guaranteed to make only one transfer const handleAchSell = useHandleAchSell(); const locked = useStorage('locked'); - useEffect(() => { + const checkAchSell = useCallback(async () => { if (search) { const { detail, method } = qs.parse(search); - // if (detail) { - // // TODO SELL LOCKED - // } if (detail && method === walletMessage.ACH_SELL_REDIRECT && !locked && isSell.current === 0) { isSell.current = 1; - handleAchSell(detail); + await handleAchSell(detail); } } }, [handleAchSell, locked, search]); - const getLocationState = useCallback(async () => { - try { - if (!isPopupInit) return; - setLoading(1); - const isExpire = await popupHandler.popupActive(); - if (isExpire) { - setLoading(false); - return navigate('/'); - } - - const lastLocationState = await getLocalStorage('lastLocationState'); - setLoading(false); - if (!lastLocationState?.path) { - lastLocationState.path = '/'; - } - navigate(lastLocationState.path, { state: lastLocationState.state }); - } catch (error) { - setLoading(false); - console.log(error); - } - }, [isPopupInit, navigate, setLoading]); - - useEffect(() => { - getLocationState(); - }, [getLocationState, setLoading]); + useEffectOnce(() => { + checkAchSell(); + }); return (
    diff --git a/packages/web-extension-did/app/web/store/Provider/hooks.ts b/packages/web-extension-did/app/web/store/Provider/hooks.ts index f00a45696c..117c4f9a93 100644 --- a/packages/web-extension-did/app/web/store/Provider/hooks.ts +++ b/packages/web-extension-did/app/web/store/Provider/hooks.ts @@ -25,7 +25,8 @@ export const useLoading = () => { const { loadingInfo } = useAppSelector((state) => state.userInfo); const dispatch = useAppDispatch(); const setLoading = useCallback( - (isLoading: boolean | OpacityType, loadingText?: string) => dispatch(setGlobalLoading({ isLoading, loadingText })), + (isLoading: boolean | OpacityType, loadingText?: string, isEllipsis = true) => + dispatch(setGlobalLoading({ isLoading, loadingText, isEllipsis })), [dispatch], ); return useMemo(() => ({ isLoading: !!loadingInfo.isLoading, setLoading }), [loadingInfo.isLoading, setLoading]); diff --git a/packages/web-extension-did/app/web/store/reducers/user/slice.ts b/packages/web-extension-did/app/web/store/reducers/user/slice.ts index 6b25ca869e..c4b145ed55 100644 --- a/packages/web-extension-did/app/web/store/reducers/user/slice.ts +++ b/packages/web-extension-did/app/web/store/reducers/user/slice.ts @@ -4,6 +4,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface LoadingInfo { isLoading?: boolean | OpacityType; loadingText?: string; + isEllipsis?: boolean; } export interface UserState { @@ -16,6 +17,7 @@ export const initialState: UserState = { loadingInfo: { isLoading: false, loadingText: 'Loading...', + isEllipsis: true, }, }; From 4402ca327d5cdbfc0714b43b9fbdf4164f720d65 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 13 Jun 2023 17:14:23 +0800 Subject: [PATCH 104/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20sell=20?= =?UTF-8?q?timer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/index.tsx | 77 ++++++++----------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 4c8c1fe72e..bace572309 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -144,9 +144,8 @@ export default function Buy() { valueSaveRef.current.receive = ''; } }, [showLimitText]); - - const updateReceive = useCallback( - async ( + const { updateReceive, stopInterval } = useMemo(() => { + const updateReceive = async ( params = { crypto: valueSaveRef.current.crypto, network: valueSaveRef.current.network, @@ -164,45 +163,47 @@ export default function Buy() { setReceiveCase({ fiatQuantity, rampFee, cryptoQuantity }); setRate(cryptoPrice); setErrMsg(''); + if (!updateTimerRef.current && valueSaveRef.current.receive) { + resetTimer(); + } } catch (error) { setReceive(''); valueSaveRef.current.receive = ''; - setRate(''); + stopInterval(); setErrMsg(''); - console.log('error', error); } - }, - [setReceiveCase], - ); + }; - const handleSetTimer = useCallback(() => { - const timer = setInterval(() => { - updateTimerRef.current = timer; - --updateTimeRef.current; + const handleSetTimer = () => { + const timer = setInterval(() => { + updateTimerRef.current = timer; + --updateTimeRef.current; - if (updateTimeRef.current === 0) { - updateReceive(); - updateTimeRef.current = MAX_UPDATE_TIME; - } + if (updateTimeRef.current === 0) { + updateReceive(); + updateTimeRef.current = MAX_UPDATE_TIME; + } - setRateUpdateTime(updateTimeRef.current); - }, 1000); - }, [updateReceive]); + setRateUpdateTime(updateTimeRef.current); + }, 1000); + }; + const stopInterval = () => { + clearInterval(updateTimerRef.current); + updateTimerRef.current = undefined; + setRate(''); + }; - const stopInterval = useCallback(() => { - clearInterval(updateTimerRef.current); - updateTimerRef.current = undefined; - setRate(''); - }, []); + const resetTimer = () => { + clearInterval(updateTimerRef.current); + updateTimerRef.current = undefined; + updateTimeRef.current = MAX_UPDATE_TIME; + setRateUpdateTime(MAX_UPDATE_TIME); + handleSetTimer(); + }; - const resetTimer = useCallback(() => { - clearInterval(updateTimerRef.current); - updateTimerRef.current = undefined; - updateTimeRef.current = MAX_UPDATE_TIME; - setRateUpdateTime(MAX_UPDATE_TIME); - handleSetTimer(); - }, [handleSetTimer]); + return { updateReceive, handleSetTimer, stopInterval, resetTimer }; + }, [setReceiveCase]); const updateCrypto = useCallback(async () => { const { currency, crypto, network, side } = valueSaveRef.current; @@ -225,12 +226,9 @@ export default function Buy() { stopInterval(); } else { await updateReceive(); - if (!updateTimerRef.current && valueSaveRef.current.receive) { - resetTimer(); - } } } - }, [isValidValue, resetTimer, setErrMsgCase, stopInterval, updateReceive]); + }, [isValidValue, setErrMsgCase, stopInterval, updateReceive]); const handleInputChange = useCallback( async (v: string) => { @@ -251,11 +249,8 @@ export default function Buy() { amount: v, side, }); - if (!updateTimerRef.current && valueSaveRef.current.receive) { - resetTimer(); - } }, - [isValidValue, resetTimer, setErrMsgCase, stopInterval, updateReceive], + [isValidValue, setErrMsgCase, stopInterval, updateReceive], ); const handlePageChange = useCallback( @@ -302,10 +297,6 @@ export default function Buy() { return; } - setErrMsg(''); - setReceive(''); - valueSaveRef.current.receive = ''; - setRate(''); try { setLoading(true); await updateCrypto(); From b7aa7bb7717b1da8fe3a5d972a7de230e9cbaa67 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 13 Jun 2023 17:21:02 +0800 Subject: [PATCH 105/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20Correct=20in?= =?UTF-8?q?ternationalized=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/ConnectWallet/index.tsx | 10 +++------- .../web/pages/components/AccountConnectModal/index.tsx | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index 66dbde826a..50fdb034c9 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -10,11 +10,7 @@ import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; import './index.less'; -const allowItem = [ - 'viewing wallet balance and activity', - 'sending requests for transactions', - 'moving funds without your permission', -]; +const allowItem = ['view wallet balance and activities', 'send you transaction requests']; export default function ConnectWallet() { const detail = usePromptSearch(); @@ -47,7 +43,7 @@ export default function ConnectWallet() {
    - {t('Allow')} + {t('Allow this site to')}
    {item}
    @@ -92,7 +88,7 @@ export default function ConnectWallet() { {t('Reject')}
    diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx index 3656f38dd3..3b89dfa27f 100644 --- a/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/AccountConnectModal/index.tsx @@ -37,7 +37,7 @@ export default function AccountConnectModal({ open, onCancel }: IAccountConnectM ) : (
    {origin}
    -
    {t('To connect, locate the connect button on their site.')}
    +
    {t('To connect, please find and click the "Connect" button on this site.')}
    ), [dapp, handleDisConnect, origin, t], From d3fd43cfae64495cbac5cb7363228b884b8d3d55 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 13 Jun 2023 17:36:49 +0800 Subject: [PATCH 106/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Maximum=20and=20m?= =?UTF-8?q?inimum=20rounding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/index.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index bace572309..6a5f20e5f9 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -90,7 +90,7 @@ export default function Buy() { const showLimitText = useCallback( (min: string | number, max: string | number, fiat = 'USD') => - `Limit Amount ${formatAmountShow(min, 4, BigNumber.ROUND_CEIL)}-${formatAmountShow(max)} ${fiat} `, + `Limit Amount ${formatAmountShow(min)}-${formatAmountShow(max)} ${fiat} `, [], ); @@ -210,13 +210,19 @@ export default function Buy() { const data = await getCryptoInfo({ fiat: currency }, crypto, network, side); if (side === PaymentTypeEnum.BUY) { if (data && data.maxPurchaseAmount !== null && data.minPurchaseAmount !== null) { - valueSaveRef.current.max = data.maxPurchaseAmount; - valueSaveRef.current.min = data.minPurchaseAmount; + valueSaveRef.current.max = Number( + ZERO.plus(data.maxPurchaseAmount).decimalPlaces(4, BigNumber.ROUND_DOWN).valueOf(), + ); + valueSaveRef.current.min = Number( + ZERO.plus(data.minPurchaseAmount).decimalPlaces(4, BigNumber.ROUND_UP).valueOf(), + ); } } else { if (data && data.maxSellAmount !== null && data.minSellAmount !== null) { - valueSaveRef.current.max = data.maxSellAmount; - valueSaveRef.current.min = data.minSellAmount; + valueSaveRef.current.max = Number( + ZERO.plus(data.maxSellAmount).decimalPlaces(4, BigNumber.ROUND_DOWN).valueOf(), + ); + valueSaveRef.current.min = Number(ZERO.plus(data.minSellAmount).decimalPlaces(4, BigNumber.ROUND_UP).valueOf()); } } const { amount, min, max } = valueSaveRef.current; From 68a8732b1f74466ea47ee7e29614313657c1274b Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 13 Jun 2023 17:46:26 +0800 Subject: [PATCH 107/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Browser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 145 ++++-------------- .../js/components/ProviderWebview/index.tsx | 139 +++++++++++++++++ .../TabsDrawer/TabsDrawerContent.tsx | 142 +++++++++-------- .../js/components/TabsDrawer/context.ts | 17 ++ .../js/components/TabsDrawer/index.tsx | 7 +- packages/mobile-app-did/js/store/config.ts | 2 - .../mobile-app-did/js/store/rootReducer.ts | 2 +- packages/store/store-ca/discover/slice.ts | 20 ++- packages/store/store-ca/discover/type.ts | 3 +- packages/utils/dapp/dappManager.ts | 5 +- 10 files changed, 279 insertions(+), 203 deletions(-) create mode 100644 packages/mobile-app-did/js/components/ProviderWebview/index.tsx create mode 100644 packages/mobile-app-did/js/components/TabsDrawer/context.ts diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index adb15443f7..c5844c2456 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,136 +1,50 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { forwardRef, memo, useEffect, useImperativeHandle, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; -import { defaultColors } from 'assets/theme'; -import WebView from 'react-native-webview'; -import { pTd } from 'utils/unit'; -import useEffectOnce from 'hooks/useEffectOnce'; -import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; -import { MobileStream } from 'dapp/mobileStream'; -import DappMobileOperator from 'dapp/dappMobileOperator'; -import { WebViewErrorEvent, WebViewNavigationEvent } from 'react-native-webview/lib/WebViewTypes'; -import URL from 'url-parse'; -import { store } from 'store'; -import { DappOverlay } from 'dapp/dappOverlay'; -import { DappMobileManager } from 'dapp/dappManager'; -import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; -import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -import { isIos } from '@portkey-wallet/utils/mobile/device'; +import ProviderWebview, { IWebView } from 'components/ProviderWebview'; +import { captureRef } from 'react-native-view-shot'; +import { useBrowser } from 'components/TabsDrawer/context'; type BrowserTabProps = { isHidden: boolean; - item: ITabItem; - activeTabId: number | undefined; - setActiveTabRef: (ref: any) => void; - setActiveWebViewRef: (ref: any) => void; + uri: string; }; -const BrowserTab: React.FC = ({ - isHidden, - item, - activeTabId, - setActiveTabRef, - setActiveWebViewRef, -}) => { +const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri }, forward) { const viewRef = useRef(null); - const webViewRef = useRef(null); - const operatorRef = useRef(null); - const [entryScriptWeb3, setEntryScriptWeb3] = useState(); + const webViewRef = useRef(null); - useEffectOnce(() => { - const getEntryScriptWeb3 = async () => { - const script = await EntryScriptWeb3.get(); - setEntryScriptWeb3(script); - }; + const { setTabRef } = useBrowser(); - getEntryScriptWeb3(); - return () => { - operatorRef?.current?.onDestroy(); - }; - }); - - const initOperator = useCallback( - (origin: string) => { - if (!isIos) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - webViewRef.current?.injectJavaScript(entryScriptWeb3!); - } - operatorRef.current = new DappMobileOperator({ - origin, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - stream: new MobileStream(webViewRef.current!), - dappManager: new DappMobileManager({ store: store as any }), - dappOverlay: new DappOverlay(), - }); - }, - [entryScriptWeb3], - ); - - const onLoadStart = useCallback( - ({ nativeEvent }: WebViewNavigationEvent) => { - const { origin } = new URL(nativeEvent.url); - initOperator(origin); - }, - [initOperator], + useImperativeHandle( + forward, + () => ({ + capture: () => captureRef(viewRef?.current), + reload: () => webViewRef.current?.reload(), + }), + [], ); - const handleUpdate = useCallback(({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { - const { origin, pathname = '', query = '' } = new URL(nativeEvent.url); - const realUrl = `${origin}${pathname}${query}`; - const icon = getFaviconUrl(realUrl, 50); - operatorRef.current?.updateDappInfo({ - origin, - name: nativeEvent.title, - icon, - }); - }, []); useEffect(() => { - if (viewRef && viewRef.current && activeTabId === item.id) { - setActiveTabRef(viewRef); - } - }, [activeTabId, item.id, setActiveTabRef, viewRef]); - - useEffect(() => { - if (webViewRef && webViewRef.current) { - setActiveWebViewRef(webViewRef); - } - }, [webViewRef, setActiveWebViewRef]); - - if (!entryScriptWeb3) return null; + if (isHidden) return; + setTabRef?.({ + capture: () => captureRef(viewRef?.current), + reload: () => webViewRef.current?.reload(), + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isHidden]); return ( - { - operatorRef.current?.handleRequestMessage(nativeEvent.data); - }} - onLoadStart={onLoadStart} - onLoad={handleUpdate} - onLoadProgress={({ nativeEvent }) => { - console.log(nativeEvent.progress, '=onLoadProgress'); - }} - onLoadEnd={handleUpdate} - applicationNameForUserAgent={'WebView Portkey did Mobile'} - /> + ); -}; +}); -export default BrowserTab; +export default memo(BrowserTab, (prevProps: BrowserTabProps, nextProps: BrowserTabProps) => { + return prevProps.isHidden === nextProps.isHidden && prevProps.uri === nextProps.uri; +}); export const styles = StyleSheet.create({ - pageWrap: { - paddingLeft: 0, - paddingRight: 0, - backgroundColor: defaultColors.bg1, - }, - svgWrap: { - marginRight: pTd(16), - }, webViewContainerHidden: { flex: 0, opacity: 0, @@ -142,9 +56,4 @@ export const styles = StyleSheet.create({ width: screenWidth, height: screenHeight, }, - webView: { - flex: 1, - zIndex: 1, - }, - noResult: {}, }); diff --git a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx new file mode 100644 index 0000000000..d49d0bbc5a --- /dev/null +++ b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx @@ -0,0 +1,139 @@ +import React, { forwardRef, memo, useCallback, useImperativeHandle, useRef, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import WebView, { WebViewProps } from 'react-native-webview'; +import useEffectOnce from 'hooks/useEffectOnce'; +import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; +import { MobileStream } from 'dapp/mobileStream'; +import DappMobileOperator from 'dapp/dappMobileOperator'; +import { WebViewErrorEvent, WebViewNavigationEvent } from 'react-native-webview/lib/WebViewTypes'; +import URL from 'url-parse'; +import { store } from 'store'; +import { DappOverlay } from 'dapp/dappOverlay'; +import { DappMobileManager } from 'dapp/dappManager'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import { isIos } from '@portkey-wallet/utils/mobile/device'; + +export interface IWebView { + goBack: WebView['goBack']; + reload: WebView['reload']; + postMessage: WebView['postMessage']; + injectJavaScript: WebView['injectJavaScript']; + goForward: WebView['goForward']; +} + +const ProviderWebview = forwardRef(function ProviderWebview(props, forward) { + const webViewRef = useRef(null); + const operatorRef = useRef(null); + const [entryScriptWeb3, setEntryScriptWeb3] = useState(); + useEffectOnce(() => { + const getEntryScriptWeb3 = async () => { + const script = await EntryScriptWeb3.get(); + setEntryScriptWeb3(script); + }; + + getEntryScriptWeb3(); + return () => { + operatorRef.current?.onDestroy(); + }; + }); + + const initOperator = useCallback( + (origin: string) => { + if (!isIos) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + webViewRef.current?.injectJavaScript(entryScriptWeb3!); + } + operatorRef.current = new DappMobileOperator({ + origin, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stream: new MobileStream(webViewRef.current!), + dappManager: new DappMobileManager({ store: store as any }), + dappOverlay: new DappOverlay(), + }); + }, + [entryScriptWeb3], + ); + + const onLoadStart = useCallback( + ({ nativeEvent }: WebViewNavigationEvent) => { + const { origin } = new URL(nativeEvent.url); + initOperator(origin); + }, + [initOperator], + ); + const handleUpdate = useCallback(({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { + const { origin, pathname = '', query = '' } = new URL(nativeEvent.url); + const realUrl = `${origin}${pathname}${query}`; + const icon = getFaviconUrl(realUrl, 50); + operatorRef.current?.updateDappInfo({ + origin, + name: nativeEvent.title, + icon, + }); + }, []); + useImperativeHandle( + forward, + () => ({ + goBack: () => webViewRef.current?.goBack(), + /** + * Go forward one page in the webview's history. + */ + goForward: () => webViewRef.current?.goForward(), + + /** + * Reloads the current page. + */ + reload: () => webViewRef.current?.reload(), + + /** + * Stop loading the current page. + */ + stopLoading: () => webViewRef.current?.reload(), + + /** + * Executes the JavaScript string. + */ + injectJavaScript: (script: string) => webViewRef.current?.injectJavaScript(script), + + /** + * Focuses on WebView redered page. + */ + requestFocus: () => webViewRef.current?.requestFocus(), + + /** + * Posts a message to WebView. + */ + postMessage: (message: string) => webViewRef.current?.postMessage(message), + }), + [], + ); + if (!entryScriptWeb3) return null; + return ( + { + operatorRef.current?.handleRequestMessage(nativeEvent.data); + }} + onLoadStart={onLoadStart} + onLoad={handleUpdate} + onLoadProgress={({ nativeEvent }) => { + console.log(nativeEvent.progress, '=onLoadProgress'); + }} + onLoadEnd={handleUpdate} + applicationNameForUserAgent={'WebView Portkey did Mobile'} + {...props} + /> + ); +}); + +export default memo(ProviderWebview); + +export const styles = StyleSheet.create({ + webView: { + flex: 1, + zIndex: 1, + }, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 7cecb240d5..274bdad630 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -3,7 +3,7 @@ import GStyles from 'assets/theme/GStyles'; import { TextM } from 'components/CommonText'; import PageContainer from 'components/PageContainer'; import { TouchableOpacity } from 'react-native'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { StyleSheet, ScrollView, View } from 'react-native'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca/index'; import { pTd } from 'utils/unit'; @@ -22,38 +22,35 @@ import { } from '@portkey-wallet/store/store-ca/discover/slice'; import BrowserTab from 'components/BrowserTab'; import { showBrowserModal } from './components/TabsOverlay'; -import { captureRef } from 'react-native-view-shot'; import { showWalletInfo } from './components/WalletInfoOverlay'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; - -const takeSnapshot = (viewRef: any) => captureRef(viewRef?.current, { format: 'jpg', quality: 0.2 }); +import { BrowserContext, IBrowserTab } from './context'; const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { isDrawerOpen, discoverMap } = useAppCASelector(state => state.discover); - const { activeTabId, tabs } = discoverMap[networkType] ?? {}; + const { isDrawerOpen, discoverMap, initializedList, activeTabId } = useAppCASelector(state => state.discover); + + const { tabs } = discoverMap[networkType] ?? {}; - const [activeTabRef, setActiveTabRef] = React.useState(null); - const [activeWebViewRef, setActiveWebViewRef] = React.useState(null); + const tabRef = useRef(null); const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); const activeWebviewScreenShot = useCallback(() => { - console.log('==========activeTabRef==========================', activeTabRef); - - takeSnapshot(activeTabRef).then( + if (!activeTabId) return; + tabRef.current?.capture?.().then( uri => { console.log('Image saved to', uri); - dispatch(updateTab({ id: activeTabId || 0, screenShotUrl: uri, networkType })); + dispatch(updateTab({ id: activeTabId, screenShotUrl: uri, networkType })); }, error => console.error('Oops, snapshot failed', error), ); - }, [activeTabId, activeTabRef, dispatch, networkType]); + }, [activeTabId, dispatch, networkType]); const backToSearchPage = useCallback(() => { activeWebviewScreenShot(); @@ -64,7 +61,6 @@ const TabsDrawerContent: React.FC = () => { // header right const rightDom = useMemo(() => { const activeItem = tabs?.find(ele => ele.id === activeTabId) as ITabItem; - if (activeTabId) return ( @@ -75,7 +71,7 @@ const TabsDrawerContent: React.FC = () => { onPress={() => showBrowserModal({ browserInfo: activeItem, - activeWebViewRef, + activeWebViewRef: tabRef.current, activeWebviewScreenShot, setPreActiveTabId, }) @@ -86,62 +82,72 @@ const TabsDrawerContent: React.FC = () => { ); return null; - }, [activeTabId, activeWebViewRef, activeWebviewScreenShot, tabs]); + }, [activeTabId, activeWebviewScreenShot, tabs]); + + const tabsDom = useMemo(() => { + return tabs?.map(ele => { + const isHidden = activeTabId !== ele.id; + const initialized = initializedList?.has(ele.id); + if (isHidden && !initialized) return; + return ; + }); + }, [activeTabId, initializedList, tabs]); + + const value = useMemo( + () => ({ + setTabRef: (ref: IBrowserTab) => { + tabRef.current = ref; + }, + }), + [], + ); return ( - } - leftCallback={backToSearchPage} - rightDom={rightDom} - safeAreaColor={['blue', 'white']} - containerStyles={styles.container} - scrollViewProps={{ disabled: true }} - titleDom={activeTabId ? '' : `${tabs?.length} Tabs`}> - {tabs?.map(ele => ( - - ))} - - {/* card group */} - {!activeTabId && isDrawerOpen && ( - <> - - - {tabs?.map(ele => ( - - ))} + + } + leftCallback={backToSearchPage} + rightDom={rightDom} + safeAreaColor={['blue', 'white']} + containerStyles={styles.container} + scrollViewProps={{ disabled: true }} + titleDom={activeTabId ? '' : `${tabs?.length} Tabs`}> + {tabsDom} + {/* card group */} + {!activeTabId && isDrawerOpen && ( + <> + + + {tabs?.map(ele => ( + + ))} + + + + dispatch(closeAllTabs({ networkType }))}> + {t('Close All')} + + dispatch(changeDrawerOpenStatus(false))}> + + + { + if (tabs?.length === 0) return; + if (tabs?.find(ele => ele.id === preActiveTabId)) { + dispatch(setActiveTab({ id: preActiveTabId, networkType })); + } else { + dispatch(setActiveTab({ id: tabs?.[tabs?.length - 1]?.id, networkType })); + } + }}> + {t('Done')} + - - - dispatch(closeAllTabs({ networkType }))}> - {t('Close All')} - - dispatch(changeDrawerOpenStatus(false))}> - - - { - if (tabs?.length === 0) return; - if (tabs?.find(ele => ele.id === preActiveTabId)) { - dispatch(setActiveTab({ id: preActiveTabId, networkType })); - } else { - dispatch(setActiveTab({ id: tabs?.[tabs?.length - 1]?.id, networkType })); - } - }}> - {t('Done')} - - - - )} - + + )} + + ); }; diff --git a/packages/mobile-app-did/js/components/TabsDrawer/context.ts b/packages/mobile-app-did/js/components/TabsDrawer/context.ts new file mode 100644 index 0000000000..fbc80d3f31 --- /dev/null +++ b/packages/mobile-app-did/js/components/TabsDrawer/context.ts @@ -0,0 +1,17 @@ +import { IWebView } from 'components/ProviderWebview'; +import { createContext, useContext } from 'react'; +import ViewShot from 'react-native-view-shot'; + +export interface BrowserState { + setTabRef?: (ref: any) => void; +} +export const BrowserContext = createContext({}); + +export function useBrowser(): BrowserState { + return useContext(BrowserContext); +} + +export interface IBrowserTab { + reload: IWebView['reload']; + capture: ViewShot['capture']; +} diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx index 77dface772..4db173178d 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -3,6 +3,7 @@ import { ScreenWidth } from '@rneui/base'; import React from 'react'; import { Drawer } from 'react-native-drawer-layout'; import TabsDrawerContent from './TabsDrawerContent'; +import { usePin } from 'hooks/store'; interface TabsDrawerPropsType { children: React.ReactNode; @@ -11,16 +12,14 @@ interface TabsDrawerPropsType { export default function TabsDrawer(props: TabsDrawerPropsType) { const { children } = props; + const pin = usePin(); const { isDrawerOpen } = useAppCASelector(state => state.discover); const tabsDrawerContent = React.useMemo(() => , []); - return ( console.log('open')} - onClose={() => console.log('close')} drawerPosition="right" drawerStyle={{ width: ScreenWidth }} renderDrawerContent={() => { diff --git a/packages/mobile-app-did/js/store/config.ts b/packages/mobile-app-did/js/store/config.ts index f2879ed2ee..03b5b03ed6 100644 --- a/packages/mobile-app-did/js/store/config.ts +++ b/packages/mobile-app-did/js/store/config.ts @@ -12,7 +12,6 @@ import { tokenBalanceSlice } from '@portkey-wallet/store/tokenBalance/slice'; import settingsSlice from '@portkey-wallet/store/settings/slice'; import recentSlice from '@portkey-wallet/store/store-ca/recent/slice'; import activitySlice from '@portkey-wallet/store/store-ca/activity/slice'; -import discoverSlice from '@portkey-wallet/store/store-ca/discover/slice'; import switchSlice from '@portkey-wallet/store/store-ca/switch/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; @@ -44,7 +43,6 @@ const reduxPersistConfig = { chainSlice.name, recentSlice.name, activitySlice.name, - discoverSlice.name, switchSlice.name, dappSlice.name, cmsSlice.name, diff --git a/packages/mobile-app-did/js/store/rootReducer.ts b/packages/mobile-app-did/js/store/rootReducer.ts index e2cef66c27..fefc977493 100644 --- a/packages/mobile-app-did/js/store/rootReducer.ts +++ b/packages/mobile-app-did/js/store/rootReducer.ts @@ -67,7 +67,7 @@ const guardiansPersistConfig = { const discoverPersistConfig = { key: discoverSlice.name, storage: AsyncStorage, - blacklist: ['isDrawerOpen'], + blacklist: ['isDrawerOpen', 'initializedList', 'activeTabId'], }; const paymentPersistConfig = { diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 847543a242..45874dc778 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -1,17 +1,19 @@ import { createSlice } from '@reduxjs/toolkit'; import { IDiscoverStateType, IDiscoverNetworkStateType, ITabItem } from './type'; import { NetworkType } from '@portkey-wallet/types'; - +import { enableMapSet } from 'immer'; +enableMapSet(); const initNetworkData: IDiscoverNetworkStateType = { recordsList: [], whiteList: [], - activeTabId: undefined, tabs: [], }; const initialState: IDiscoverStateType = { isDrawerOpen: false, discoverMap: {}, + activeTabId: undefined, + initializedList: new Set(), }; //it automatically uses the immer library to let you write simpler immutable updates with normal mutative code @@ -56,7 +58,7 @@ export const discoverSlice = createSlice({ const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); targetNetworkDiscover.tabs = []; - targetNetworkDiscover.activeTabId = undefined; + state.activeTabId = undefined; }, createNewTab: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { const { networkType, id } = payload; @@ -68,7 +70,7 @@ export const discoverSlice = createSlice({ targetNetworkDiscover.tabs.push({ ...payload }); } - targetNetworkDiscover.activeTabId = id; + state.activeTabId = id; }, closeExistingTab: (state, { payload }: { payload: { id: number; networkType: NetworkType } }) => { const { networkType, id } = payload; @@ -77,10 +79,12 @@ export const discoverSlice = createSlice({ targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.filter(item => item.id !== id); }, setActiveTab: (state, { payload }: { payload: { id: number | undefined; networkType: NetworkType } }) => { - const { networkType, id } = payload; - const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - - targetNetworkDiscover.activeTabId = id; + const { id } = payload; + if (!state.initializedList) state.initializedList = new Set(); + console.log(state.activeTabId, '====state.activeTabId1'); + id && state.initializedList.add(id); + state.activeTabId = id; + console.log(state.activeTabId, '====state.activeTabId2'); }, updateTab: (state, { payload }: { payload: { networkType: NetworkType; id: number; [key: string]: any } }) => { const { networkType, id } = payload; diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index 8c6ecef37c..33c0980825 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -10,7 +10,6 @@ export interface ITabItem { export interface IDiscoverNetworkStateType { recordsList: ITabItem[]; whiteList: any[]; - activeTabId?: number; tabs: ITabItem[]; } @@ -19,4 +18,6 @@ export interface IDiscoverStateType { discoverMap: { [key in NetworkType]?: IDiscoverNetworkStateType; }; + initializedList?: Set; + activeTabId?: number; } diff --git a/packages/utils/dapp/dappManager.ts b/packages/utils/dapp/dappManager.ts index bf31884ac4..47c7deb0f7 100644 --- a/packages/utils/dapp/dappManager.ts +++ b/packages/utils/dapp/dappManager.ts @@ -103,7 +103,10 @@ export abstract class DappManager async chainsInfo() { const chainsInfo: ChainsInfo = {}; (await this.getCurrentChainList())?.forEach(chainInfo => { - chainsInfo[chainInfo.chainId] = [chainInfo]; + const tmpChainInfo: any = { ...chainInfo }; + tmpChainInfo.lastModifyTime && delete tmpChainInfo.lastModifyTime; + tmpChainInfo.id && delete tmpChainInfo.id; + chainsInfo[chainInfo.chainId] = [tmpChainInfo]; }); return chainsInfo; } From 186c6f38c78bf52727d6f2c550d3b6a7b1bfdc3c Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 13 Jun 2023 17:49:34 +0800 Subject: [PATCH 108/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20DrawerProps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/TabsDrawer/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx index 4db173178d..b147a9c4a4 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -20,6 +20,8 @@ export default function TabsDrawer(props: TabsDrawerPropsType) { console.log('open')} + onClose={() => console.log('close')} drawerPosition="right" drawerStyle={{ width: ScreenWidth }} renderDrawerContent={() => { From 860d59a76abdb36031da7c5ccfd47c6e9cb000cc Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 13 Jun 2023 18:16:55 +0800 Subject: [PATCH 109/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20transaction=20err?= =?UTF-8?q?or=20&=20unique=20key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/SendTransactions/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 361421359a..99ef97a9bf 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -26,6 +26,7 @@ export default function SendTransactions() { chainId: ChainId; contractAddress: string; method: string; + rpcUrl: string; params: any; }; }>(); @@ -177,10 +178,10 @@ export default function SendTransactions() {
    Message
    {Object.keys(params).map((item) => ( - <> +
    {item}
    {params[item]}
    - +
    ))}
    @@ -200,7 +201,7 @@ export default function SendTransactions() { closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid chain id' } }); return; } - if (chainInfo?.endPoint !== payload?.params?.rpcUrl) { + if (chainInfo?.endPoint !== payload?.rpcUrl) { closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid rpcUrl' } }); return; } From e8030de3ac77245f5c04c98a8535644f003d3341 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 18:26:38 +0800 Subject: [PATCH 110/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20limit=20number?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/discover.ts | 2 ++ .../components/TransactionOverlay/index.tsx | 30 ++++++++++--------- packages/store/store-ca/discover/slice.ts | 12 ++++++-- 3 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 packages/constants/constants-ca/discover.ts diff --git a/packages/constants/constants-ca/discover.ts b/packages/constants/constants-ca/discover.ts new file mode 100644 index 0000000000..62fd06beb2 --- /dev/null +++ b/packages/constants/constants-ca/discover.ts @@ -0,0 +1,2 @@ +export const RECORD_LIMIT = 100; +export const TAB_LIMIT = 50; diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 96e44f875b..0b1e305c06 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -105,7 +105,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`} {isMainnet && ( - {`formatAmountInUsdShow(amount, decimals, symbol)`} + {`${formatAmountInUsdShow(amount, decimals, symbol)}`} )} {/* From */} @@ -144,11 +144,11 @@ const ConnectModal = (props: TransactionModalPropsType) => { 8, )} ELF`} - {isMainnet && ( + {!isMainnet && ( - {fee === '0' ? '$ 0' : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).valueOf(), 0, 'ELF')} + {fee === '0' ? '$ 0' : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).toNumber(), 0, 'ELF')} )} @@ -165,12 +165,12 @@ const ConnectModal = (props: TransactionModalPropsType) => { 8, )} ${symbol}`} - {isMainnet && ( + {!isMainnet && ( - + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount).plus(fee)).toNumber(), 0, symbol)} - + )} @@ -179,16 +179,16 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Total')} {`${formatAmountShow( - divDecimals(ZERO.plus(amount), decimals), + divDecimals(ZERO.plus(fee), ELF_DECIMAL), 8, )} ELF`} - {isMainnet && ( + {!isMainnet && ( - - {formatAmountInUsdShow(divDecimals(ZERO.plus(amount), ELF_DECIMAL).toNumber(), 0, ELF_SYMBOL)} - + + {fee === '0' ? '$ 0' : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).toNumber(), 0, 'ELF')} + )} @@ -198,10 +198,10 @@ const ConnectModal = (props: TransactionModalPropsType) => { 8, )} ${symbol}`} - {isMainnet && ( + {!isMainnet && ( - + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount), decimals).toNumber(), 0, symbol)} @@ -256,7 +256,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, }); - setFee(TransactionFee?.ELF); + if (!TransactionFee && !TransactionFee?.ELF) return setNoEnoughFee(true); + + setFee(TransactionFee?.ELF || '0'); } catch (e) { setFee('0'); setNoEnoughFee(true); diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 1a03ad3761..ddab01612d 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -2,6 +2,8 @@ import { createSlice } from '@reduxjs/toolkit'; import { IDiscoverStateType, IDiscoverNetworkStateType, ITabItem } from './type'; import { NetworkType } from '@portkey-wallet/types'; import { enableMapSet } from 'immer'; +import { RECORD_LIMIT, TAB_LIMIT } from '@portkey-wallet/constants/constants-ca/discover'; + enableMapSet(); const initNetworkData: IDiscoverNetworkStateType = { recordsList: [], @@ -36,6 +38,11 @@ export const discoverSlice = createSlice({ const targetItem = state.discoverMap?.[networkType]?.recordsList.find(item => item.url === url); const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + // limit number + if (RECORD_LIMIT <= targetNetworkDiscover.recordsList.length) { + targetNetworkDiscover.tabs.shift(); + } + if (targetItem) { const arr = state.discoverMap?.[networkType]?.recordsList.filter(item => item.url !== url) || []; arr.push(targetItem); @@ -72,6 +79,9 @@ export const discoverSlice = createSlice({ if (!targetNetworkDiscover?.tabs) { targetNetworkDiscover.tabs = [{ ...payload }]; } else { + if (TAB_LIMIT <= targetNetworkDiscover.tabs.length) { + targetNetworkDiscover.tabs.shift(); + } targetNetworkDiscover.tabs.push({ ...payload }); } @@ -86,10 +96,8 @@ export const discoverSlice = createSlice({ setActiveTab: (state, { payload }: { payload: { id: number | undefined; networkType: NetworkType } }) => { const { id } = payload; if (!state.initializedList) state.initializedList = new Set(); - console.log(state.activeTabId, '====state.activeTabId1'); id && state.initializedList.add(id); state.activeTabId = id; - console.log(state.activeTabId, '====state.activeTabId2'); }, updateTab: (state, { payload }: { payload: { networkType: NetworkType; id: number; [key: string]: any } }) => { const { networkType, id } = payload; From ddeb11ad58a860d699434b1686c87100e914bc05 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 18:35:33 +0800 Subject: [PATCH 111/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20android=20back?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 15 ++++++--------- .../js/components/CustomHeader/index.tsx | 4 +++- .../components/TabsDrawer/TabsDrawerContent.tsx | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index c5844c2456..d66f419738 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, memo, useEffect, useImperativeHandle, useRef } from 'react'; +import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import ProviderWebview, { IWebView } from 'components/ProviderWebview'; @@ -16,23 +16,20 @@ const BrowserTab = forwardRef(function BrowserTab({ isHidd const { setTabRef } = useBrowser(); - useImperativeHandle( - forward, + const options = useMemo( () => ({ - capture: () => captureRef(viewRef?.current), + capture: () => captureRef(viewRef?.current, { quality: 0.2, format: 'jpg' }), reload: () => webViewRef.current?.reload(), }), [], ); + useImperativeHandle(forward, () => options, [options]); useEffect(() => { if (isHidden) return; - setTabRef?.({ - capture: () => captureRef(viewRef?.current), - reload: () => webViewRef.current?.reload(), - }); + setTabRef?.(options); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isHidden]); + }, [isHidden, options]); return ( diff --git a/packages/mobile-app-did/js/components/CustomHeader/index.tsx b/packages/mobile-app-did/js/components/CustomHeader/index.tsx index ea16f93fcf..9bf4b79f98 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/index.tsx +++ b/packages/mobile-app-did/js/components/CustomHeader/index.tsx @@ -24,6 +24,7 @@ export type CustomHeaderProps = { type?: 'leftBack' | 'default'; leftIconType?: 'close' | 'back'; style?: StyleProp; + isHardwareBackPress?: boolean; }; const CustomHeader: React.FC = props => { @@ -41,6 +42,7 @@ const CustomHeader: React.FC = props => { style, leftIconType = 'back', onGestureStartCallback, + isHardwareBackPress, } = props; // theme change @@ -63,7 +65,7 @@ const CustomHeader: React.FC = props => { ); }, [leftIconType, styles.leftBackTitle.color]); useHardwareBackPress(() => { - if (isFocused && leftCallback) { + if ((isHardwareBackPress || isFocused) && leftCallback) { leftCallback(); return true; } diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 47156d6167..f1639f2b84 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -106,6 +106,7 @@ const TabsDrawerContent: React.FC = () => { } leftCallback={backToSearchPage} rightDom={rightDom} From 4d4420fd0a0285a873ef2db452d1ade6f4457132 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 19:42:52 +0800 Subject: [PATCH 112/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20transfe?= =?UTF-8?q?r=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/Progressbar/index.tsx | 21 +++ .../components/TransactionOverlay/index.tsx | 143 +++++++++--------- 2 files changed, 96 insertions(+), 68 deletions(-) create mode 100644 packages/mobile-app-did/js/components/Progressbar/index.tsx diff --git a/packages/mobile-app-did/js/components/Progressbar/index.tsx b/packages/mobile-app-did/js/components/Progressbar/index.tsx new file mode 100644 index 0000000000..35461be8ba --- /dev/null +++ b/packages/mobile-app-did/js/components/Progressbar/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { pTd } from 'utils/unit'; + +interface ProgressbarProps { + percentage: number; +} + +export function Progressbar(props: ProgressbarProps) { + const { percentage } = props; + + return ; +} + +const styles = StyleSheet.create({ + progressBar: { + width: '100%', + justifyContent: 'space-between', + paddingBottom: pTd(40), + }, +}); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 0b1e305c06..b07e9c2887 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -13,7 +13,6 @@ import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; -import { useGStyles } from 'assets/theme/useGStyles'; import { CommonButtonProps } from 'components/CommonButton'; import { SendTransactionParams } from '@portkey/provider-types'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; @@ -24,11 +23,10 @@ import { usePin } from 'hooks/store'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount } from 'utils/redux'; import { customFetch } from '@portkey-wallet/utils/fetch'; -import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { screenHeight } from '@portkey-wallet/utils/mobile/device'; import DappInfoSection from '../DappInfoSection'; import TransactionDataSection from '../TransactionDataSection'; import { ELF_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; interface TransactionModalPropsType { dappInfo: DappStoreItem; @@ -39,7 +37,6 @@ interface TransactionModalPropsType { const ConnectModal = (props: TransactionModalPropsType) => { const { dappInfo, transactionInfo, onReject, onSign } = props; const { t } = useLanguage(); - const gStyles = useGStyles(); const isMainnet = useIsMainnet(); const pin = usePin(); @@ -49,7 +46,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { const amountInUsdShow = useAmountInUsdShow(); const chainInfo = useCurrentChain(transactionInfo.chainId); - const [tokenPriceObject, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); + const [, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); const isCAContract = useMemo( () => chainInfo?.caContractAddress === transactionInfo?.contractAddress, @@ -101,11 +98,15 @@ const ConnectModal = (props: TransactionModalPropsType) => { return ( <> - - {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`} - - {isMainnet && ( - {`${formatAmountInUsdShow(amount, decimals, symbol)}`} + {isTransfer && ( + <> + + {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`} + + {isMainnet && ( + {`${formatAmountInUsdShow(amount, decimals, symbol)}`} + )} + )} {/* From */} @@ -144,7 +145,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { 8, )} ELF`} - {!isMainnet && ( + {isMainnet && ( @@ -155,58 +156,64 @@ const ConnectModal = (props: TransactionModalPropsType) => { {/* total */} - - {symbol === 'ELF' ? ( - - - {t('Total')} - {`${formatAmountShow( - divDecimals(ZERO.plus(amount).plus(fee), decimals), - 8, - )} ${symbol}`} - - {!isMainnet && ( - - - - {formatAmountInUsdShow(divDecimals(ZERO.plus(amount).plus(fee)).toNumber(), 0, symbol)} - + {isTransfer && ( + <> + + {symbol === 'ELF' ? ( + + + {t('Total')} + {`${formatAmountShow( + divDecimals(ZERO.plus(amount).plus(fee), decimals), + 8, + )} ${symbol}`} + + {isMainnet && ( + + + + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount).plus(fee)).toNumber(), 0, symbol)} + + + )} - )} - - ) : ( - - - {t('Total')} - {`${formatAmountShow( - divDecimals(ZERO.plus(fee), ELF_DECIMAL), - 8, - )} ELF`} - - {!isMainnet && ( - - - - {fee === '0' ? '$ 0' : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).toNumber(), 0, 'ELF')} - + ) : ( + + + {t('Total')} + {`${formatAmountShow( + divDecimals(ZERO.plus(fee), ELF_DECIMAL), + 8, + )} ELF`} + + {isMainnet && ( + + + + {fee === '0' + ? '$ 0' + : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).toNumber(), 0, 'ELF')} + + + )} + + + {`${formatAmountShow( + divDecimals(ZERO.plus(amount), decimals), + 8, + )} ${symbol}`} + + {isMainnet && ( + + + + {formatAmountInUsdShow(divDecimals(ZERO.plus(amount), decimals).toNumber(), 0, symbol)} + + + )} )} - - - {`${formatAmountShow( - divDecimals(ZERO.plus(amount), decimals), - 8, - )} ${symbol}`} - - {!isMainnet && ( - - - - {formatAmountInUsdShow(divDecimals(ZERO.plus(amount), decimals).toNumber(), 0, symbol)} - - - )} - + )} {noEnoughFee && Insufficient funds for transaction fee} @@ -216,6 +223,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { fee, formatAmountInUsdShow, isMainnet, + isTransfer, noEnoughFee, t, transactionInfo.chainId, @@ -295,7 +303,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {transactionInfo?.method} {transferContent} - {isTransfer && ( + {!isTransfer && ( Date: Tue, 13 Jun 2023 20:58:19 +0800 Subject: [PATCH 113/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20android=20screenS?= =?UTF-8?q?hot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 5 +++- .../TabsDrawer/TabsDrawerContent.tsx | 10 ++------ .../components/TabsOverlay/index.tsx | 25 +++++++++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index d66f419738..702b592ed8 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -31,7 +31,10 @@ const BrowserTab = forwardRef(function BrowserTab({ isHidd // eslint-disable-next-line react-hooks/exhaustive-deps }, [isHidden, options]); return ( - + ); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index f1639f2b84..6a3ac25ac4 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -43,14 +43,8 @@ const TabsDrawerContent: React.FC = () => { const activeWebviewScreenShot = useCallback(() => { if (!activeTabId) return; - tabRef.current?.capture?.().then( - uri => { - console.log('Image saved to', uri); - dispatch(updateTab({ id: activeTabId, screenShotUrl: uri, networkType })); - }, - error => console.error('Oops, snapshot failed', error), - ); - }, [activeTabId, dispatch, networkType]); + return tabRef.current?.capture?.(); + }, [activeTabId]); const backToSearchPage = useCallback(() => { activeWebviewScreenShot(); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index 3dbf0fd184..8c3a7935f1 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -15,8 +15,8 @@ import CommonToast from 'components/CommonToast'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { isIOS } from '@rneui/base'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { setActiveTab } from '@portkey-wallet/store/store-ca/discover/slice'; +import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { setActiveTab, updateTab } from '@portkey-wallet/store/store-ca/discover/slice'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; @@ -45,16 +45,18 @@ const BrowserEditModal = ({ }: { browserInfo: ITabItem; activeWebViewRef: any; - activeWebviewScreenShot: () => void; + activeWebviewScreenShot: () => Promise; setPreActiveTabId: Dispatch>; }) => { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); const { networkType } = useCurrentNetworkInfo(); + const { activeTabId } = useAppCASelector(state => state.discover); const handleUrl = useCallback( async (type: HANDLE_TYPE) => { let isCopy = false; + let uri = ''; switch (type) { case HANDLE_TYPE.REFRESH: @@ -86,10 +88,18 @@ const BrowserEditModal = ({ break; case HANDLE_TYPE.SWITCH: - OverlayModal.hide(); - activeWebviewScreenShot(); - dispatch(setActiveTab({ id: undefined, networkType })); - setPreActiveTabId(Number(browserInfo?.id)); + if (!activeTabId) return; + + try { + uri = await activeWebviewScreenShot(); + + OverlayModal.hide(); + dispatch(setActiveTab({ id: undefined, networkType })); + dispatch(updateTab({ id: activeTabId, screenShotUrl: uri, networkType })); + setPreActiveTabId(Number(browserInfo?.id)); + } catch (error) { + console.error('Oops, snapshot failed', error); + } break; @@ -103,6 +113,7 @@ const BrowserEditModal = ({ browserInfo?.name, browserInfo?.id, t, + activeTabId, activeWebviewScreenShot, dispatch, networkType, From f3e76ea56226ac4b8f1c279fe8388c0dc22871ca Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 13 Jun 2023 20:58:47 +0800 Subject: [PATCH 114/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20modal=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TabsDrawer/components/WalletInfoOverlay/index.tsx | 2 +- .../js/dapp/components/TransactionOverlay/index.tsx | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx index a7c5fd2637..19660cc575 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx @@ -57,7 +57,7 @@ const MyWalletModal = () => { - {`${formatAmountShow(divDecimals(item?.balance, item?.decimals))} ${item?.symbol}`} + {`${formatAmountShow(divDecimals(item?.balance, item?.decimals))} ${item?.symbol || '0'}`} diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index b07e9c2887..99f5816fb8 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -172,7 +172,11 @@ const ConnectModal = (props: TransactionModalPropsType) => { - {formatAmountInUsdShow(divDecimals(ZERO.plus(amount).plus(fee)).toNumber(), 0, symbol)} + {formatAmountInUsdShow( + divDecimals(ZERO.plus(amount).plus(fee), ELF_DECIMAL).toNumber(), + 0, + symbol, + )} )} @@ -309,6 +313,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { style={styles.transactionDataSection} /> )} + @@ -346,7 +351,9 @@ const styles = StyleSheet.create({ }, scrollSection: { height: screenHeight / 2, - paddingBottom: pTd(100), + }, + blank: { + height: pTd(300), }, error: { color: defaultColors.error, From 85e0d3aa530ea75e0e9c99861954b03fb9facd81 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Wed, 14 Jun 2023 10:40:50 +0800 Subject: [PATCH 115/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20discover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/Test/Discover/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/Test/Discover/index.tsx b/packages/mobile-app-did/js/Test/Discover/index.tsx index 1a8e2394d3..beb4997097 100644 --- a/packages/mobile-app-did/js/Test/Discover/index.tsx +++ b/packages/mobile-app-did/js/Test/Discover/index.tsx @@ -6,7 +6,7 @@ import navigationService from 'utils/navigationService'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; import CommonButton from 'components/CommonButton'; import DappEventBus from 'dapp/dappEventBus'; -import BrowserTab from 'components/BrowserTab'; +import ProviderWebview from 'components/ProviderWebview'; const safeAreaColorMap = { white: defaultColors.bg1, @@ -22,8 +22,7 @@ const Discover: React.FC = () => { return ( - - + { DappEventBus.dispatchEvent({ From 5e61dd50d1c67663cf4aede5feff745048ba8fa7 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 14 Jun 2023 11:26:53 +0800 Subject: [PATCH 116/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20data=20update=20&?= =?UTF-8?q?=20save=20tabId=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/controllers/SWEventController.ts | 3 +++ .../app/web/controllers/openNewTabController/index.ts | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.ts b/packages/web-extension-did/app/web/controllers/SWEventController.ts index d555bc47a2..107b30e63a 100644 --- a/packages/web-extension-did/app/web/controllers/SWEventController.ts +++ b/packages/web-extension-did/app/web/controllers/SWEventController.ts @@ -25,6 +25,7 @@ import { getDappState, getWalletState } from 'utils/lib/SWGetReduxStore'; import InternalMessage from 'messages/InternalMessage'; import { handleAccounts, handleChainIds } from '@portkey-wallet/utils/dapp'; import { addDapp, removeDapp, resetDapp, resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { sleep } from '@portkey-wallet/utils'; export interface DappEventPack { eventName: T; @@ -131,6 +132,8 @@ export default class SWEventController { // Trigger events based on user operations to notify service workers public static async emit(action: string, payload: any) { console.log(action, payload, 'action==action'); + // Asynchronous updates lead to data exceptions when emitting + await sleep(50); switch (action) { case changeNetworkType.toString(): { const { currentNetwork } = await getWalletState(); diff --git a/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts b/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts index 8ab2558e75..6fb6cee878 100644 --- a/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts +++ b/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts @@ -7,7 +7,10 @@ export default class OpenNewTabController { } openNewTab() { const createdListener = async (tab: chrome.tabs.Tab) => { - saveOpenTabs(tab.id || ''); + const extId = apis.runtime.id; + if (tab.id && extId && (tab.id + '').includes(extId)) { + saveOpenTabs(tab.id); + } }; apis.tabs.onCreated.addListener(createdListener); From 38b1d295dd5d013e4343114fa4132987244acfd1 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 14:14:00 +0800 Subject: [PATCH 117/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20progressBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 13 ++++- .../js/components/Progressbar/index.tsx | 52 +++++++++++++++---- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 702b592ed8..d080355c35 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,9 +1,10 @@ -import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; +import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import ProviderWebview, { IWebView } from 'components/ProviderWebview'; import { captureRef } from 'react-native-view-shot'; import { useBrowser } from 'components/TabsDrawer/context'; +import Progressbar from 'components/Progressbar'; type BrowserTabProps = { isHidden: boolean; @@ -13,6 +14,7 @@ type BrowserTabProps = { const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri }, forward) { const viewRef = useRef(null); const webViewRef = useRef(null); + const progressbarRef = useRef(null); const { setTabRef } = useBrowser(); @@ -30,12 +32,18 @@ const BrowserTab = forwardRef(function BrowserTab({ isHidd setTabRef?.(options); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isHidden, options]); + return ( - + + progressbarRef.current.changeInnerBarWidth(nativeEvent.progress)} + /> ); }); @@ -51,6 +59,7 @@ export const styles = StyleSheet.create({ display: 'none', width: 0, height: 0, + position: 'relative', }, webViewContainer: { width: screenWidth, diff --git a/packages/mobile-app-did/js/components/Progressbar/index.tsx b/packages/mobile-app-did/js/components/Progressbar/index.tsx index 35461be8ba..f2fe0ab5f6 100644 --- a/packages/mobile-app-did/js/components/Progressbar/index.tsx +++ b/packages/mobile-app-did/js/components/Progressbar/index.tsx @@ -1,21 +1,51 @@ -import React from 'react'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { defaultColors } from 'assets/theme'; +import React, { forwardRef, useImperativeHandle, useMemo, useState } from 'react'; import { View, StyleSheet } from 'react-native'; import { pTd } from 'utils/unit'; - -interface ProgressbarProps { - percentage: number; +export interface IProgressbar { + changeInnerBarWidth: (per: number) => void; } -export function Progressbar(props: ProgressbarProps) { - const { percentage } = props; +const Progressbar = forwardRef(function Progressbar(props, ref) { + const [percentage, setPercentage] = useState(0); - return ; -} + const innerBarWidthStyle = useMemo(() => { + const num = Number(percentage); + return { width: `${num === 1 ? 0 : num * 100}%` }; + }, [percentage]); + + useImperativeHandle( + ref, + () => ({ + changeInnerBarWidth(per: number) { + setPercentage(per); + }, + }), + [], + ); + + return ( + + + + ); +}); + +export default Progressbar; const styles = StyleSheet.create({ progressBar: { - width: '100%', - justifyContent: 'space-between', - paddingBottom: pTd(40), + zIndex: 100, + height: pTd(2), + width: screenWidth, + position: 'absolute', + top: 0, + left: 0, + }, + innerBar: { + height: '100%', + zIndex: 101, + backgroundColor: defaultColors.bg8, }, }); From 940f37c6ea690e9f1060387fd8f588df7598c997 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 14:14:53 +0800 Subject: [PATCH 118/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20iOS=20and=20?= =?UTF-8?q?android=20shot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TabsDrawer/TabsDrawerContent.tsx | 16 +++++++++---- .../TabsDrawer/components/Card/index.tsx | 1 + .../components/TabsOverlay/index.tsx | 24 ++++--------------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 6a3ac25ac4..6455a3d3a9 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -41,16 +41,22 @@ const TabsDrawerContent: React.FC = () => { const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); - const activeWebviewScreenShot = useCallback(() => { + const activeWebviewScreenShot = useCallback(async () => { if (!activeTabId) return; - return tabRef.current?.capture?.(); - }, [activeTabId]); + + try { + const uri = await tabRef.current?.capture?.(); + dispatch(setActiveTab({ id: undefined, networkType })); + dispatch(updateTab({ id: activeTabId, screenShotUrl: uri, networkType })); + } catch (error) { + console.log(error); + } + }, [activeTabId, dispatch, networkType]); const backToSearchPage = useCallback(() => { activeWebviewScreenShot(); - dispatch(setActiveTab({ id: undefined, networkType })); dispatch(changeDrawerOpenStatus(false)); - }, [activeWebviewScreenShot, dispatch, networkType]); + }, [activeWebviewScreenShot, dispatch]); // header right const rightDom = useMemo(() => { diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx index e9d0d7a5fe..2ebc995811 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx @@ -52,6 +52,7 @@ export default Card; const tabShowItemStyle = StyleSheet.create({ cardWrap: { + overflow: 'hidden', borderRadius: pTd(8), width: pTd(160), height: pTd(214), diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index 8c3a7935f1..e4aba3d9d8 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -15,11 +15,9 @@ import CommonToast from 'components/CommonToast'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { isIOS } from '@rneui/base'; -import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { setActiveTab, updateTab } from '@portkey-wallet/store/store-ca/discover/slice'; +import { useAppCASelector } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; enum HANDLE_TYPE { REFRESH = 'Refresh', @@ -45,18 +43,15 @@ const BrowserEditModal = ({ }: { browserInfo: ITabItem; activeWebViewRef: any; - activeWebviewScreenShot: () => Promise; + activeWebviewScreenShot: () => void; setPreActiveTabId: Dispatch>; }) => { const { t } = useLanguage(); - const dispatch = useAppCommonDispatch(); - const { networkType } = useCurrentNetworkInfo(); const { activeTabId } = useAppCASelector(state => state.discover); const handleUrl = useCallback( async (type: HANDLE_TYPE) => { let isCopy = false; - let uri = ''; switch (type) { case HANDLE_TYPE.REFRESH: @@ -90,16 +85,9 @@ const BrowserEditModal = ({ case HANDLE_TYPE.SWITCH: if (!activeTabId) return; - try { - uri = await activeWebviewScreenShot(); - - OverlayModal.hide(); - dispatch(setActiveTab({ id: undefined, networkType })); - dispatch(updateTab({ id: activeTabId, screenShotUrl: uri, networkType })); - setPreActiveTabId(Number(browserInfo?.id)); - } catch (error) { - console.error('Oops, snapshot failed', error); - } + activeWebviewScreenShot(); + OverlayModal.hide(); + setPreActiveTabId(Number(browserInfo?.id)); break; @@ -115,8 +103,6 @@ const BrowserEditModal = ({ t, activeTabId, activeWebviewScreenShot, - dispatch, - networkType, setPreActiveTabId, ], ); From 95768b493f773bf2913215c5592ab6c1ffd9acf9 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 15:06:15 +0800 Subject: [PATCH 119/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20serviceworker?= =?UTF-8?q?=20back?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../serviceWorker/ServiceWorkerInstantiate.ts | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 8eee1a1c73..7eb2f03873 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -204,6 +204,8 @@ export default class ServiceWorkerInstantiate { this.aelfMethodController.dispenseMessage(message, sendResponse); break; } + sendResponse(errorHandler(700001, `Portkey does not contain this method (${message.type})`)); + break; } } @@ -348,30 +350,6 @@ export default class ServiceWorkerInstantiate { sendResponse(errorHandler(0)); } - /** - * Determine whether the portkey is locked, and if not, get the list of authorized users - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getAddress(sendResponse: SendResponseFun, _message: any) { - try { - sendResponse(errorHandler(700001)); - } catch (error) { - sendResponse(errorHandler(500001, error)); - } - } - - /** - * Dapp connection portkey - */ - async connectWallet(sendResponse: SendResponseFun, message: any) { - try { - sendResponse(errorHandler(700001, message)); - } catch (error) { - console.log(error, 'connectWallet=='); - return sendResponse(errorHandler(500001, error)); - } - } - async notificationServiceClose( sendResponse: SendResponseFun, payload: { From 495b29963b10f42e3fe03b7cc49b1add5d1a066e Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Wed, 14 Jun 2023 17:14:47 +0800 Subject: [PATCH 120/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20android=20Hardwar?= =?UTF-8?q?eBackPress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/CustomHeader/index.tsx | 3 +++ .../TabsDrawer/TabsDrawerContent.tsx | 12 ++++++++++-- packages/mobile-app-did/js/navigation/Tab.tsx | 3 ++- .../mobile-app-did/js/navigation/index.tsx | 19 ++++++++++++------- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/components/CustomHeader/index.tsx b/packages/mobile-app-did/js/components/CustomHeader/index.tsx index ea16f93fcf..f52fa7b392 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/index.tsx +++ b/packages/mobile-app-did/js/components/CustomHeader/index.tsx @@ -24,6 +24,7 @@ export type CustomHeaderProps = { type?: 'leftBack' | 'default'; leftIconType?: 'close' | 'back'; style?: StyleProp; + notHandleHardwareBackPress?: boolean; }; const CustomHeader: React.FC = props => { @@ -41,6 +42,7 @@ const CustomHeader: React.FC = props => { style, leftIconType = 'back', onGestureStartCallback, + notHandleHardwareBackPress, } = props; // theme change @@ -63,6 +65,7 @@ const CustomHeader: React.FC = props => { ); }, [leftIconType, styles.leftBackTitle.color]); useHardwareBackPress(() => { + if (notHandleHardwareBackPress) return false; if (isFocused && leftCallback) { leftCallback(); return true; diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 47156d6167..cc46145810 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -28,6 +28,7 @@ import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { BrowserContext, IBrowserTab } from './context'; +import { useHardwareBackPress } from '@portkey-wallet/hooks/mobile'; const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); @@ -101,14 +102,21 @@ const TabsDrawerContent: React.FC = () => { }), [], ); - + useHardwareBackPress(() => { + if (isDrawerOpen) { + backToSearchPage(); + return true; + } + return false; + }); return ( } leftCallback={backToSearchPage} - rightDom={rightDom} + notHandleHardwareBackPress safeAreaColor={['blue', 'white']} containerStyles={styles.container} scrollViewProps={{ disabled: true }} diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index 03d5ad80f7..fc4365ec44 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -14,7 +14,7 @@ import DiscoverHome from 'pages/Discover/DiscoverHome'; const Tab = createBottomTabNavigator(); type TabMenuTypeType = { icon: IconName; component: React.FC }; export interface TabMenuItem extends TabMenuTypeType { - name: string; + name: 'Wallet' | 'Discover' | 'Settings'; label: string; index: number; } @@ -46,6 +46,7 @@ export default function TabRoot() { const tabMenuListStore = useTabMenuList(); const tabMenuList = useMemo(() => { + return defaultTabMenuList; const _tabMenuListStore = tabMenuListStore.reduce((acc: typeof tabMenuListStore, cur) => { if (!acc.find(item => item.type.value === cur.type.value)) { acc.push(cur); diff --git a/packages/mobile-app-did/js/navigation/index.tsx b/packages/mobile-app-did/js/navigation/index.tsx index fedfbbc31a..a95fa529d4 100644 --- a/packages/mobile-app-did/js/navigation/index.tsx +++ b/packages/mobile-app-did/js/navigation/index.tsx @@ -26,7 +26,7 @@ import Discover from 'Test/Discover'; import TabsDrawer from 'components/TabsDrawer'; const Stack = createStackNavigator(); -export const stackNav = [ +export const productionNav = [ { name: 'Referral', component: Referral }, { name: 'Tab', component: Tab }, { name: 'SecurityLock', component: SecurityLock, options: { gestureEnabled: false } }, @@ -34,10 +34,6 @@ export const stackNav = [ { name: 'NFTDetail', component: NFTDetail }, { name: 'QrScanner', component: QrScanner }, - // FIXME: test page - { name: 'Home', component: Home }, - { name: 'Discover', component: Discover }, - ...GuardianNav, ...ActivityNav, ...LoginNav, @@ -49,13 +45,22 @@ export const stackNav = [ ...DiscoverNav, ] as const; +// dav nav +export const davNav = [ + ...productionNav, + { name: 'Home', component: Home }, + { name: 'Discover', component: Discover }, +] as const; + +const stackNav = __DEV__ ? davNav : productionNav; + export type RootStackParamList = { - [key in typeof stackNav[number]['name']]: undefined; + [key in typeof davNav[number]['name']]: undefined; }; export type TabParamList = { [key in TabMenuItem['name']]: undefined; }; -export type RootStackName = typeof stackNav[number]['name']; +export type RootStackName = typeof davNav[number]['name']; export type RootNavigationProp = StackNavigationProp; export default function NavigationRoot() { From b8e060d38ae1ada416a1997d8c66913c3c1622ef Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 17:26:43 +0800 Subject: [PATCH 121/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20dapp=20svg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/assets/image/svgs.js | 2 +- packages/mobile-app-did/js/assets/image/svgs/more.svg | 2 +- .../mobile-app-did/js/assets/image/svgs/wallet-white.svg | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index 2fa85778d3..ac95bff9eb 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n jrosmjmwjg\n \n \n \n \n \n \n \n \n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n rewbbtbkvk\n \n \n \n \n \n \n \n \n \n \n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n jrosmjmwjg\n \n \n \n \n \n \n \n \n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n rewbbtbkvk\n \n \n \n \n \n \n \n \n \n \n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/more.svg b/packages/mobile-app-did/js/assets/image/svgs/more.svg index 41acc81c93..1ca35a058c 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/more.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/more.svg @@ -1,3 +1,3 @@ - + diff --git a/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg b/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg index eb98cf38f5..7e7bf0f1ee 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/wallet-white.svg @@ -1,11 +1,11 @@ - - - + + + - + From 40fc876d6ba56943521ffd315d59f7745204b565 Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 14 Jun 2023 17:50:48 +0800 Subject: [PATCH 122/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?for=20new=20discover=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/util/index.ts | 9 ++------- packages/utils/dapp/middle.ts | 2 +- .../app/web/pages/Buy/Preview/index.tsx | 4 +++- packages/web-extension-did/app/web/pages/Buy/index.tsx | 3 +-- packages/web-extension-did/app/web/pages/Home/index.tsx | 1 + .../app/web/serviceWorker/ServiceWorkerInstantiate.ts | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/api/api-did/payment/util/index.ts b/packages/api/api-did/payment/util/index.ts index 1dea0df537..d728db18b4 100644 --- a/packages/api/api-did/payment/util/index.ts +++ b/packages/api/api-did/payment/util/index.ts @@ -27,14 +27,9 @@ export const getOrderQuote = async (params: GetOrderQuoteParamsType) => { export const getCryptoInfo = async ( params: { fiat: string }, symbol: string, - _chainId: string, + network: string, side: PaymentTypeEnum, ) => { - // FIXME _chainId to chainId - console.log( - 'At present, only the main network is connected to legal currency, and the test is the faucet. If the test network is connected to legal currency, chainId will be used', - ); - const rst = await request.payment.getCryptoList({ params, }); @@ -44,7 +39,7 @@ export const getCryptoInfo = async ( return (rst.data as CryptoInfoType[]).find( (item: any) => item.crypto === symbol && - item.network === symbol && + item.network === network && (side === PaymentTypeEnum.BUY ? Number(item.buyEnable) === 1 : Number(item.sellEnable) === 1), ); }; diff --git a/packages/utils/dapp/middle.ts b/packages/utils/dapp/middle.ts index a18632fc70..86f788c22b 100644 --- a/packages/utils/dapp/middle.ts +++ b/packages/utils/dapp/middle.ts @@ -1,5 +1,5 @@ import { CACommonState } from '@portkey-wallet/types/types-ca/store'; -import { changeNetworkType, setCAInfo, setWalletNameAction } from '@portkey-wallet/store/store-ca/wallet/actions'; +import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; import { addDapp, removeDapp, resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; export interface IMiddlewareAPI { diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index e8477f4a5f..90f4924368 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -122,7 +122,9 @@ export default function Preview() { const signature = await getAchSignature({ address }); achUrl += `&address=${address}&sign=${encodeURIComponent(signature)}`; } else { - const withdrawUrl = encodeURIComponent(ACH_WITHDRAW_URL + `&payload=${orderNo}`); + const withdrawUrl = encodeURIComponent( + ACH_WITHDRAW_URL + `&payload=${encodeURIComponent(JSON.stringify({ orderNo: orderNo }))}`, + ); achUrl += `&type=sell&cryptoAmount=${amount}&withdrawUrl=${withdrawUrl}&source=3#/sell-formUserInfo`; } diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 6a5f20e5f9..8c17d37d63 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -176,8 +176,7 @@ export default function Buy() { }; const handleSetTimer = () => { - const timer = setInterval(() => { - updateTimerRef.current = timer; + updateTimerRef.current = setInterval(() => { --updateTimeRef.current; if (updateTimeRef.current === 0) { diff --git a/packages/web-extension-did/app/web/pages/Home/index.tsx b/packages/web-extension-did/app/web/pages/Home/index.tsx index 0063cc4eed..b1e66fc803 100644 --- a/packages/web-extension-did/app/web/pages/Home/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/index.tsx @@ -29,6 +29,7 @@ export default function Home() { if (search) { const { detail, method } = qs.parse(search); if (detail && method === walletMessage.ACH_SELL_REDIRECT && !locked && isSell.current === 0) { + history.replaceState(null, '', location.pathname); isSell.current = 1; await handleAchSell(detail); } diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 7eb2f03873..b439338798 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -343,7 +343,7 @@ export default class ServiceWorkerInstantiate { notificationService.openPrompt( { method: PromptRouteTypes.EXPAND_FULL_SCREEN, - search: `${payload.payload}&method=${payload.method}`, + search: `${payload.payload?.orderNo || ''}&method=${payload.method}`, }, 'tabs', ); From f3d842a5044bd63ee79ccfe3ba9508a4ee078e8f Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 14 Jun 2023 18:01:56 +0800 Subject: [PATCH 123/893] =?UTF-8?q?style:=20=F0=9F=92=84=20UI=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/dapp/middle.ts | 2 +- .../app/web/assets/svgIcon/DappDefault.svg | 5 ++ .../web-extension-did/app/web/assets/svgs.ts | 2 + .../app/web/pages/ConnectWallet/index.less | 4 ++ .../app/web/pages/ConnectWallet/index.tsx | 10 +--- .../app/web/pages/GetSignature/index.less | 4 ++ .../app/web/pages/GetSignature/index.tsx | 10 +--- .../app/web/pages/SendTransactions/index.less | 4 +- .../app/web/pages/SendTransactions/index.tsx | 2 +- .../components/ConnectedSiteList/index.less | 8 +++ .../components/ConnectedSiteList/index.tsx | 10 +--- .../components/AccountConnect/index.less | 1 + .../pages/components/ImageDisplay/index.less | 12 +++++ .../pages/components/ImageDisplay/index.tsx | 49 +++++++++++++++++++ .../NotificationService/getPromptConfig.ts | 1 - 15 files changed, 96 insertions(+), 28 deletions(-) create mode 100644 packages/web-extension-did/app/web/assets/svgIcon/DappDefault.svg create mode 100644 packages/web-extension-did/app/web/pages/components/ImageDisplay/index.less create mode 100644 packages/web-extension-did/app/web/pages/components/ImageDisplay/index.tsx diff --git a/packages/utils/dapp/middle.ts b/packages/utils/dapp/middle.ts index a18632fc70..86f788c22b 100644 --- a/packages/utils/dapp/middle.ts +++ b/packages/utils/dapp/middle.ts @@ -1,5 +1,5 @@ import { CACommonState } from '@portkey-wallet/types/types-ca/store'; -import { changeNetworkType, setCAInfo, setWalletNameAction } from '@portkey-wallet/store/store-ca/wallet/actions'; +import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; import { addDapp, removeDapp, resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; export interface IMiddlewareAPI { diff --git a/packages/web-extension-did/app/web/assets/svgIcon/DappDefault.svg b/packages/web-extension-did/app/web/assets/svgIcon/DappDefault.svg new file mode 100644 index 0000000000..55e77be16c --- /dev/null +++ b/packages/web-extension-did/app/web/assets/svgIcon/DappDefault.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/web-extension-did/app/web/assets/svgs.ts b/packages/web-extension-did/app/web/assets/svgs.ts index c17916a055..bcacbf0e97 100644 --- a/packages/web-extension-did/app/web/assets/svgs.ts +++ b/packages/web-extension-did/app/web/assets/svgs.ts @@ -38,6 +38,8 @@ export default { '\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n ', CreateIcon: '\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n', + DappDefault: + '\n\n\n\n\n', Delete: '\n\n Delete\n \n \n \n \n \n \n \n \n', Discord: diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less index 7565f51046..9ff957d724 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less @@ -19,6 +19,10 @@ width: 24px; border-radius: 50%; margin-right: 8px; + .dappdefault-icon { + width: 24px; + height: 24px; + } } .origin { line-break: anywhere; diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index 50fdb034c9..60ea3bd6b9 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -1,8 +1,8 @@ import { addDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; import { Button } from 'antd'; import CustomSvg from 'components/CustomSvg'; -import ImgLoading from 'components/ImgLoading'; import usePromptSearch from 'hooks/usePromptSearch'; +import ImageDisplay from 'pages/components/ImageDisplay'; import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; @@ -23,13 +23,7 @@ export default function ConnectWallet() { () => detail && (
    - + {detail.appHref}
    ), diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.less b/packages/web-extension-did/app/web/pages/GetSignature/index.less index 468b7d0b3a..3425518b2b 100644 --- a/packages/web-extension-did/app/web/pages/GetSignature/index.less +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.less @@ -23,6 +23,10 @@ width: 24px; border-radius: 50%; margin-right: 8px; + .dappdefault-icon { + width: 24px; + height: 24px; + } } .origin { font-size: 14px; diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx index afebda10c1..0163183b6f 100644 --- a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx @@ -10,7 +10,7 @@ import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; import { ResponseCode } from '@portkey/provider-types'; import { getWallet } from '@portkey-wallet/utils/aelf'; -import ImgLoading from 'components/ImgLoading'; +import ImageDisplay from 'pages/components/ImageDisplay'; import './index.less'; export default function GetSignature() { @@ -38,13 +38,7 @@ export default function GetSignature() { () => curDapp && (
    - + {curDapp.origin}
    ), diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.less b/packages/web-extension-did/app/web/pages/SendTransactions/index.less index 4deea15d84..97cc983444 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.less +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.less @@ -95,13 +95,15 @@ .message-wrapper { margin-top: 16px; .message { + flex-direction: column; + gap: 16px; width: 328px; padding: 16px; margin-top: 4px; border: 1px solid @border-2; border-radius: 6px; .content { - margin: 4px 0 16px; + margin-top: 4px; width: 100%; line-break: anywhere; white-space: pre-wrap; diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 99ef97a9bf..4beb192714 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -176,7 +176,7 @@ export default function SendTransactions() { return (
    Message
    -
    +
    {Object.keys(params).map((item) => (
    {item}
    diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less index 22684788cb..217fb7dda9 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less @@ -16,6 +16,14 @@ flex-shrink: 0; width: 32px; border-radius: 50%; + .dappdefault-icon { + width: 32px; + height: 32px; + svg { + width: 32px; + height: 32px; + } + } } .desc { flex: 1; diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx index 23ac960d74..7b1b3d3aaf 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { Button } from 'antd'; import { useMemo } from 'react'; -import ImgLoading from 'components/ImgLoading'; +import ImageDisplay from 'pages/components/ImageDisplay'; import './index.less'; export interface IConnectedSiteListProps { @@ -19,13 +19,7 @@ export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSite {list.map((item) => (
    - +
    {item.name}
    {item.origin}
    diff --git a/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less b/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less index 1ba8106930..63e856197b 100644 --- a/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less +++ b/packages/web-extension-did/app/web/pages/components/AccountConnect/index.less @@ -5,6 +5,7 @@ max-width: fit-content; font-size: 12px; line-height: 16px; + font-weight: 400; color: @font-13; border-radius: 20px; cursor: pointer; diff --git a/packages/web-extension-did/app/web/pages/components/ImageDisplay/index.less b/packages/web-extension-did/app/web/pages/components/ImageDisplay/index.less new file mode 100644 index 0000000000..1867c53da6 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/ImageDisplay/index.less @@ -0,0 +1,12 @@ +.img-loading-wrapper { + overflow: hidden; + border-radius: 12px; + position: relative; + + .show-image { + float: left; + width: 100%; + height: 100%; + aspect-ratio: 1; + } +} diff --git a/packages/web-extension-did/app/web/pages/components/ImageDisplay/index.tsx b/packages/web-extension-did/app/web/pages/components/ImageDisplay/index.tsx new file mode 100644 index 0000000000..55abfd1b08 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/ImageDisplay/index.tsx @@ -0,0 +1,49 @@ +import clsx from 'clsx'; +import CustomSvg from 'components/CustomSvg'; +import { useMemo, useState } from 'react'; +import { IconType } from 'types/icon'; +import './index.less'; + +interface ImageDisplayProps { + src?: string; + className?: string; + backupSrc?: IconType; + key?: string; + notReady?: boolean; + defaultHeight?: number | string; +} + +export default function ImageDisplay({ + src, + className, + backupSrc, + notReady = false, + defaultHeight = 'auto', +}: ImageDisplayProps) { + const [isError, setError] = useState(); + + const isShowDefault = useMemo(() => isError || notReady || !src, [isError, notReady, src]); + + return ( +
    + {isShowDefault ? ( +
    {backupSrc ? : <>{/* default loading */}}
    + ) : ( + { + setError(false); + if (!(e.target as any).src.includes('brokenImg')) { + (e.target as HTMLElement).className = 'show-image'; + } + }} + onError={() => { + setError(true); + }} + /> + )} +
    + ); +} diff --git a/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts b/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts index 94bbb21d05..e32d16cb8d 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/getPromptConfig.ts @@ -21,7 +21,6 @@ export default async function getPromptConfig({ message }: PromptConfigParam) { const lastFocused = await apis.windows.getLastFocused(); if (lastFocused.state) isFullscreen = lastFocused.state === 'fullscreen'; switch (method) { - case PromptRouteTypes.GET_SIGNATURE: case PromptRouteTypes.UNLOCK_WALLET: case PromptRouteTypes.REGISTER_WALLET: case PromptRouteTypes.REGISTER_START_WALLET: From 68e12948f79d8402b7f34f63dda7fb6bea64e205 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:17:13 +0800 Subject: [PATCH 124/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20ui=208467754=2084?= =?UTF-8?q?67973?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/CommonInput/index.tsx | 2 ++ .../js/pages/Discover/DiscoverSearch/index.tsx | 6 +++++- .../Discover/components/DiscoverCmsListSection/index.tsx | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/components/CommonInput/index.tsx b/packages/mobile-app-did/js/components/CommonInput/index.tsx index 0a1b6bb277..79ab7ae611 100644 --- a/packages/mobile-app-did/js/components/CommonInput/index.tsx +++ b/packages/mobile-app-did/js/components/CommonInput/index.tsx @@ -34,6 +34,7 @@ const CommonInput = forwardRef(function CommonInput(props: CommonInputProps, for rightIconContainerStyle={[searchStyle.rightIconContainerStyle, rightIconContainerStyle]} leftIconContainerStyle={[searchStyle.leftIconContainerStyle, leftIconContainerStyle]} placeholder={placeholder || t('Please enter')} + placeholderTextColor={defaultColors.font7} leftIcon={} {...inputProps} ref={forwardedRef} @@ -55,6 +56,7 @@ const CommonInput = forwardRef(function CommonInput(props: CommonInputProps, for leftIconContainerStyle={leftIconContainerStyle} errorStyle={[generalStyles.errorStyle]} placeholder={placeholder || t('Please enter')} + placeholderTextColor={defaultColors.font7} disabledInputStyle={[generalStyles.disabledInputStyle]} {...inputProps} ref={forwardedRef} diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index efed9557d6..6f3f71fdc6 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -101,7 +101,7 @@ export default function DiscoverSearch() { onChangeText={v => setValue(v)} onSubmitEditing={onSearch} returnKeyType="search" - placeholder={t('Enter URL to explorer')} + placeholder={t('Search Dapp or enter URL')} containerStyle={styles.inputStyle} rightIcon={ value ? ( @@ -111,6 +111,7 @@ export default function DiscoverSearch() { ) : undefined } rightIconContainerStyle={styles.rightIconContainerStyle} + style={styles.rnInputStyle} /> {t('Cancel')} @@ -135,6 +136,9 @@ const styles = StyleSheet.create({ sectionWrap: { ...GStyles.paddingArg(24, 20), }, + rnInputStyle: { + fontSize: pTd(14), + }, headerWrap: { height: pTd(22), }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 70dff78030..398addc565 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -40,7 +40,7 @@ export function DiscoverCmsListSection() { onJump(item)}> - + {item.title} @@ -74,7 +74,7 @@ const styles = StyleSheet.create({ backgroundColor: defaultColors.bg1, display: 'flex', flexDirection: 'row', - ...GStyles.paddingArg(12, 16), + ...GStyles.paddingArg(16, 12), width: '100%', }, image: { From 7d8c0b5ca266c6e83a643334673b157355000ce2 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:19:39 +0800 Subject: [PATCH 125/893] =?UTF-8?q?style:=20=F0=9F=92=84=20ui=20shadow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/components/Card/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx index 2ebc995811..b02505ec36 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx @@ -57,10 +57,10 @@ const tabShowItemStyle = StyleSheet.create({ width: pTd(160), height: pTd(214), marginTop: pTd(24), - shadowOffset: { width: 10, height: 10 }, + shadowOffset: { width: 2, height: 8 }, backgroundColor: defaultColors.bg1, shadowColor: defaultColors.shadow1, - shadowOpacity: 0.2, + shadowOpacity: 0.1, shadowRadius: 10, elevation: 2, }, From 1f9c38435c727a84964edbc91d5e6ccb58548658 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:20:06 +0800 Subject: [PATCH 126/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20podfile.lock'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/ios/Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/ios/Podfile.lock b/packages/mobile-app-did/ios/Podfile.lock index 547b8cc9d7..b225e61a57 100644 --- a/packages/mobile-app-did/ios/Podfile.lock +++ b/packages/mobile-app-did/ios/Podfile.lock @@ -669,7 +669,7 @@ PODS: - React-Core - RNFS (2.20.0): - React-Core - - RNGestureHandler (2.10.2): + - RNGestureHandler (2.9.0): - React-Core - RNGoogleSignin (8.2.2): - GoogleSignIn (~> 6.2) @@ -1211,7 +1211,7 @@ SPEC CHECKSUMS: RNFBPerf: 789e745a88bc520bcab1fcc9e6cc80d3c9aaa6aa RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 - RNGestureHandler: f75d81410b40aaa99e71ae8f8bb7a88620c95042 + RNGestureHandler: 071d7a9ad81e8b83fe7663b303d132406a7d8f39 RNGoogleSignin: 81521697b2c8f97f9a586ac7257b1a1d9b51b115 RNLocalize: 0df7970cfc60389f00eb62fd7c097dc75af3fb4f RNNotifee: 5155e0a5e0a97d0c839030d8192783cd63053999 From a15511c8bc9024a356a2e51fa61c0a792186f6df Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:28:21 +0800 Subject: [PATCH 127/893] =?UTF-8?q?style:=20=F0=9F=92=84=20overlay=20disab?= =?UTF-8?q?led?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dapp/components/TransactionOverlay/index.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 99f5816fb8..5d8736f658 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -54,12 +54,15 @@ const ConnectModal = (props: TransactionModalPropsType) => { ); const [fee, setFee] = useState(''); + const [isFetchingFee, setIsFetchingFee] = useState(true); const [noEnoughFee, setNoEnoughFee] = useState(false); const isTransfer = useMemo(() => transactionInfo.method.toLowerCase() === 'transfer', [transactionInfo.method]); - const buttonList = useMemo( - () => [ + const buttonList = useMemo(() => { + const disabled = isFetchingFee || noEnoughFee; + + return [ { title: t('Reject'), type: 'outline' as CommonButtonProps['type'], @@ -75,10 +78,10 @@ const ConnectModal = (props: TransactionModalPropsType) => { onSign?.(); OverlayModal.hide(); }, + disabled: disabled, }, - ], - [onReject, onSign, t], - ); + ]; + }, [isFetchingFee, noEnoughFee, onReject, onSign, t]); const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { @@ -271,10 +274,12 @@ const ConnectModal = (props: TransactionModalPropsType) => { if (!TransactionFee && !TransactionFee?.ELF) return setNoEnoughFee(true); setFee(TransactionFee?.ELF || '0'); + setIsFetchingFee(false); } catch (e) { setFee('0'); setNoEnoughFee(true); console.log('get fee error', e); + setIsFetchingFee(false); } }, [ chainInfo, From 2b4f4ce977051195ae1130a42bb5c0f57bc5c146 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:29:18 +0800 Subject: [PATCH 128/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20ui=20fontsize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/components/SearchDiscoverSection/index.tsx | 6 +++--- .../pages/Discover/components/SimulatedInputBox/index.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx index 794102259c..fd43ca7e2e 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { StyleSheet, View, ScrollView, TouchableOpacity } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { useLanguage } from 'i18n/hooks'; -import { TextL, TextS } from 'components/CommonText'; +import { TextM, TextS } from 'components/CommonText'; import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import NoData from 'components/NoData'; @@ -41,9 +41,9 @@ export default function SearchDiscoverSection(props: SearchDiscoverSectionProps) - + {item?.title} - + {item?.description} diff --git a/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx index 9f59616472..8573a6c121 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx @@ -17,7 +17,7 @@ export default function SimulatedInputBox({ onClickInput }: SimulatedInputBoxPro - Search Dapp or enter URL to explore + Search Dapp or enter URL From 29e6c69466380cf8d79f7dcc1ded781fb48246e3 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:30:57 +0800 Subject: [PATCH 129/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20store=20persisit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/store/config.ts | 2 + .../mobile-app-did/js/store/rootReducer.ts | 69 +++---------------- 2 files changed, 10 insertions(+), 61 deletions(-) diff --git a/packages/mobile-app-did/js/store/config.ts b/packages/mobile-app-did/js/store/config.ts index 03b5b03ed6..1dafe4bd67 100644 --- a/packages/mobile-app-did/js/store/config.ts +++ b/packages/mobile-app-did/js/store/config.ts @@ -15,6 +15,7 @@ import activitySlice from '@portkey-wallet/store/store-ca/activity/slice'; import switchSlice from '@portkey-wallet/store/store-ca/switch/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; +import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; interface ThunkOptions { extraArgument: E; @@ -46,6 +47,7 @@ const reduxPersistConfig = { switchSlice.name, dappSlice.name, cmsSlice.name, + paymentSlice.name, ], // More info here: https://shift.infinite.red/shipping-persistant-reducers-7341691232b1 diff --git a/packages/mobile-app-did/js/store/rootReducer.ts b/packages/mobile-app-did/js/store/rootReducer.ts index fefc977493..115947913b 100644 --- a/packages/mobile-app-did/js/store/rootReducer.ts +++ b/packages/mobile-app-did/js/store/rootReducer.ts @@ -2,15 +2,12 @@ import { combineReducers } from '@reduxjs/toolkit'; import { walletSlice } from '@portkey-wallet/store/store-ca/wallet/slice'; import { contactSlice } from '@portkey-wallet/store/store-ca/contact/slice'; import chainSlice from '@portkey-wallet/store/network/slice'; -import tokenSlice from '@portkey-wallet/store/token/slice'; -import tokenBalanceSlice from '@portkey-wallet/store/tokenBalance/slice'; import settingsSlice from '@portkey-wallet/store/settings/slice'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { persistReducer } from 'redux-persist'; import userSlice from './user/slice'; -import { rateApi } from '@portkey-wallet/store/rate/api'; import { recentSlice } from '@portkey-wallet/store/store-ca/recent/slice'; import { assetsSlice } from '@portkey-wallet/store/store-ca/assets/slice'; import { tokenManagementSlice } from '@portkey-wallet/store/store-ca/tokenManagement/slice'; @@ -29,83 +26,33 @@ const userPersistConfig = { storage: AsyncStorage, blacklist: ['credentials'], }; -const tokenPersistConfig = { - key: tokenSlice.name, - storage: AsyncStorage, - blacklist: ['tokenDataShowInMarket'], -}; - -const tokenBalancePersistConfig = { - key: tokenBalanceSlice.name, - storage: AsyncStorage, -}; - -const settingsPersistConfig = { - key: settingsSlice.name, - storage: AsyncStorage, -}; - -const recentPersistConfig = { - key: recentSlice.name, - storage: AsyncStorage, -}; - -const activityPersistConfig = { - key: activitySlice.name, - storage: AsyncStorage, -}; - -const assetsPersistConfig = { - key: assetsSlice.name, - storage: AsyncStorage, -}; -const guardiansPersistConfig = { - key: guardiansSlice.name, - storage: AsyncStorage, -}; const discoverPersistConfig = { key: discoverSlice.name, storage: AsyncStorage, blacklist: ['isDrawerOpen', 'initializedList', 'activeTabId'], }; -const paymentPersistConfig = { - key: paymentSlice.name, - storage: AsyncStorage, -}; - export const userReducer = persistReducer(userPersistConfig, userSlice.reducer); -export const tokenReducer = persistReducer(tokenPersistConfig, tokenSlice.reducer); -export const tokenBalanceReducer = persistReducer(tokenBalancePersistConfig, tokenBalanceSlice.reducer); -export const settingsReducer = persistReducer(settingsPersistConfig, settingsSlice.reducer); -export const recentReducer = persistReducer(recentPersistConfig, recentSlice.reducer); -export const activityReducer = persistReducer(activityPersistConfig, activitySlice.reducer); -export const assetsReducer = persistReducer(assetsPersistConfig, assetsSlice.reducer); -export const guardiansReducer = persistReducer(guardiansPersistConfig, guardiansSlice.reducer); -export const paymentReducer = persistReducer(paymentPersistConfig, paymentSlice.reducer); export const discoverReducer = persistReducer(discoverPersistConfig, discoverSlice.reducer); const rootReducer = combineReducers({ - [userSlice.name]: userReducer, [walletSlice.name]: walletSlice.reducer, [chainSlice.name]: chainSlice.reducer, - [tokenSlice.name]: tokenReducer, [contactSlice.name]: contactSlice.reducer, [miscSlice.name]: miscSlice.reducer, - [guardiansSlice.name]: guardiansReducer, - [tokenBalanceSlice.name]: tokenBalanceReducer, - [settingsSlice.name]: settingsReducer, - [recentSlice.name]: recentReducer, - [rateApi.reducerPath]: rateApi.reducer, - [assetsSlice.name]: assetsReducer, - [activitySlice.name]: activityReducer, + [guardiansSlice.name]: guardiansSlice.reducer, + [settingsSlice.name]: settingsSlice.reducer, + [recentSlice.name]: recentSlice.reducer, + [assetsSlice.name]: assetsSlice.reducer, + [activitySlice.name]: activitySlice.reducer, [tokenManagementSlice.name]: tokenManagementSlice.reducer, - [paymentSlice.name]: paymentReducer, - [discoverSlice.name]: discoverReducer, + [paymentSlice.name]: paymentSlice.reducer, [switchSlice.name]: switchSlice.reducer, [dappSlice.name]: dappSlice.reducer, [cmsSlice.name]: cmsSlice.reducer, + [userSlice.name]: userReducer, + [discoverSlice.name]: discoverReducer, }); export default rootReducer; From 810b1e3d1b90c90735265d0268a04fc3c33ef2db Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 18:32:34 +0800 Subject: [PATCH 130/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20change=20header?= =?UTF-8?q?=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ' --- packages/utils/fetch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/fetch.ts b/packages/utils/fetch.ts index 3c9244f0db..2c810fc3a0 100644 --- a/packages/utils/fetch.ts +++ b/packages/utils/fetch.ts @@ -15,7 +15,7 @@ const defaultHeaders = { Accept: 'text/plain;v=1.0', 'Content-Type': 'application/json', // FIXME: delete - version: 'v1.2.7', + version: 'v1.3.0', }; function formatResponse(response: string) { From 56c105fe31fa7cc9bbb8608397d2fcb318b954a7 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:20:01 +0800 Subject: [PATCH 131/893] =?UTF-8?q?style:=20=F0=9F=92=84=20ui=20close=20ic?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/ModalBody/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/ModalBody/index.tsx b/packages/mobile-app-did/js/components/ModalBody/index.tsx index ebc6590579..b87351d23a 100644 --- a/packages/mobile-app-did/js/components/ModalBody/index.tsx +++ b/packages/mobile-app-did/js/components/ModalBody/index.tsx @@ -89,7 +89,7 @@ export const styles = StyleSheet.create({ textAlign: 'center', }, closeIcon: { - ...GStyles.paddingArg(21, 28), + ...GStyles.paddingArg(21, 20), position: 'absolute', right: 0, }, From 3387279eac3f8533c6a104fb3caadd0caa014842 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:21:20 +0800 Subject: [PATCH 132/893] =?UTF-8?q?style:=20=F0=9F=92=84=20border=20radius?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/components/TabsOverlay/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index e4aba3d9d8..c6c2086b99 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -182,7 +182,6 @@ const styles = StyleSheet.create({ }, listItem: { marginRight: pTd(34), - borderRadius: pTd(6), overflow: 'hidden', }, listItemNoMarginRight: { @@ -190,6 +189,8 @@ const styles = StyleSheet.create({ }, svgWrap: { backgroundColor: defaultColors.bg1, + borderRadius: pTd(6), + overflow: 'hidden', }, itemTitle: { textAlign: 'center', From ae9f5740b8cd3c0da257d8beae4d35529086bbaf Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:21:52 +0800 Subject: [PATCH 133/893] =?UTF-8?q?style:=20=F0=9F=92=84=20ui=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/components/Card/index.tsx | 6 +++--- .../TabsDrawer/components/WalletInfoOverlay/index.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx index b02505ec36..2654ebc657 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx @@ -1,5 +1,5 @@ import GStyles from 'assets/theme/GStyles'; -import { TextM } from 'components/CommonText'; +import { TextS } from 'components/CommonText'; import { TouchableOpacity } from 'react-native'; import React from 'react'; import { StyleSheet, View, Image } from 'react-native'; @@ -28,9 +28,9 @@ const Card: React.FC = (props: ICardsProps) => { - + {item?.name ?? getHost(item?.url)} - + dispatch(closeExistingTab({ id: item?.id, networkType }))}> diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx index 19660cc575..e2c2ffdba8 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx @@ -51,7 +51,7 @@ const MyWalletModal = () => { {formatStr2EllipsisStr(addressFormat(item?.caAddress, item?.chaiId as ChainId), 10)} - + {formatChainInfoToShow(item?.chaiId as ChainId, currentNetwork)} From 12875b76c452645a88dce2ecde214f4bf8d8f4b0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:36:55 +0800 Subject: [PATCH 134/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20refresh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/TabsDrawerContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 1ae2d1b6c5..c5a04bc68e 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -72,7 +72,7 @@ const TabsDrawerContent: React.FC = () => { onPress={() => showBrowserModal({ browserInfo: activeItem, - activeWebViewRef: tabRef.current, + activeWebViewRef: tabRef, activeWebviewScreenShot, setPreActiveTabId, }) From 4cdb9dd7b5f15e563d615150a1542cdbb651931c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:37:37 +0800 Subject: [PATCH 135/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20bold?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/TransactionOverlay/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 5d8736f658..cd0c87ec70 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -143,7 +143,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Transaction Fee')} - {`${formatAmountShow( + {`${formatAmountShow( divDecimals(fee, ELF_DECIMAL), 8, )} ELF`} @@ -166,7 +166,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Total')} - {`${formatAmountShow( + {`${formatAmountShow( divDecimals(ZERO.plus(amount).plus(fee), decimals), 8, )} ${symbol}`} @@ -188,7 +188,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Total')} - {`${formatAmountShow( + {`${formatAmountShow( divDecimals(ZERO.plus(fee), ELF_DECIMAL), 8, )} ELF`} @@ -205,7 +205,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { )} - {`${formatAmountShow( + {`${formatAmountShow( divDecimals(ZERO.plus(amount), decimals), 8, )} ${symbol}`} From 55c08319d9f253593e2ef5e0f31ae1b786f9bda9 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:50:14 +0800 Subject: [PATCH 136/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20overlay?= =?UTF-8?q?=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TabsOverlay/index.tsx | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index c6c2086b99..5ba3ff5718 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -12,7 +12,7 @@ import { screenWidth } from '@portkey-wallet/utils/mobile/device'; import { FontStyles } from 'assets/theme/styles'; import { setStringAsync } from 'expo-clipboard'; import CommonToast from 'components/CommonToast'; -import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; import { isIOS } from '@rneui/base'; import { useAppCASelector } from '@portkey-wallet/hooks'; @@ -111,10 +111,14 @@ const BrowserEditModal = ({ - - {browserInfo?.name} - - + + + {browserInfo?.name || getHost(browserInfo?.url)} + + + {browserInfo?.url} + + handleUrl(HANDLE_TYPE.CANCEL)}> @@ -164,14 +168,19 @@ const styles = StyleSheet.create({ backgroundColor: defaultColors.bg6, width: screenWidth, }, + headerCenter: { + paddingLeft: pTd(8), + paddingRight: pTd(16), + flex: 1, + }, title: { textAlign: 'left', - height: pTd(22), - lineHeight: pTd(22), - marginVertical: pTd(13), - paddingLeft: pTd(8), + color: defaultColors.font5, ...fonts.mediumFont, }, + url: { + color: defaultColors.font7, + }, listWrap: { marginTop: pTd(24), marginBottom: pTd(24), From 0599480ae31c136a82a05c5a898ae37a07e5e5ea Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:50:37 +0800 Subject: [PATCH 137/893] =?UTF-8?q?style:=20=F0=9F=92=84=20dapp=20list=20s?= =?UTF-8?q?tyle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx index 28e4943db4..8c68957c1d 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -95,6 +95,7 @@ const itemStyles = StyleSheet.create({ }, itemCenter: { flex: 1, + paddingRight: pTd(16), }, itemDappTitle: { marginBottom: pTd(1), From 2e0720d21fbe62856866403d0462cf4a58aa8e9e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 14 Jun 2023 20:52:18 +0800 Subject: [PATCH 138/893] =?UTF-8?q?style:=20=F0=9F=92=84=20bottom=20paddin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/TransactionOverlay/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index cd0c87ec70..7cb03eb620 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -358,7 +358,7 @@ const styles = StyleSheet.create({ height: screenHeight / 2, }, blank: { - height: pTd(300), + height: pTd(200), }, error: { color: defaultColors.error, From fb631983d4bd28f2778968e20cb68817d10b58a8 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Wed, 14 Jun 2023 21:06:32 +0800 Subject: [PATCH 139/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20HardwareBackPress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/mobile.ts | 9 +++------ .../js/components/CustomHeader/index.tsx | 18 ++++++++++-------- .../TabsDrawer/TabsDrawerContent.tsx | 17 ++++++++++------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/hooks/mobile.ts b/packages/hooks/mobile.ts index 722be910de..73695b1325 100644 --- a/packages/hooks/mobile.ts +++ b/packages/hooks/mobile.ts @@ -1,18 +1,15 @@ import { useEffect, useRef } from 'react'; import { BackHandler } from 'react-native'; export function useHardwareBackPress(callback?: () => boolean) { - const savedCallback = useRef<() => boolean>(); - useEffect(() => { - savedCallback.current = callback; - }); useEffect(() => { + if (!callback) return; const backHandler = BackHandler.addEventListener('hardwareBackPress', () => { - return savedCallback.current?.(); + return callback(); }); return () => { backHandler.remove(); }; - }, []); + }, [callback]); } export function usePreventHardwareBack(callback?: () => void) { diff --git a/packages/mobile-app-did/js/components/CustomHeader/index.tsx b/packages/mobile-app-did/js/components/CustomHeader/index.tsx index f52fa7b392..55fd8a70d6 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/index.tsx +++ b/packages/mobile-app-did/js/components/CustomHeader/index.tsx @@ -64,14 +64,16 @@ const CustomHeader: React.FC = props => { /> ); }, [leftIconType, styles.leftBackTitle.color]); - useHardwareBackPress(() => { - if (notHandleHardwareBackPress) return false; - if (isFocused && leftCallback) { - leftCallback(); - return true; - } - return false; - }); + useHardwareBackPress( + useMemo(() => { + if (isFocused && leftCallback && !notHandleHardwareBackPress) { + return () => { + leftCallback(); + return true; + }; + } + }, [isFocused, leftCallback, notHandleHardwareBackPress]), + ); useEffect(() => { if (onGestureStartCallback) { const unsubscribe = navigation.addListener('gestureStart' as any, () => { diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 1ae2d1b6c5..a8c8f4d3e4 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -102,13 +102,16 @@ const TabsDrawerContent: React.FC = () => { }), [], ); - useHardwareBackPress(() => { - if (isDrawerOpen) { - backToSearchPage(); - return true; - } - return false; - }); + useHardwareBackPress( + useMemo(() => { + if (isDrawerOpen) { + return () => { + backToSearchPage(); + return true; + }; + } + }, [backToSearchPage, isDrawerOpen]), + ); return ( Date: Wed, 14 Jun 2023 21:10:00 +0800 Subject: [PATCH 140/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index d080355c35..60fba8b8be 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,20 +1,20 @@ -import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; +import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import ProviderWebview, { IWebView } from 'components/ProviderWebview'; import { captureRef } from 'react-native-view-shot'; -import { useBrowser } from 'components/TabsDrawer/context'; -import Progressbar from 'components/Progressbar'; +import { IBrowserTab, useBrowser } from 'components/TabsDrawer/context'; +import Progressbar, { IProgressbar } from 'components/Progressbar'; type BrowserTabProps = { isHidden: boolean; uri: string; }; -const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri }, forward) { +const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri }, forward) { const viewRef = useRef(null); const webViewRef = useRef(null); - const progressbarRef = useRef(null); + const progressbarRef = useRef(null); const { setTabRef } = useBrowser(); @@ -42,7 +42,7 @@ const BrowserTab = forwardRef(function BrowserTab({ isHidd progressbarRef.current.changeInnerBarWidth(nativeEvent.progress)} + onLoadProgress={({ nativeEvent }) => progressbarRef.current?.changeInnerBarWidth(nativeEvent.progress)} /> ); From 209f216c585474259167aaa47fadbd485f484139 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 15 Jun 2023 10:08:50 +0800 Subject: [PATCH 141/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20ACH=5FW?= =?UTF-8?q?ITHDRAW=5FURL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/constants/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/constants/index.ts b/packages/web-extension-did/app/web/constants/index.ts index d1e44be57a..68b848f680 100644 --- a/packages/web-extension-did/app/web/constants/index.ts +++ b/packages/web-extension-did/app/web/constants/index.ts @@ -44,4 +44,4 @@ export const AUTH_APPLE_URL = `${AUTH_HOST}/apple-auth`; export const RECAPTCHA_URL = `${AUTH_HOST}/recaptcha-check`; // after ach-sell, redirect url, then wake up extension. -export const ACH_WITHDRAW_URL = `https://openlogin-test.portkey.finance/extension-rouse?method=${walletMessage.ACH_SELL_REDIRECT}`; // TODO: change url +export const ACH_WITHDRAW_URL = `https://openlogin-test.portkey.finance/extension-bridge?method=${walletMessage.ACH_SELL_REDIRECT}`; // TODO: change url From 3ab0ddafe261d14f01404a6b819fb704da05202c Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 15 Jun 2023 10:38:16 +0800 Subject: [PATCH 142/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20GoogleR?= =?UTF-8?q?ecaptcha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/verification/utils.ts | 3 +++ .../pages/Guardian/GuardianApproval/index.tsx | 6 +++--- .../pages/Guardian/SelectVerifier/index.tsx | 3 ++- .../pages/Guardian/VerifierDetails/index.tsx | 19 +++++++++++++++++-- .../components/GuardianItem/index.tsx | 19 ++++++++++--------- .../My/Guardian/GuardianDetail/index.tsx | 3 ++- .../pages/My/Guardian/GuardianEdit/index.tsx | 3 ++- packages/mobile-app-did/js/utils/api.ts | 12 +++++++++--- packages/types/verifier.ts | 10 ++++++++-- .../components/GuardianItems/index.tsx | 14 ++++++++++++-- .../pages/Guardians/GuardiansAdd/index.tsx | 3 ++- .../pages/Guardians/GuardiansView/index.tsx | 2 ++ .../app/web/pages/SelectVerifier/index.tsx | 2 ++ .../app/web/pages/VerifierAccount/index.tsx | 14 ++++++++++++-- .../pages/components/VerifierPage/index.tsx | 13 +++++++++++-- .../web-extension-did/app/web/utils/api.ts | 6 ++++-- .../app/web/utils/lib/checkReCaptcha.ts | 10 +++++++--- 17 files changed, 108 insertions(+), 34 deletions(-) diff --git a/packages/api/api-did/verification/utils.ts b/packages/api/api-did/verification/utils.ts index fd69241f65..7fa4321586 100644 --- a/packages/api/api-did/verification/utils.ts +++ b/packages/api/api-did/verification/utils.ts @@ -2,6 +2,7 @@ import { IStorage, StorageBaseLoader } from '@portkey-wallet/types/storage'; import { request } from '@portkey-wallet/api/api-did'; import { RequestConfig } from '../../types'; import { LoginKeyType } from '@portkey-wallet/types/types-ca/wallet'; +import { RecaptchaType } from '@portkey-wallet/types/verifier'; type VerifierInfo = { verifierSessionId: string; @@ -14,6 +15,7 @@ export interface SendVerificationConfig extends RequestConfig { guardianIdentifier?: string; verifierId?: string; chainId: string | number; + operationType: RecaptchaType; }; } @@ -59,6 +61,7 @@ export class Verification extends StorageBaseLoader { this.verifierMap[key] = value; await this.save(); } + public async sendVerificationCode(config: SendVerificationConfig) { const { guardianIdentifier, verifierId } = config.params; const key = (guardianIdentifier || '') + (verifierId || ''); diff --git a/packages/mobile-app-did/js/pages/Guardian/GuardianApproval/index.tsx b/packages/mobile-app-did/js/pages/Guardian/GuardianApproval/index.tsx index 713c7426f0..6292d50e32 100644 --- a/packages/mobile-app-did/js/pages/Guardian/GuardianApproval/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/GuardianApproval/index.tsx @@ -53,7 +53,7 @@ export default function GuardianApproval() { const { loginAccount, userGuardiansList: paramUserGuardiansList, - approvalType = ApprovalType.register, + approvalType = ApprovalType.communityRecovery, guardianItem, verifierInfo, verifiedTime, @@ -269,7 +269,7 @@ export default function GuardianApproval() { const onFinish = useCallback(async () => { switch (approvalType) { - case ApprovalType.register: + case ApprovalType.communityRecovery: registerAccount(); break; case ApprovalType.addGuardian: @@ -295,7 +295,7 @@ export default function GuardianApproval() { containerStyles={styles.containerStyle} leftIconType="close" leftCallback={onBack} - backTitle={approvalType === ApprovalType.register ? 'Wallet Login' : undefined} + backTitle={approvalType === ApprovalType.communityRecovery ? 'Wallet Login' : undefined} type="leftBack" titleDom hideTouchable> diff --git a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx index 5915641d2b..8f28e01bfb 100644 --- a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx @@ -23,7 +23,7 @@ import { VerifierImage } from '../components/VerifierImage'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import myEvents from 'utils/deviceEvent'; import { verification } from 'utils/api'; -import { AuthenticationInfo, VerificationType, VerifierItem } from '@portkey-wallet/types/verifier'; +import { AuthenticationInfo, RecaptchaType, VerificationType, VerifierItem } from '@portkey-wallet/types/verifier'; import { useVerifyToken } from 'hooks/authentication'; import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useOnRequestOrSetPin } from 'hooks/login'; @@ -94,6 +94,7 @@ export default function SelectVerifier() { guardianIdentifier: loginAccount, verifierId: selectedVerifier?.id, chainId: originChainId, + operationType: RecaptchaType.register, }, }); if (requestCodeResult.verifierSessionId) { diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index 112e896859..fad4a45f71 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -7,7 +7,13 @@ import DigitInput, { DigitInputInterface } from 'components/DigitInput'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import { StyleSheet, Text } from 'react-native'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; -import { ApprovalType, VerificationType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { + ApprovalType, + RecaptchaType, + VerificationType, + VerifierInfo, + VerifyStatus, +} from '@portkey-wallet/types/verifier'; import GuardianItem from '../components/GuardianItem'; import { FontStyles } from 'assets/theme/styles'; import Loading from 'components/Loading'; @@ -123,7 +129,7 @@ export default function VerifierDetails() { switch (verificationType) { case VerificationType.communityRecovery: - case VerificationType.editGuardianApproval: + case VerificationType.optGuardianApproval: setGuardianStatus({ requestCodeResult: requestCodeResult, status: VerifyStatus.Verified, @@ -168,12 +174,20 @@ export default function VerifierDetails() { const resendCode = useCallback(async () => { try { Loading.show(); + + let recaptchaType = RecaptchaType.optGuardian; + if (verificationType === VerificationType.register) { + recaptchaType = RecaptchaType.communityRecovery; + } else if (verificationType === VerificationType.communityRecovery) { + recaptchaType = RecaptchaType.communityRecovery; + } const req = await verification.sendVerificationCode({ params: { type: LoginType[guardianItem?.guardianType as LoginType], guardianIdentifier: guardianItem?.guardianAccount, verifierId: guardianItem?.verifier?.id, chainId: originChainId, + operationType: recaptchaType, }, }); if (req.verifierSessionId) { @@ -195,6 +209,7 @@ export default function VerifierDetails() { guardianItem?.verifier?.id, originChainId, setGuardianStatus, + verificationType, ]); return ( diff --git a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx index ac2edc01e0..6c1b81c5c0 100644 --- a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx @@ -15,6 +15,7 @@ import { sleep } from '@portkey-wallet/utils'; import { ApprovalType, AuthenticationInfo, + RecaptchaType, VerificationType, VerifierInfo, VerifyStatus, @@ -63,13 +64,9 @@ function GuardianItemButton({ const { status, requestCodeResult } = itemStatus || {}; const verifyToken = useVerifyToken(); const guardianInfo = useMemo(() => { - let _verificationType = VerificationType.communityRecovery; - if ( - approvalType === ApprovalType.addGuardian || - approvalType === ApprovalType.deleteGuardian || - approvalType === ApprovalType.editGuardian - ) { - _verificationType = VerificationType.editGuardianApproval; + let _verificationType = VerificationType.optGuardianApproval; + if (approvalType === ApprovalType.communityRecovery) { + _verificationType = VerificationType.communityRecovery; } return { guardianItem, @@ -93,6 +90,10 @@ function GuardianItemButton({ guardianIdentifier: guardianInfo.guardianItem.guardianAccount, verifierId: guardianInfo.guardianItem.verifier?.id, chainId: originChainId, + operationType: + approvalType === ApprovalType.communityRecovery + ? RecaptchaType.communityRecovery + : RecaptchaType.optGuardian, }, }); if (req.verifierSessionId) { @@ -115,7 +116,7 @@ function GuardianItemButton({ CommonToast.failError(error); } Loading.hide(); - }, [onSetGuardianStatus, guardianInfo]); + }, [onSetGuardianStatus, guardianInfo, approvalType, originChainId]); const onVerifierAuth = useCallback(async () => { try { @@ -224,7 +225,7 @@ export default function GuardianItem({ setGuardianStatus, isExpired, isSuccess, - approvalType = ApprovalType.register, + approvalType = ApprovalType.communityRecovery, authenticationInfo, }: GuardianAccountItemProps) { const itemStatus = useMemo(() => guardiansStatus?.[guardianItem.key], [guardianItem.key, guardiansStatus]); diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx index 659efb724a..e20167624a 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx @@ -14,7 +14,7 @@ import { useGuardiansInfo } from 'hooks/store'; import { useGetGuardiansInfo } from 'hooks/guardian'; import Loading from 'components/Loading'; import CommonToast from 'components/CommonToast'; -import { VerificationType } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerificationType } from '@portkey-wallet/types/verifier'; import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import myEvents from 'utils/deviceEvent'; import { VerifierImage } from 'pages/Guardian/components/VerifierImage'; @@ -95,6 +95,7 @@ export default function GuardianDetail() { guardianIdentifier: guardian.guardianAccount, verifierId: guardian.verifier?.id, chainId: originChainId, + operationType: RecaptchaType.optGuardian, }, }); if (req.verifierSessionId) { diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx index 3921036782..6262073cfb 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx @@ -15,7 +15,7 @@ import { checkEmail } from '@portkey-wallet/utils/check'; import { useGuardiansInfo } from 'hooks/store'; import { LOGIN_TYPE_LIST } from '@portkey-wallet/constants/verifier'; import { PRIVATE_GUARDIAN_ACCOUNT } from '@portkey-wallet/constants/constants-ca/guardian'; -import { ApprovalType, VerificationType, VerifierItem } from '@portkey-wallet/types/verifier'; +import { ApprovalType, RecaptchaType, VerificationType, VerifierItem } from '@portkey-wallet/types/verifier'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import GuardianTypeSelectOverlay from '../components/GuardianTypeSelectOverlay'; import VerifierSelectOverlay from '../components/VerifierSelectOverlay'; @@ -249,6 +249,7 @@ const GuardianEdit: React.FC = () => { guardianIdentifier: guardianAccount, verifierId: selectedVerifier.id, chainId: originChainId, + operationType: RecaptchaType.optGuardian, }, }); if (req.verifierSessionId) { diff --git a/packages/mobile-app-did/js/utils/api.ts b/packages/mobile-app-did/js/utils/api.ts index 079ec1cffb..5a52eb45c6 100644 --- a/packages/mobile-app-did/js/utils/api.ts +++ b/packages/mobile-app-did/js/utils/api.ts @@ -5,6 +5,7 @@ import { Verification, } from '@portkey-wallet/api/api-did/verification/utils'; import { IStorage } from '@portkey-wallet/types/storage'; +import { RecaptchaType } from '@portkey-wallet/types/verifier'; import { baseStore } from '@portkey-wallet/utils/mobile/storage'; import { verifyHumanMachine } from 'components/VerifyHumanMachine'; @@ -13,7 +14,7 @@ class MobileVerification extends Verification { super(store); } public async sendVerificationCode(config: SendVerificationConfig) { - const { guardianIdentifier, verifierId } = config.params; + const { guardianIdentifier, verifierId, operationType } = config.params; const key = (guardianIdentifier || '') + (verifierId || ''); try { @@ -21,15 +22,20 @@ class MobileVerification extends Verification { if (item) { return item; } else { - const needRecaptcha = await request.verify.checkGoogleRecaptcha(); + let isNeedRecaptcha = operationType === RecaptchaType.register; + if (!isNeedRecaptcha) { + const result = await request.verify.checkGoogleRecaptcha(); + isNeedRecaptcha = !!result; + } - if (needRecaptcha) { + if (isNeedRecaptcha) { // TODO: add language const reCaptchaToken = await verifyHumanMachine('en'); config.headers = { reCaptchaToken: reCaptchaToken as string, }; } + const req = await request.verify.sendVerificationRequest(config); await this.set(key, { ...req, time: Date.now() }); return req; diff --git a/packages/types/verifier.ts b/packages/types/verifier.ts index 2d03e58ed1..0cb7b9ce56 100644 --- a/packages/types/verifier.ts +++ b/packages/types/verifier.ts @@ -19,17 +19,23 @@ export enum VerificationType { addGuardian, setLoginAccount, addManager, - editGuardianApproval, + optGuardianApproval, } export enum ApprovalType { - register, + communityRecovery, addGuardian, deleteGuardian, editGuardian, removeOtherManager, } +export enum RecaptchaType { + register = 0, + communityRecovery = 1, + optGuardian = 2, +} + export interface VerifierInfo { verifierId: string; verificationDoc: string; diff --git a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx index 62884d6665..f1585b95f7 100644 --- a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx +++ b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx @@ -1,6 +1,6 @@ import { setCurrentGuardianAction, setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; import { UserGuardianItem, UserGuardianStatus } from '@portkey-wallet/store/store-ca/guardians/type'; -import { VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { ApprovalType, RecaptchaType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import { Button, message } from 'antd'; import clsx from 'clsx'; import VerifierPair from 'components/VerifierPair'; @@ -63,6 +63,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount type: LoginType[item.guardianType], verifierId: item?.verifier?.id || '', chainId: originChainId, + operationType: RecaptchaType.optGuardian, }, }); setLoading(false); @@ -107,12 +108,21 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount throw 'User registration information is invalid, please fill in the registration method again'; } + let approvalType = ApprovalType.communityRecovery; + if (query && query.indexOf('removeManage') !== -1) { + approvalType = ApprovalType.deleteGuardian; + } + const result = await verification.sendVerificationCode({ params: { guardianIdentifier: item?.guardianAccount, type: LoginType[item.guardianType], verifierId: item.verifier?.id || '', chainId: originChainId, + operationType: + approvalType === ApprovalType.communityRecovery + ? RecaptchaType.communityRecovery + : RecaptchaType.optGuardian, }, }); setLoading(false); @@ -133,7 +143,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount status: VerifyStatus.Verifying, }), ); - if (query && query.indexOf('removeManage') !== -1) { + if (approvalType === ApprovalType.deleteGuardian) { navigate('/setting/wallet-security/manage-devices/verifier-account', { state: query }); } else { navigate('/login/verifier-account', { state: 'login' }); diff --git a/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx b/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx index 32de1c2405..0b78f78afc 100644 --- a/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx +++ b/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx @@ -29,7 +29,7 @@ import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { request } from '@portkey-wallet/api/api-did'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { handleVerificationDoc } from '@portkey-wallet/utils/guardian'; -import { VerifyStatus } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifyStatus } from '@portkey-wallet/types/verifier'; import verificationApiConfig from '@portkey-wallet/api/api-did/verification'; import GuardianAddPrompt from './Prompt'; import GuardianAddPopup from './Popup'; @@ -301,6 +301,7 @@ export default function AddGuardian() { type: LoginType[guardianType as LoginType], verifierId: selectVerifierItem?.id || '', chainId: currentChain?.chainId || originChainId, + operationType: RecaptchaType.optGuardian, }, }); setLoading(false); diff --git a/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx b/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx index f338649162..7c1847eb50 100644 --- a/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx +++ b/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx @@ -34,6 +34,7 @@ import { useCommonState } from 'store/Provider/hooks'; import AccountShow from '../components/AccountShow'; import { guardianIconMap } from '../utils'; import './index.less'; +import { RecaptchaType } from '@portkey-wallet/types/verifier'; export default function GuardiansView() { const { t } = useTranslation(); @@ -184,6 +185,7 @@ export default function GuardiansView() { type: LoginType[opGuardian?.guardianType as LoginType], verifierId: opGuardian?.verifier?.id || '', chainId: originChainId, + operationType: RecaptchaType.optGuardian, }, }); setLoading(false); diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx index de065dca8e..86b7480044 100644 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx +++ b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx @@ -19,6 +19,7 @@ import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQ import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import './index.less'; +import { RecaptchaType } from '@portkey-wallet/types/verifier'; export default function SelectVerifier() { const { verifierMap } = useGuardiansInfo(); @@ -66,6 +67,7 @@ export default function SelectVerifier() { type: LoginType[loginAccount.loginType], verifierId: selectItem.id, chainId: originChainId, + operationType: RecaptchaType.register, }, }); setLoading(false); diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 70fdb1d357..364a861bdf 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -4,7 +4,7 @@ import { useAppDispatch, useLoginInfo, useGuardiansInfo, useUserInfo, useLoading import { useCallback, useMemo } from 'react'; import { message } from 'antd'; import { setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; -import { VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import useLocationState from 'hooks/useLocationState'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { handleGuardian } from 'utils/sandboxUtil/handleGuardian'; @@ -189,6 +189,15 @@ export default function VerifierAccount() { return !!currentGuardian?.isInitStatus; }, [currentGuardian, state]); + const recaptchaType = useMemo(() => { + if (state === 'register') { + return RecaptchaType.register; + } else if (state === 'login') { + return RecaptchaType.communityRecovery; + } + return RecaptchaType.optGuardian; + }, [state]); + const renderContent = useMemo( () => (
    @@ -198,10 +207,11 @@ export default function VerifierAccount() { currentGuardian={currentGuardian} guardianType={loginAccount?.loginType} onSuccess={onSuccess} + recaptchaType={recaptchaType} />
    ), - [currentGuardian, isInitStatus, loginAccount, onSuccess], + [currentGuardian, isInitStatus, loginAccount, onSuccess, recaptchaType], ); const props = useMemo( diff --git a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx index 6bb3cbfe81..c68d85239d 100644 --- a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx @@ -17,6 +17,7 @@ import { verification } from 'utils/api'; import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCommonState } from 'store/Provider/hooks'; import { useLocation } from 'react-router'; +import { RecaptchaType } from '@portkey-wallet/types/verifier'; const MAX_TIMER = 60; @@ -26,6 +27,7 @@ enum VerificationError { } interface VerifierPageProps { + recaptchaType: RecaptchaType; loginAccount?: LoginInfo; currentGuardian?: UserGuardianItem; guardianType?: LoginType; @@ -33,7 +35,13 @@ interface VerifierPageProps { onSuccess?: (res: { verificationDoc: string; signature: string; verifierId: string }) => void; } -export default function VerifierPage({ currentGuardian, guardianType, isInitStatus, onSuccess }: VerifierPageProps) { +export default function VerifierPage({ + recaptchaType, + currentGuardian, + guardianType, + isInitStatus, + onSuccess, +}: VerifierPageProps) { const { setLoading } = useLoading(); const [timer, setTimer] = useState(0); const { isNotLessThan768 } = useCommonState(); @@ -107,6 +115,7 @@ export default function VerifierPage({ currentGuardian, guardianType, isInitStat type: LoginType[guardianType], verifierId: currentGuardian.verifier?.id || '', chainId: originChainId, + operationType: recaptchaType, }, }); setLoading(false); @@ -128,7 +137,7 @@ export default function VerifierPage({ currentGuardian, guardianType, isInitStat const _error = verifyErrorHandler(error); message.error(_error); } - }, [currentGuardian, guardianType, originChainId, dispatch, setLoading]); + }, [currentGuardian, guardianType, originChainId, dispatch, setLoading, recaptchaType]); useEffect(() => { if (timer !== MAX_TIMER) return; diff --git a/packages/web-extension-did/app/web/utils/api.ts b/packages/web-extension-did/app/web/utils/api.ts index 79828e5bfd..8361769c3a 100644 --- a/packages/web-extension-did/app/web/utils/api.ts +++ b/packages/web-extension-did/app/web/utils/api.ts @@ -7,13 +7,14 @@ import { localStorage } from 'redux-persist-webextension-storage'; import { IStorage } from '@portkey-wallet/types/storage'; import { request } from '@portkey-wallet/api/api-did'; import { checkReCaptcha } from './lib/checkReCaptcha'; +import { RecaptchaType } from '@portkey-wallet/types/verifier'; export class ExtensionVerification extends Verification { constructor(store: IStorage) { super(store); } public async sendVerificationCode(config: SendVerificationConfig) { - const { guardianIdentifier, verifierId } = config.params; + const { guardianIdentifier, verifierId, operationType } = config.params; const key = (guardianIdentifier || '') + (verifierId || ''); try { @@ -21,7 +22,8 @@ export class ExtensionVerification extends Verification { if (item) { return item; } else { - const reCaptcha = await checkReCaptcha(); + const isNeedRecaptcha = operationType === RecaptchaType.register; + const reCaptcha = await checkReCaptcha(isNeedRecaptcha); if (reCaptcha) { config.headers = { reCaptchaToken: reCaptcha, diff --git a/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts b/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts index a65d111094..b2c7f7d5f3 100644 --- a/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts +++ b/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts @@ -5,9 +5,13 @@ import { request } from '@portkey-wallet/api/api-did'; * check is need to call Google reCAPTCHA * @returns {string} check response */ -export const checkReCaptcha = async () => { - const req = await request.verify.checkGoogleRecaptcha(); - if (req) { +export const checkReCaptcha = async (isNeedRecaptcha = false) => { + if (!isNeedRecaptcha) { + const req = await request.verify.checkGoogleRecaptcha(); + isNeedRecaptcha = !!req; + } + + if (isNeedRecaptcha) { // Google reCAPTCHA const reCaptcha = await reCAPTCHAAction(); if (reCaptcha?.error) throw reCaptcha.message || reCaptcha.error; From ba7ee17c308ee99a9bc2f1b5d2118617bd8d8469 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 15 Jun 2023 10:39:37 +0800 Subject: [PATCH 143/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20checkGoogl?= =?UTF-8?q?eRecaptcha=20params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/utils/api.ts | 6 +++++- .../app/web/utils/lib/checkReCaptcha.ts | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/utils/api.ts b/packages/mobile-app-did/js/utils/api.ts index 5a52eb45c6..d627cfd013 100644 --- a/packages/mobile-app-did/js/utils/api.ts +++ b/packages/mobile-app-did/js/utils/api.ts @@ -24,7 +24,11 @@ class MobileVerification extends Verification { } else { let isNeedRecaptcha = operationType === RecaptchaType.register; if (!isNeedRecaptcha) { - const result = await request.verify.checkGoogleRecaptcha(); + const result = await request.verify.checkGoogleRecaptcha({ + params: { + operationType, + }, + }); isNeedRecaptcha = !!result; } diff --git a/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts b/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts index b2c7f7d5f3..ad9fd5508b 100644 --- a/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts +++ b/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts @@ -1,3 +1,4 @@ +import { RecaptchaType } from '@portkey-wallet/types/verifier'; import { reCAPTCHAAction } from './serviceWorkerAction'; import { request } from '@portkey-wallet/api/api-did'; @@ -5,9 +6,13 @@ import { request } from '@portkey-wallet/api/api-did'; * check is need to call Google reCAPTCHA * @returns {string} check response */ -export const checkReCaptcha = async (isNeedRecaptcha = false) => { +export const checkReCaptcha = async (operationType = RecaptchaType.register, isNeedRecaptcha = false) => { if (!isNeedRecaptcha) { - const req = await request.verify.checkGoogleRecaptcha(); + const req = await request.verify.checkGoogleRecaptcha({ + params: { + operationType, + }, + }); isNeedRecaptcha = !!req; } From 8f9ab25c1e7102ba1d79e539cb7296ade4a9a852 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 15 Jun 2023 10:40:25 +0800 Subject: [PATCH 144/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20checkRe?= =?UTF-8?q?Captcha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/utils/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/utils/api.ts b/packages/web-extension-did/app/web/utils/api.ts index 8361769c3a..4aa925e5dd 100644 --- a/packages/web-extension-did/app/web/utils/api.ts +++ b/packages/web-extension-did/app/web/utils/api.ts @@ -23,7 +23,7 @@ export class ExtensionVerification extends Verification { return item; } else { const isNeedRecaptcha = operationType === RecaptchaType.register; - const reCaptcha = await checkReCaptcha(isNeedRecaptcha); + const reCaptcha = await checkReCaptcha(operationType, isNeedRecaptcha); if (reCaptcha) { config.headers = { reCaptchaToken: reCaptcha, From 52f4b64929d7a01acbb2c60694d69ffe6a4f2af8 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 15 Jun 2023 10:41:13 +0800 Subject: [PATCH 145/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20operationType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index fad4a45f71..dc1dfd0418 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -177,7 +177,7 @@ export default function VerifierDetails() { let recaptchaType = RecaptchaType.optGuardian; if (verificationType === VerificationType.register) { - recaptchaType = RecaptchaType.communityRecovery; + recaptchaType = RecaptchaType.register; } else if (verificationType === VerificationType.communityRecovery) { recaptchaType = RecaptchaType.communityRecovery; } From 5e88bde33577ad4229a71a4ce63052a3f4337215 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 15 Jun 2023 10:42:59 +0800 Subject: [PATCH 146/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20removeManage=20ap?= =?UTF-8?q?provalType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/GuardianApproval/components/GuardianItems/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx index f1585b95f7..ca979b9547 100644 --- a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx +++ b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx @@ -110,7 +110,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount let approvalType = ApprovalType.communityRecovery; if (query && query.indexOf('removeManage') !== -1) { - approvalType = ApprovalType.deleteGuardian; + approvalType = ApprovalType.removeOtherManager; } const result = await verification.sendVerificationCode({ @@ -143,7 +143,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount status: VerifyStatus.Verifying, }), ); - if (approvalType === ApprovalType.deleteGuardian) { + if (approvalType === ApprovalType.removeOtherManager) { navigate('/setting/wallet-security/manage-devices/verifier-account', { state: query }); } else { navigate('/login/verifier-account', { state: 'login' }); From d091c2d75fc7f28ef034accc7333f8ec37ee8722 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 10:58:38 +0800 Subject: [PATCH 147/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20delete=20web?= =?UTF-8?q?view=20discover=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/BrowserOverlay.tsx | 181 ------------------ .../js/pages/Activity/ViewOnWebView/index.tsx | 37 +--- 2 files changed, 5 insertions(+), 213 deletions(-) delete mode 100644 packages/mobile-app-did/js/pages/Activity/ViewOnWebView/components/BrowserOverlay.tsx diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/components/BrowserOverlay.tsx b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/components/BrowserOverlay.tsx deleted file mode 100644 index a08c0d6698..0000000000 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/components/BrowserOverlay.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import React, { useCallback } from 'react'; -import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, TouchableOpacity, View, Share } from 'react-native'; -import { TextL, TextS } from 'components/CommonText'; -import { defaultColors } from 'assets/theme'; -import fonts from 'assets/theme/fonts'; -import { pTd } from 'utils/unit'; -import Svg from 'components/Svg'; -import { useLanguage } from 'i18n/hooks'; -import GStyles from 'assets/theme/GStyles'; -import CommonAvatar from 'components/CommonAvatar'; -import { screenWidth } from '@portkey-wallet/utils/mobile/device'; -import { FontStyles } from 'assets/theme/styles'; -import { setStringAsync } from 'expo-clipboard'; -import CommonToast from 'components/CommonToast'; -import { getFaviconUrl } from 'utils'; -import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; -import { isIOS } from '@rneui/base'; - -const BrowserEditModal = ({ - browserInfo, - handleReload, -}: { - browserInfo: IRecordsItemType; - setBrowserInfo: any; - handleReload: any; -}) => { - const { t } = useLanguage(); - - enum HANDLE_TYPE { - REFRESH = 'Refresh', - COPY = 'Copy URL', - SHARE = 'Share', - CLOSE = 'Close', - CANCEL = 'Cancel', - } - - const handleArray = [ - { title: HANDLE_TYPE.REFRESH, icon: 'refresh1' }, - { title: HANDLE_TYPE.COPY, icon: 'copy1' }, - { title: HANDLE_TYPE.SHARE, icon: 'share' }, - ] as const; - - const handleUrl = useCallback( - async (type: HANDLE_TYPE) => { - let isCopy = false; - - switch (type) { - case HANDLE_TYPE.REFRESH: - handleReload?.(); - OverlayModal.hide(); - break; - - case HANDLE_TYPE.COPY: - isCopy = await setStringAsync(browserInfo?.url || ''); - isCopy && CommonToast.success(t('Copy Success')); - break; - - case HANDLE_TYPE.SHARE: - await Share.share({ - message: isIOS ? browserInfo?.title ?? browserInfo.url : browserInfo?.url, - url: browserInfo?.url ?? browserInfo?.title ?? '', - title: browserInfo?.title ?? browserInfo.url, - }).catch(shareError => { - console.log(shareError); - }); - break; - - case HANDLE_TYPE.CLOSE: - OverlayModal.hide(); - break; - - case HANDLE_TYPE.CANCEL: - OverlayModal.hide(); - break; - - default: - break; - } - }, - [ - HANDLE_TYPE.REFRESH, - HANDLE_TYPE.COPY, - HANDLE_TYPE.SHARE, - HANDLE_TYPE.CLOSE, - HANDLE_TYPE.CANCEL, - handleReload, - browserInfo.url, - browserInfo?.title, - t, - ], - ); - - return ( - - - - - {browserInfo?.title} - - - handleUrl(HANDLE_TYPE.CANCEL)}> - - - - - {handleArray.map((ele, index) => ( - handleUrl(ele.title)}> - - - - - {ele.title} - - - ))} - - - handleUrl(HANDLE_TYPE.CANCEL)}> - {t('Cancel')} - - - ); -}; - -export const showBrowserModal = (props: { browserInfo: IRecordsItemType; setBrowserInfo: any; handleReload: any }) => { - OverlayModal.show(, { - position: 'bottom', - containerStyle: { backgroundColor: defaultColors.bg6 }, - }); -}; - -export default { - showBrowserModal, -}; - -const styles = StyleSheet.create({ - modalStyle: { - ...GStyles.paddingArg(16, 20), - backgroundColor: defaultColors.bg6, - width: screenWidth, - }, - headerWrap: {}, - title: { - textAlign: 'left', - height: pTd(22), - lineHeight: pTd(22), - marginVertical: pTd(13), - paddingLeft: pTd(8), - ...fonts.mediumFont, - }, - listWrap: { - marginTop: pTd(24), - marginBottom: pTd(24), - paddingLeft: pTd(12), - display: 'flex', - flexWrap: 'wrap', - flexDirection: 'row', - }, - listItem: { - marginRight: pTd(34), - borderRadius: pTd(6), - overflow: 'hidden', - }, - svgWrap: { - backgroundColor: defaultColors.bg1, - }, - itemTitle: { - textAlign: 'center', - marginTop: pTd(8), - }, - divider: { - width: '100%', - height: StyleSheet.hairlineWidth, - backgroundColor: defaultColors.bg7, - }, - cancelButton: { - height: pTd(44), - fontSize: pTd(16), - }, -}); diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx index e998181927..ef11dd48a1 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx @@ -1,15 +1,11 @@ import React, { useCallback, useRef, useState } from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; +import { StyleSheet } from 'react-native'; import { defaultColors } from 'assets/theme'; import WebView from 'react-native-webview'; import CustomHeader from 'components/CustomHeader'; import SafeAreaBox from 'components/SafeAreaBox'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; -import Svg from 'components/Svg'; -import BrowserOverlay from './components/BrowserOverlay'; import { pTd } from 'utils/unit'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { upDateRecordsItem } from '@portkey-wallet/store/store-ca/discover/slice'; import navigationService from 'utils/navigationService'; import { ACH_REDIRECT_URL, ACH_WITHDRAW_URL } from 'constants/common'; import { useHandleAchSell } from './hooks/useHandleAchSell'; @@ -24,7 +20,7 @@ const safeAreaColorMap = { export type SafeAreaColorMapKeyUnit = keyof typeof safeAreaColorMap; -type WebViewPageType = 'default' | 'discover' | 'ach' | 'achSell'; +type WebViewPageType = 'default' | 'ach' | 'achSell'; export interface AchSellParams { orderNo?: string; @@ -45,21 +41,10 @@ const ViewOnWebView: React.FC = () => { params?: any; }>(); - const [browserInfo, setBrowserInfo] = useState({ url, title }); + const [browserInfo] = useState({ url, title }); - const dispatch = useAppCommonDispatch(); const webViewRef = React.useRef(null); - const handleReload = useCallback(() => { - if (webViewPageType === 'default') return; - - if (webViewRef && webViewRef.current) webViewRef.current.reload(); - }, [webViewPageType]); - - const showDialog = useCallback(() => { - BrowserOverlay.showBrowserModal({ browserInfo, setBrowserInfo, handleReload }); - }, [browserInfo, handleReload]); - const handleAchSell = useHandleAchSell(); const isAchSellHandled = useRef(false); @@ -84,23 +69,12 @@ const ViewOnWebView: React.FC = () => { handleAchSell(orderNo); } } - dispatch(upDateRecordsItem({ url, title: title ? title : navState.title })); }, - [dispatch, handleAchSell, params, title, url, webViewPageType], + [handleAchSell, params, webViewPageType], ); return ( - - -
    - ) - } - /> + Date: Thu, 15 Jun 2023 11:28:51 +0800 Subject: [PATCH 148/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20third=20pa?= =?UTF-8?q?rty=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/network-mainnet.ts | 2 ++ packages/constants/constants-ca/network-test1.ts | 2 ++ packages/constants/constants-ca/network-test2.ts | 2 ++ packages/constants/constants-ca/network-testnet.ts | 2 ++ packages/web-extension-did/app/web/constants/index.ts | 3 ++- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/constants/constants-ca/network-mainnet.ts b/packages/constants/constants-ca/network-mainnet.ts index fa01d9fe29..45602fd6fc 100644 --- a/packages/constants/constants-ca/network-mainnet.ts +++ b/packages/constants/constants-ca/network-mainnet.ts @@ -10,3 +10,5 @@ export const DefaultChainId = 'AELF'; export const OfficialWebsite = 'https://portkey.finance'; export const BingoGame = 'https://portkey-bingo-game.vercel.app'; + +export const ThirdParty = `https://thirdparty.portkey.finance`; diff --git a/packages/constants/constants-ca/network-test1.ts b/packages/constants/constants-ca/network-test1.ts index 1d5993373c..368ef789f7 100644 --- a/packages/constants/constants-ca/network-test1.ts +++ b/packages/constants/constants-ca/network-test1.ts @@ -7,3 +7,5 @@ export const DefaultChainId = 'AELF'; export const OfficialWebsite = 'https://portkey.finance'; export const BingoGame = 'http://192.168.66.240:3000'; + +export const ThirdParty = `https://openlogin-test.portkey.finance`; diff --git a/packages/constants/constants-ca/network-test2.ts b/packages/constants/constants-ca/network-test2.ts index 28badc43f2..40a8eadfb6 100644 --- a/packages/constants/constants-ca/network-test2.ts +++ b/packages/constants/constants-ca/network-test2.ts @@ -23,3 +23,5 @@ export const NetworkList: NetworkItem[] = [ export const DefaultChainId = 'AELF'; export const OfficialWebsite = 'https://portkey.finance'; + +export const ThirdParty = `https://openlogin-test.portkey.finance`; diff --git a/packages/constants/constants-ca/network-testnet.ts b/packages/constants/constants-ca/network-testnet.ts index fa01d9fe29..45602fd6fc 100644 --- a/packages/constants/constants-ca/network-testnet.ts +++ b/packages/constants/constants-ca/network-testnet.ts @@ -10,3 +10,5 @@ export const DefaultChainId = 'AELF'; export const OfficialWebsite = 'https://portkey.finance'; export const BingoGame = 'https://portkey-bingo-game.vercel.app'; + +export const ThirdParty = `https://thirdparty.portkey.finance`; diff --git a/packages/web-extension-did/app/web/constants/index.ts b/packages/web-extension-did/app/web/constants/index.ts index 68b848f680..798b077fee 100644 --- a/packages/web-extension-did/app/web/constants/index.ts +++ b/packages/web-extension-did/app/web/constants/index.ts @@ -1,5 +1,6 @@ import { DeviceType } from '@portkey-wallet/types/types-ca/device'; import walletMessage from 'messages/walletMessage'; +import { ThirdParty } from '@portkey-wallet/constants/constants-ca/network'; export const prefixCls = 'portkey'; // redux @@ -44,4 +45,4 @@ export const AUTH_APPLE_URL = `${AUTH_HOST}/apple-auth`; export const RECAPTCHA_URL = `${AUTH_HOST}/recaptcha-check`; // after ach-sell, redirect url, then wake up extension. -export const ACH_WITHDRAW_URL = `https://openlogin-test.portkey.finance/extension-bridge?method=${walletMessage.ACH_SELL_REDIRECT}`; // TODO: change url +export const ACH_WITHDRAW_URL = `${ThirdParty}/extension-bridge?method=${walletMessage.ACH_SELL_REDIRECT}`; From bac7b5c6ad31f154c8d144b0b7ff8854ca0d2ba7 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 13:35:44 +0800 Subject: [PATCH 149/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20tablist=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/assets/image/svgs.js | 2 +- .../js/assets/image/svgs/discover.svg | 6 ++++-- .../js/assets/image/svgs/logo-icon.svg | 15 +++------------ .../mobile-app-did/js/assets/image/svgs/my.svg | 17 +++-------------- .../js/components/TokenListItem/index.tsx | 1 - packages/mobile-app-did/js/navigation/Tab.tsx | 7 +++---- 6 files changed, 14 insertions(+), 34 deletions(-) diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index ac95bff9eb..a94257f070 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n jrosmjmwjg\n \n \n \n \n \n \n \n \n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n rewbbtbkvk\n \n \n \n \n \n \n \n \n \n \n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/discover.svg b/packages/mobile-app-did/js/assets/image/svgs/discover.svg index c2e59f2e94..33ea29cff6 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/discover.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/discover.svg @@ -1,3 +1,5 @@ - - + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/logo-icon.svg b/packages/mobile-app-did/js/assets/image/svgs/logo-icon.svg index e613ff0379..bef92285ff 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/logo-icon.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/logo-icon.svg @@ -1,12 +1,3 @@ - - - jrosmjmwjg - - - - - - - - - \ No newline at end of file + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/my.svg b/packages/mobile-app-did/js/assets/image/svgs/my.svg index df49ffea91..354f6915f8 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/my.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/my.svg @@ -1,14 +1,3 @@ - - - rewbbtbkvk - - - - - - - - - - - \ No newline at end of file + + + diff --git a/packages/mobile-app-did/js/components/TokenListItem/index.tsx b/packages/mobile-app-did/js/components/TokenListItem/index.tsx index 0f89623d9d..b63f4bd202 100644 --- a/packages/mobile-app-did/js/components/TokenListItem/index.tsx +++ b/packages/mobile-app-did/js/components/TokenListItem/index.tsx @@ -1,5 +1,4 @@ import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; -import { ZERO } from '@portkey-wallet/constants/misc'; import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { defaultColors } from 'assets/theme'; diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index fc4365ec44..d8f9bc6788 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -14,7 +14,7 @@ import DiscoverHome from 'pages/Discover/DiscoverHome'; const Tab = createBottomTabNavigator(); type TabMenuTypeType = { icon: IconName; component: React.FC }; export interface TabMenuItem extends TabMenuTypeType { - name: 'Wallet' | 'Discover' | 'Settings'; + name: 'Wallet' | 'Discover' | 'My'; label: string; index: number; } @@ -24,7 +24,7 @@ export const tabMenuTypeMap: Record = { icon: 'logo-icon', component: DashBoard, }, - Settings: { + My: { icon: 'my', component: MyMenu, }, @@ -37,7 +37,7 @@ export const tabMenuTypeMap: Record = { export const defaultTabMenuList: TabMenuItem[] = [ { name: 'Wallet', label: 'Wallet', index: 0, icon: 'logo-icon', component: DashBoard }, { name: 'Discover', label: 'Discover', index: 1, icon: 'discover', component: DiscoverHome }, - { name: 'Settings', label: 'My', index: 2, icon: 'my', component: MyMenu }, + { name: 'My', label: 'My', index: 2, icon: 'my', component: MyMenu }, ]; export default function TabRoot() { @@ -46,7 +46,6 @@ export default function TabRoot() { const tabMenuListStore = useTabMenuList(); const tabMenuList = useMemo(() => { - return defaultTabMenuList; const _tabMenuListStore = tabMenuListStore.reduce((acc: typeof tabMenuListStore, cur) => { if (!acc.find(item => item.type.value === cur.type.value)) { acc.push(cur); From 45deb9a5c2848109633cc26c8447493620a15baa Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Thu, 15 Jun 2023 13:49:19 +0800 Subject: [PATCH 150/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20entryScriptWeb3?= =?UTF-8?q?=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/Test/Discover/index.tsx | 2 -- packages/mobile-app-did/js/components/TabsDrawer/context.ts | 2 +- packages/mobile-app-did/js/components/Updater/index.tsx | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/Test/Discover/index.tsx b/packages/mobile-app-did/js/Test/Discover/index.tsx index beb4997097..283cb7a896 100644 --- a/packages/mobile-app-did/js/Test/Discover/index.tsx +++ b/packages/mobile-app-did/js/Test/Discover/index.tsx @@ -3,7 +3,6 @@ import { defaultColors } from 'assets/theme'; import CustomHeader from 'components/CustomHeader'; import SafeAreaBox from 'components/SafeAreaBox'; import navigationService from 'utils/navigationService'; -import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; import CommonButton from 'components/CommonButton'; import DappEventBus from 'dapp/dappEventBus'; import ProviderWebview from 'components/ProviderWebview'; @@ -17,7 +16,6 @@ const safeAreaColorMap = { export type SafeAreaColorMapKeyUnit = keyof typeof safeAreaColorMap; -EntryScriptWeb3.init(); const Discover: React.FC = () => { return ( diff --git a/packages/mobile-app-did/js/components/TabsDrawer/context.ts b/packages/mobile-app-did/js/components/TabsDrawer/context.ts index fbc80d3f31..4e9f9b5235 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/context.ts +++ b/packages/mobile-app-did/js/components/TabsDrawer/context.ts @@ -13,5 +13,5 @@ export function useBrowser(): BrowserState { export interface IBrowserTab { reload: IWebView['reload']; - capture: ViewShot['capture']; + capture?: ViewShot['capture']; } diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index 301e1183e7..cfc843e720 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -18,6 +18,7 @@ import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; import { useDiscoverGroupList, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; +import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; request.setExceptionManager(exceptionManager); export default function Updater() { @@ -49,6 +50,8 @@ export default function Updater() { }, [onLocking]); useEffectOnce(() => { + // init entryScriptWeb3 + EntryScriptWeb3.init(); socket.onScanLoginSuccess(data => { CommonToast.success(data.body); }); From 33c7c6d11680300824cbdccad9bc8898c159f49e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 14:01:56 +0800 Subject: [PATCH 151/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20the=20cm?= =?UTF-8?q?s=20tab=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/navigation/Tab.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index d8f9bc6788..1c37d95921 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -14,7 +14,7 @@ import DiscoverHome from 'pages/Discover/DiscoverHome'; const Tab = createBottomTabNavigator(); type TabMenuTypeType = { icon: IconName; component: React.FC }; export interface TabMenuItem extends TabMenuTypeType { - name: 'Wallet' | 'Discover' | 'My'; + name: 'Wallet' | 'Discover' | 'Settings'; label: string; index: number; } @@ -24,7 +24,7 @@ export const tabMenuTypeMap: Record = { icon: 'logo-icon', component: DashBoard, }, - My: { + Settings: { icon: 'my', component: MyMenu, }, @@ -37,7 +37,7 @@ export const tabMenuTypeMap: Record = { export const defaultTabMenuList: TabMenuItem[] = [ { name: 'Wallet', label: 'Wallet', index: 0, icon: 'logo-icon', component: DashBoard }, { name: 'Discover', label: 'Discover', index: 1, icon: 'discover', component: DiscoverHome }, - { name: 'My', label: 'My', index: 2, icon: 'my', component: MyMenu }, + { name: 'Settings', label: 'My', index: 2, icon: 'my', component: MyMenu }, ]; export default function TabRoot() { From 6d63bf7e16e4841b2e0ff62dec7029ee6dcc88dd Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 15:01:26 +0800 Subject: [PATCH 152/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20cr=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/index.tsx | 15 ++- .../dapp/components/ConnectOverlay/index.tsx | 6 +- .../dapp/components/DappInfoSection/index.tsx | 4 +- .../js/dapp/components/SignOverlay/index.tsx | 6 +- .../TransactionDataSection/index.tsx | 4 +- .../components/TransactionOverlay/index.tsx | 119 +----------------- .../TransactionOverlay/styles/index.ts | 115 +++++++++++++++++ .../components/CommonSection/index.tsx | 48 ------- .../DiscoverCmsListSection/index.tsx | 2 - .../Discover/components/RecordItem/index.tsx | 6 +- .../components/RecordSection/index.tsx | 4 +- .../SearchDiscoverSection/index.tsx | 4 +- .../components/SimulatedInputBox/index.tsx | 6 +- 13 files changed, 146 insertions(+), 193 deletions(-) create mode 100644 packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts delete mode 100644 packages/mobile-app-did/js/pages/Discover/components/CommonSection/index.tsx diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx index b147a9c4a4..7093eece7d 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -4,10 +4,9 @@ import React from 'react'; import { Drawer } from 'react-native-drawer-layout'; import TabsDrawerContent from './TabsDrawerContent'; import { usePin } from 'hooks/store'; - -interface TabsDrawerPropsType { +type TabsDrawerPropsType = { children: React.ReactNode; -} +}; export default function TabsDrawer(props: TabsDrawerPropsType) { const { children } = props; @@ -16,17 +15,17 @@ export default function TabsDrawer(props: TabsDrawerPropsType) { const { isDrawerOpen } = useAppCASelector(state => state.discover); const tabsDrawerContent = React.useMemo(() => , []); + + const otherProps = {} as { onClose: () => void; onOpen: () => void }; + return ( console.log('open')} - onClose={() => console.log('close')} drawerPosition="right" drawerStyle={{ width: ScreenWidth }} - renderDrawerContent={() => { - return tabsDrawerContent; - }}> + renderDrawerContent={() => tabsDrawerContent} + {...otherProps}> {children} ); diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 0d663ab910..d0dfc8d271 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, View, Image } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import fonts from 'assets/theme/fonts'; import { pTd } from 'utils/unit'; @@ -21,11 +21,11 @@ import { useGStyles } from 'assets/theme/useGStyles'; import { CommonButtonProps } from 'components/CommonButton'; import DappInfoSection from '../DappInfoSection'; -interface ConnectModalType { +type ConnectModalType = { dappInfo: DappStoreItem; onReject: () => void; onApprove: () => void; -} +}; const ConnectModal = (props: ConnectModalType) => { const { dappInfo, onReject, onApprove } = props; diff --git a/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx b/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx index bac325e426..7ec72542e0 100644 --- a/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/DappInfoSection/index.tsx @@ -10,9 +10,9 @@ import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; import { getHost } from '@portkey-wallet/utils/dapp/browser'; -interface DappInfoSectionType { +type DappInfoSectionType = { dappInfo: DappStoreItem; -} +}; export const DappInfoSection = (props: DappInfoSectionType) => { const { diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx index 5d0c794bf6..d8d3bd1f1f 100644 --- a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -12,14 +12,14 @@ import { CommonButtonProps } from 'components/CommonButton'; import DappInfoSection from '../DappInfoSection'; import { GetSignatureParams } from '@portkey/provider-types'; import TransactionDataSection from '../TransactionDataSection'; -import { TextL, TextXXXL } from 'components/CommonText'; +import { TextXXXL } from 'components/CommonText'; -interface SignModalPropsType { +type SignModalPropsType = { dappInfo: DappStoreItem; signInfo: GetSignatureParams; onReject: () => void; onSign: () => void; -} +}; const SignModal = (props: SignModalPropsType) => { const { dappInfo, signInfo, onReject, onSign } = props; const { t } = useLanguage(); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx index 8a339d2e0e..33ea225f2a 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx @@ -9,10 +9,10 @@ import { FontStyles } from 'assets/theme/styles'; import GStyles from 'assets/theme/GStyles'; import Touchable from 'components/Touchable'; -interface TransactionDataSectionType { +type TransactionDataSectionType = { dataInfo: { [key: string]: any }; style?: ViewStyle; -} +}; export const TransactionDataSection = (props: TransactionDataSectionType) => { const { dataInfo, style = {} } = props; diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 7cb03eb620..d56c5de69b 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -1,9 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, View, Text, ScrollView } from 'react-native'; -import { defaultColors } from 'assets/theme'; +import { View, Text, ScrollView } from 'react-native'; import fonts from 'assets/theme/fonts'; -import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; import { ModalBody } from 'components/ModalBody'; import { TextM, TextS } from 'components/CommonText'; @@ -23,17 +21,17 @@ import { usePin } from 'hooks/store'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount } from 'utils/redux'; import { customFetch } from '@portkey-wallet/utils/fetch'; -import { screenHeight } from '@portkey-wallet/utils/mobile/device'; import DappInfoSection from '../DappInfoSection'; import TransactionDataSection from '../TransactionDataSection'; import { ELF_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; +import { styles, transferGroupStyle } from './styles/index'; -interface TransactionModalPropsType { +type TransactionModalPropsType = { dappInfo: DappStoreItem; transactionInfo: SendTransactionParams & { params: any }; onReject: () => void; onSign: () => void; -} +}; const ConnectModal = (props: TransactionModalPropsType) => { const { dappInfo, transactionInfo, onReject, onSign } = props; const { t } = useLanguage(); @@ -335,112 +333,3 @@ export const showTransactionModal = (props: TransactionModalPropsType) => { export default { showTransactionModal, }; - -const styles = StyleSheet.create({ - contentWrap: { - paddingLeft: pTd(20), - paddingRight: pTd(20), - }, - - method: { - overflow: 'hidden', - borderRadius: pTd(6), - marginTop: pTd(24), - textAlign: 'center', - color: defaultColors.primaryColor, - backgroundColor: defaultColors.bg9, - ...GStyles.paddingArg(2, 8), - }, - transactionDataSection: { - marginTop: pTd(16), - }, - scrollSection: { - height: screenHeight / 2, - }, - blank: { - height: pTd(200), - }, - error: { - color: defaultColors.error, - textAlign: 'left', - marginTop: pTd(8), - }, -}); - -const transferGroupStyle = StyleSheet.create({ - tokenCount: { - marginTop: pTd(4), - fontSize: pTd(28), - textAlign: 'center', - width: pTd(335), - }, - tokenUSD: { - color: defaultColors.font3, - textAlign: 'center', - marginTop: pTd(4), - width: pTd(335), - }, - group: { - backgroundColor: defaultColors.bg1, - marginTop: pTd(24), - borderRadius: pTd(6), - }, - tokenNum: { - textAlign: 'right', - color: defaultColors.font5, - }, - usdtNum: { - marginLeft: pTd(6), - marginTop: pTd(4), - color: defaultColors.font3, - textAlign: 'right', - }, - notELFWrap: { - height: pTd(84), - alignItems: 'flex-start', - paddingTop: pTd(18), - paddingBottom: pTd(18), - }, - totalWithUSD: { - marginTop: pTd(12), - display: 'flex', - justifyContent: 'flex-end', - flexDirection: 'row', - }, - flexSpaceBetween: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - width: '100%', - lineHeight: pTd(20), - }, - divider: { - marginTop: pTd(24), - width: pTd(335), - height: StyleSheet.hairlineWidth, - backgroundColor: defaultColors.border6, - }, - card: { - marginTop: pTd(24), - width: pTd(335), - borderRadius: pTd(6), - borderWidth: StyleSheet.hairlineWidth, - borderColor: defaultColors.border1, - }, - section: { - ...GStyles.paddingArg(16, 12), - }, - lightGrayFontColor: { - color: defaultColors.font3, - }, - blackFontColor: { - color: defaultColors.font5, - }, - fontBold: { - ...fonts.mediumFont, - }, - marginTop0: { - marginTop: 0, - }, -}); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts new file mode 100644 index 0000000000..82488f094d --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts @@ -0,0 +1,115 @@ +import { screenHeight } from '@portkey-wallet/utils/mobile/device'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import fonts from 'assets/theme/fonts'; +import { StyleSheet } from 'react-native'; +import { pTd } from 'utils/unit'; + +export const styles = StyleSheet.create({ + contentWrap: { + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + + method: { + overflow: 'hidden', + borderRadius: pTd(6), + marginTop: pTd(24), + textAlign: 'center', + color: defaultColors.primaryColor, + backgroundColor: defaultColors.bg9, + ...GStyles.paddingArg(2, 8), + }, + transactionDataSection: { + marginTop: pTd(16), + }, + scrollSection: { + height: screenHeight / 2, + }, + blank: { + height: pTd(200), + }, + error: { + color: defaultColors.error, + textAlign: 'left', + marginTop: pTd(8), + }, +}); + +export const transferGroupStyle = StyleSheet.create({ + tokenCount: { + marginTop: pTd(4), + fontSize: pTd(28), + textAlign: 'center', + width: pTd(335), + }, + tokenUSD: { + color: defaultColors.font3, + textAlign: 'center', + marginTop: pTd(4), + width: pTd(335), + }, + group: { + backgroundColor: defaultColors.bg1, + marginTop: pTd(24), + borderRadius: pTd(6), + }, + tokenNum: { + textAlign: 'right', + color: defaultColors.font5, + }, + usdtNum: { + marginLeft: pTd(6), + marginTop: pTd(4), + color: defaultColors.font3, + textAlign: 'right', + }, + notELFWrap: { + height: pTd(84), + alignItems: 'flex-start', + paddingTop: pTd(18), + paddingBottom: pTd(18), + }, + totalWithUSD: { + marginTop: pTd(12), + display: 'flex', + justifyContent: 'flex-end', + flexDirection: 'row', + }, + flexSpaceBetween: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + width: '100%', + lineHeight: pTd(20), + }, + divider: { + marginTop: pTd(24), + width: pTd(335), + height: StyleSheet.hairlineWidth, + backgroundColor: defaultColors.border6, + }, + card: { + marginTop: pTd(24), + width: pTd(335), + borderRadius: pTd(6), + borderWidth: StyleSheet.hairlineWidth, + borderColor: defaultColors.border1, + }, + section: { + ...GStyles.paddingArg(16, 12), + }, + lightGrayFontColor: { + color: defaultColors.font3, + }, + blackFontColor: { + color: defaultColors.font5, + }, + fontBold: { + ...fonts.mediumFont, + }, + marginTop0: { + marginTop: 0, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/CommonSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/CommonSection/index.tsx deleted file mode 100644 index 15a3a69dd4..0000000000 --- a/packages/mobile-app-did/js/pages/Discover/components/CommonSection/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import fonts from 'assets/theme/fonts'; -import GStyles from 'assets/theme/GStyles'; -import { FontStyles } from 'assets/theme/styles'; -import { TextL, TextS } from 'components/CommonText'; -import { useLanguage } from 'i18n/hooks'; -import React from 'react'; -import { StyleSheet, View, ScrollView, ScrollViewProps } from 'react-native'; -import { pTd } from 'utils/unit'; - -export type CommonSectionPropsType = { - headerTitle: string; - dataList: any[]; - sectionWrapStyles?: any; - renderItem: (item: any) => React.ReactNode; - clearCallback?: () => void; -} & ScrollViewProps; - -export function CommonSection(props: CommonSectionPropsType) { - const { headerTitle, dataList, clearCallback, renderItem, sectionWrapStyles = {} } = props; - - const { t } = useLanguage(); - - return ( - - - {headerTitle} - - {!!clearCallback && t('Clear')} - - - {[{}]?.map(item => renderItem(item))} - - ); -} - -const styles = StyleSheet.create({ - sectionWrap: { - ...GStyles.paddingArg(24, 20), - backgroundColor: 'red', - }, - headerWrap: { - height: pTd(22), - }, - header: { - ...fonts.mediumFont, - lineHeight: pTd(24), - }, -}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 398addc565..411997caae 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -1,9 +1,7 @@ import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; - import { defaultColors } from 'assets/theme'; -import fonts from 'assets/theme/fonts'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { TextM, TextS } from 'components/CommonText'; diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx index 55e19bb172..eeb4c93ed3 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx @@ -7,12 +7,12 @@ import { pTd } from 'utils/unit'; import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; -interface TokenListItemType { +type RecordListItemType = { item: IRecordsItemType; onPress?: () => void; -} +}; -const RecordItem: React.FC = props => { +const RecordItem: React.FC = props => { const { item, onPress } = props; return ( diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx index 6d4e0df40a..bf3a635002 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { StyleSheet, View, Text, ScrollView } from 'react-native'; +import React, { useCallback, useMemo } from 'react'; +import { StyleSheet, View, ScrollView } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { useLanguage } from 'i18n/hooks'; diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx index fd43ca7e2e..88b0a50f6f 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -12,11 +12,11 @@ import { defaultColors } from 'assets/theme'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; -interface SearchDiscoverSectionProps { +interface ISearchDiscoverSectionProps { searchedDiscoverList: DiscoverItem[]; } -export default function SearchDiscoverSection(props: SearchDiscoverSectionProps) { +export default function SearchDiscoverSection(props: ISearchDiscoverSectionProps) { const { t } = useLanguage(); const { searchedDiscoverList } = props; const { s3Url } = useCurrentNetworkInfo(); diff --git a/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx index 8573a6c121..2d4c0fe98e 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx @@ -7,11 +7,11 @@ import React from 'react'; import { StyleSheet, View, TouchableWithoutFeedback } from 'react-native'; import { pTd } from 'utils/unit'; -interface SimulatedInputBoxProps { - onClickInput: () => any; +interface ISimulatedInputBoxProps { + onClickInput: () => void; } -export default function SimulatedInputBox({ onClickInput }: SimulatedInputBoxProps) { +export default function SimulatedInputBox({ onClickInput }: ISimulatedInputBoxProps) { return ( From 0579805e18271a1e1d1e7e59a5f645da5d94be24 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 15:13:20 +0800 Subject: [PATCH 153/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20close=20and=20o?= =?UTF-8?q?pen=20func=20back?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx index 7093eece7d..d4953cfe05 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -16,16 +16,21 @@ export default function TabsDrawer(props: TabsDrawerPropsType) { const tabsDrawerContent = React.useMemo(() => , []); - const otherProps = {} as { onClose: () => void; onOpen: () => void }; - return ( { + // if no close, the drawer will crash + console.log('close'); + }} + onOpen={() => { + // if no onOpen, the drawer will crash + console.log('open'); + }} swipeEnabled={false} drawerPosition="right" drawerStyle={{ width: ScreenWidth }} - renderDrawerContent={() => tabsDrawerContent} - {...otherProps}> + renderDrawerContent={() => tabsDrawerContent}> {children} ); From c1fef0c03770aa42575d33aff0243215cedc7871 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 15:15:16 +0800 Subject: [PATCH 154/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20delete=20log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-eoa/useBalances.ts | 2 -- packages/mobile-app-did/js/dapp/dappMobileOperator.ts | 3 --- 2 files changed, 5 deletions(-) diff --git a/packages/hooks/hooks-eoa/useBalances.ts b/packages/hooks/hooks-eoa/useBalances.ts index 4ff84595c6..4344fd1a58 100644 --- a/packages/hooks/hooks-eoa/useBalances.ts +++ b/packages/hooks/hooks-eoa/useBalances.ts @@ -24,8 +24,6 @@ const useBalances = ({ tokens, tokenAddress, rpcUrl, delay = 10000 }: useBalance const { currentAccount } = useAppEOASelector(state => state.wallet); const { currentChain } = useAppEOASelector(state => state.chain); - console.log('xxxx', currentAccount, wallet1); - const getTokenContract = useCallback(async () => { if (!rpcUrl) return; const aelf = new AElf(new AElf.providers.HttpProvider(rpcUrl)); diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index cfc1c37c62..f2ee47bc6c 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -147,8 +147,6 @@ export default class DappMobileOperator extends Operator { }; protected handleRequestAccounts: SendRequest = async (eventName, params) => { - console.log('===========params=========================', params); - await this.dappManager.addDapp(params); return generateNormalResponse({ eventName, @@ -248,7 +246,6 @@ export default class DappMobileOperator extends Operator { }) { // user confirm const response = await this.userConfirmation({ eventName, method, params }); - console.log('===response=================================', response); if (response) return response; return callBack(eventName, params); } From 21493dbe55cc122772a97d4ce355d99eb16858a0 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 15 Jun 2023 17:10:48 +0800 Subject: [PATCH 155/893] =?UTF-8?q?perf:=20=E2=9A=A1=EF=B8=8F=20code=20rev?= =?UTF-8?q?iew?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/pages/Buy/components/BuyFrom/index.tsx | 4 ++-- .../pages/Buy/components/SuffixSelect/index.tsx | 17 +++++++++++++---- .../app/web/pages/Buy/hooks/useHandleAchSell.ts | 6 +++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx index 479f9ee257..c584f4a4c6 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx @@ -44,7 +44,7 @@ export default function BuyFrom({ readOnly={false} onKeyDown={handleCurrencyKeyDown} curFiat={curFiat} - onSelect={(v) => handleCurrencySelect(v)} + onSelect={handleCurrencySelect} /> {!!errMsg &&
    {errMsg}
    }
    @@ -57,7 +57,7 @@ export default function BuyFrom({ readOnly={true} onKeyDown={handleTokenKeyDown} curToken={curToken} - onSelect={(v) => handleTokenSelect(v)} + onSelect={handleTokenSelect} />
    diff --git a/packages/web-extension-did/app/web/pages/Buy/components/SuffixSelect/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/SuffixSelect/index.tsx index 6a9ce45f42..4f26da3ce3 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/SuffixSelect/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/SuffixSelect/index.tsx @@ -2,6 +2,7 @@ import { DrawerType, PartialFiatType } from 'pages/Buy/const'; import { useCommonState } from 'store/Provider/hooks'; import CustomModal from '../../components/CustomModal'; import CustomDrawer from '../../components/CustomDrawer'; +import { useMemo } from 'react'; interface ISuffixSelectProps { drawerType: DrawerType; @@ -10,10 +11,18 @@ interface ISuffixSelectProps { onSelect: (v: PartialFiatType) => void; } +const SelectCrypto = 'Select Crypto'; +const SelectCurrency = 'Select Currency'; +const SearchCrypto = 'Search crypto'; +const SearchCurrency = 'Search currency'; + export default function SuffixSelect({ drawerType, open, onClose, onSelect }: ISuffixSelectProps) { const { isPrompt } = useCommonState(); - const title = drawerType === DrawerType.token ? 'Select Crypto' : 'Select Currency'; - const searchPlaceHolder = drawerType === DrawerType.token ? 'Search crypto' : 'Search currency'; + const title = useMemo(() => (drawerType === DrawerType.token ? SelectCrypto : SelectCurrency), [drawerType]); + const searchPlaceHolder = useMemo( + () => (drawerType === DrawerType.token ? SearchCrypto : SearchCurrency), + [drawerType], + ); return isPrompt ? ( onSelect(v)} + onChange={onSelect} /> ) : ( onSelect(v)} + onChange={onSelect} /> ); } diff --git a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts index 323962096e..4a01cc369f 100644 --- a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts +++ b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts @@ -29,14 +29,14 @@ export const useHandleAchSell = () => { const paymentSellTransfer = useCallback( async (params: AchTxAddressReceivedType) => { - if (!chainInfo) throw new Error(''); + if (!chainInfo) throw new Error('Sell Transfer: No ChainInfo'); const getSeedResult = await InternalMessage.payload(InternalMessageTypes.GET_SEED).send(); const pin = getSeedResult.data.privateKey; const privateKey = await aes.decrypt(wallet.AESEncryptPrivateKey, pin); - if (!privateKey) throw new Error(''); + if (!privateKey) throw new Error('Sell Transfer: No PrivateKey'); - if (!aelfToken) throw new Error(''); + if (!aelfToken) throw new Error('Sell Transfer: No Token'); const transferParams = { chainInfo, From cfd0a965139b2de280c9a46dea5008874e280a79 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 15 Jun 2023 17:51:40 +0800 Subject: [PATCH 156/893] =?UTF-8?q?perf:=20=E2=9A=A1=EF=B8=8F=20code=20rev?= =?UTF-8?q?iew?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewOnWebView/hooks/useHandleAchSell.ts | 2 +- .../Buy/BuyHome/components/BuyForm/index.tsx | 10 +++++----- .../Buy/BuyHome/components/SellForm/index.tsx | 10 +++++----- .../js/pages/Buy/BuyHome/index.tsx | 11 +++++------ .../js/pages/Buy/BuyPreview/index.tsx | 17 +++++++++-------- packages/mobile-app-did/js/pages/Buy/hooks.tsx | 18 +++++++++--------- packages/mobile-app-did/js/pages/Buy/types.ts | 10 ---------- 7 files changed, 34 insertions(+), 44 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index 6135811658..e01db6e706 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -40,7 +40,7 @@ export const useHandleAchSell = () => { !aelfToken || !caHash ) { - throw new Error(''); + throw new Error('Incorrect preposition parameter.'); } const account = getManagerAccount(pin); diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx index 68524174d1..20e043b416 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx @@ -21,13 +21,14 @@ import { getCryptoList } from '@portkey-wallet/api/api-did/payment/util'; import { ErrorType } from 'types/common'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import { CryptoInfoType } from '@portkey-wallet/api/api-did/payment/type'; -import { CryptoItemType, LimitType, TypeEnum } from 'pages/Buy/types'; +import { CryptoItemType } from 'pages/Buy/types'; import { INIT_BUY_AMOUNT, tokenList } from 'pages/Buy/constants'; import Loading from 'components/Loading'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; import { useReceive } from 'pages/Buy/hooks'; import BigNumber from 'bignumber.js'; import { ZERO } from '@portkey-wallet/constants/misc'; +import { PaymentLimitType, PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; export default function BuyForm() { const { buyFiatList: fiatList } = usePayment(); @@ -39,7 +40,7 @@ export default function BuyForm() { const [amount, setAmount] = useState(INIT_BUY_AMOUNT); const [amountLocalError, setAmountLocalError] = useState(INIT_NONE_ERROR); - const limitAmountRef = useRef(); + const limitAmountRef = useRef(); const cryptoListRef = useRef(); const isRefreshReceiveValid = useRef(false); const cryptoListCurrency = useRef(); @@ -88,7 +89,7 @@ export default function BuyForm() { refreshReceive, amountError: amountFetchError, isAllowAmount, - } = useReceive(TypeEnum.BUY, amount, fiat, token, '', '', limitAmountRef, isRefreshReceiveValid); + } = useReceive(PaymentTypeEnum.BUY, amount, fiat, token, '', '', limitAmountRef, isRefreshReceiveValid); const refreshReceiveRef = useRef(); refreshReceiveRef.current = refreshReceive; @@ -148,7 +149,7 @@ export default function BuyForm() { amount, fiat, token, - type: TypeEnum.BUY, + type: PaymentTypeEnum.BUY, receiveAmount: _receiveAmount, rate: _rate, }); @@ -183,7 +184,6 @@ export default function BuyForm() { keyboardType="decimal-pad" onChangeText={onAmountInput} errorMessage={amountError.isError ? amountError.errorMsg : ''} - // placeholder={t('Enter Phone Number')} /> (); + const limitAmountRef = useRef(); const cryptoListRef = useRef(); const isRefreshReceiveValid = useRef(false); const cryptoListCurrency = useRef(); @@ -104,7 +105,7 @@ export default function SellForm() { refreshReceive, amountError: amountFetchError, isAllowAmount, - } = useReceive(TypeEnum.SELL, amount, fiat, token, '', '', limitAmountRef, isRefreshReceiveValid); + } = useReceive(PaymentTypeEnum.SELL, amount, fiat, token, '', '', limitAmountRef, isRefreshReceiveValid); const refreshReceiveRef = useRef(); refreshReceiveRef.current = refreshReceive; @@ -200,7 +201,7 @@ export default function SellForm() { amount, fiat, token, - type: TypeEnum.SELL, + type: PaymentTypeEnum.SELL, receiveAmount: _receiveAmount, rate: _rate, }); @@ -235,7 +236,6 @@ export default function SellForm() { keyboardType="decimal-pad" onChangeText={onAmountInput} errorMessage={amountError.isError ? amountError.errorMsg : ''} - // placeholder={t('Enter Phone Number')} /> , }, { name: 'Sell', - type: TypeEnum.SELL, + type: PaymentTypeEnum.SELL, component: , }, ]; export default function BuyHome() { const { t } = useLanguage(); - const [selectTab, setSelectTab] = useState(TypeEnum.BUY); + const [selectTab, setSelectTab] = useState(PaymentTypeEnum.BUY); return ( type === TypeEnum.BUY, [type]); + const isBuy = useMemo(() => type === PaymentTypeEnum.BUY, [type]); const apiUrl = useCurrentApiUrl(); const wallet = useCurrentWalletInfo(); const { buyConfig } = useCurrentNetworkInfo(); @@ -66,12 +67,12 @@ export default function BuyPreview() { let achUrl = `${baseUrl}/?crypto=${token.crypto}&network=${token.network}&country=${fiat.country}&fiat=${fiat.currency}&appId=${appId}&callbackUrl=${callbackUrl}`; const orderNo = await getPaymentOrderNo({ - transDirect: type === TypeEnum.BUY ? TransDirectEnum.TOKEN_BUY : TransDirectEnum.TOKEN_SELL, + transDirect: type === PaymentTypeEnum.BUY ? TransDirectEnum.TOKEN_BUY : TransDirectEnum.TOKEN_SELL, merchantName: ACH_MERCHANT_NAME, }); achUrl += `&merchantOrderNo=${orderNo}`; - if (type === TypeEnum.BUY) { + if (type === PaymentTypeEnum.BUY) { const achTokenInfo = await getAchTokenInfo(); if (achTokenInfo !== undefined && isNoEmail === false) { achUrl += `&token=${encodeURIComponent(achTokenInfo.token)}`; @@ -106,10 +107,10 @@ export default function BuyPreview() { navigationService.navigate('ViewOnWebView', { title: 'Alchemy Pay Ramp', url: achUrl, - webViewPageType: type === TypeEnum.BUY ? 'ach' : 'achSell', + webViewPageType: type === PaymentTypeEnum.BUY ? 'ach' : 'achSell', injectedJavaScript, params: - type === TypeEnum.BUY + type === PaymentTypeEnum.BUY ? undefined : { orderNo, diff --git a/packages/mobile-app-did/js/pages/Buy/hooks.tsx b/packages/mobile-app-did/js/pages/Buy/hooks.tsx index e2d6a2e101..f8bf76b0c2 100644 --- a/packages/mobile-app-did/js/pages/Buy/hooks.tsx +++ b/packages/mobile-app-did/js/pages/Buy/hooks.tsx @@ -1,24 +1,24 @@ import { FiatType } from '@portkey-wallet/store/store-ca/payment/type'; -import { CryptoItemType, LimitType, TypeEnum } from './types'; +import { CryptoItemType } from './types'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { MAX_REFRESH_TIME } from './constants'; import useEffectOnce from 'hooks/useEffectOnce'; import { GetOrderQuoteParamsType, getOrderQuote } from '@portkey-wallet/api/api-did/payment/util'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; -import CommonToast from 'components/CommonToast'; import { ErrorType } from 'types/common'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import isEqual from 'lodash/isEqual'; import { ZERO } from '@portkey-wallet/constants/misc'; +import { PaymentLimitType, PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; export const useReceive = ( - type: TypeEnum, + type: PaymentTypeEnum, amount: string, fiat?: FiatType, token?: CryptoItemType, initialReceiveAmount = '', initialRate = '', - limitAmountRef?: React.MutableRefObject, + limitAmountRef?: React.MutableRefObject, isRefreshReceiveValid?: React.MutableRefObject, ) => { const [receiveAmount, setReceiveAmount] = useState(initialReceiveAmount); @@ -90,7 +90,7 @@ export const useReceive = ( setAmountError({ ...INIT_HAS_ERROR, errorMsg: `Limit Amount ${formatAmountShow(min, 4)}-${formatAmountShow(max, 4)} ${ - type === TypeEnum.BUY ? fiat?.currency : token.crypto + type === PaymentTypeEnum.BUY ? fiat?.currency : token.crypto }`, }); setRate(''); @@ -108,7 +108,7 @@ export const useReceive = ( fiat: fiat.currency, country: fiat.country, amount, - side: type === TypeEnum.BUY ? 'BUY' : 'SELL', + side: type === PaymentTypeEnum.BUY ? 'BUY' : 'SELL', }; lastParams.current = params; @@ -123,8 +123,8 @@ export const useReceive = ( if ( !rst || !rst.cryptoPrice || - (type === TypeEnum.BUY && !rst.cryptoQuantity) || - (type === TypeEnum.SELL && !rst.fiatQuantity) + (type === PaymentTypeEnum.BUY && !rst.cryptoQuantity) || + (type === PaymentTypeEnum.SELL && !rst.fiatQuantity) ) { setRate(''); setReceiveAmount(''); @@ -134,7 +134,7 @@ export const useReceive = ( if (isRefreshReceiveValid) isRefreshReceiveValid.current = true; const _rate = Number(rst.cryptoPrice).toFixed(2) + ''; let _receiveAmount = ''; - if (type === TypeEnum.BUY) { + if (type === PaymentTypeEnum.BUY) { _receiveAmount = formatAmountShow(rst.cryptoQuantity || '', 4); } else { const fiatQuantity = ZERO.plus(rst.fiatQuantity || 0).minus(rst.rampFee || 0); diff --git a/packages/mobile-app-did/js/pages/Buy/types.ts b/packages/mobile-app-did/js/pages/Buy/types.ts index 79dd488abe..aa22922307 100644 --- a/packages/mobile-app-did/js/pages/Buy/types.ts +++ b/packages/mobile-app-did/js/pages/Buy/types.ts @@ -1,13 +1,3 @@ -export enum TypeEnum { - BUY = 'BUY', - SELL = 'SELL', -} - -export interface LimitType { - min: number; - max: number; -} - export interface CryptoItemType { crypto: string; network: string; From 35b4d24a16838469de3b706a6c4a2f3662ec0534 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 15 Jun 2023 17:51:14 +0800 Subject: [PATCH 157/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remove=20tab=20&?= =?UTF-8?q?=20CR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/controllers/openNewTabController/index.ts | 7 +++---- packages/web-extension-did/app/web/hooks/useConnect.ts | 2 +- .../web-extension-did/app/web/utils/clearOpenTabs.ts | 9 +++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts b/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts index 6fb6cee878..396bb65bf9 100644 --- a/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts +++ b/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts @@ -6,10 +6,9 @@ export default class OpenNewTabController { this.openNewTab(); } openNewTab() { - const createdListener = async (tab: chrome.tabs.Tab) => { - const extId = apis.runtime.id; - if (tab.id && extId && (tab.id + '').includes(extId)) { - saveOpenTabs(tab.id); + const createdListener = (tab: chrome.tabs.Tab) => { + if (tab.id) { + saveOpenTabs(tab.id + ''); } }; diff --git a/packages/web-extension-did/app/web/hooks/useConnect.ts b/packages/web-extension-did/app/web/hooks/useConnect.ts index 63354554c8..31e8a736f2 100644 --- a/packages/web-extension-did/app/web/hooks/useConnect.ts +++ b/packages/web-extension-did/app/web/hooks/useConnect.ts @@ -17,7 +17,7 @@ export default function useConnect() { const url = currentTab?.url; if (!url) return; const origin = new URL(url).origin; - return origin ? currentDapps.find((dapp) => dapp.origin === origin) : undefined; + return currentDapps.find((dapp) => dapp.origin === origin); }, [currentDapps, currentTab?.url]); useEffect(() => { diff --git a/packages/web-extension-did/app/web/utils/clearOpenTabs.ts b/packages/web-extension-did/app/web/utils/clearOpenTabs.ts index c67daf14d2..6d503b3913 100644 --- a/packages/web-extension-did/app/web/utils/clearOpenTabs.ts +++ b/packages/web-extension-did/app/web/utils/clearOpenTabs.ts @@ -1,3 +1,4 @@ +import { apis } from './BrowserApis'; import { getLocalStorage, setLocalStorage } from './storage/chromeStorage'; export default async function closeOpenTabs(keepCurTabAlive = false) { @@ -12,11 +13,15 @@ export default async function closeOpenTabs(keepCurTabAlive = false) { } if (openTabs) { - openTabs.forEach((tab: string) => { + openTabs.forEach(async (tab: string) => { if (keepCurTabAlive) { if (Number(tab) === curTabId) return; } - chrome.tabs.remove(Number(tab)); + const extId = apis.runtime.id; + const t = await apis.tabs.get(+tab); + if (t.pendingUrl?.includes(extId)) { + chrome.tabs.remove(Number(tab)); + } }); const openTabsId = keepCurTabAlive ? [curTabId] : []; setLocalStorage({ openTabsId }); From 460b78d43c53c0cd0f70c9095173937d0e69b493 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:16:09 +0800 Subject: [PATCH 158/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/discover/slice.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index ddab01612d..6da391fae0 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -103,7 +103,6 @@ export const discoverSlice = createSlice({ const { networkType, id } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - console.log('updateTab', payload); targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.map(item => item.id === id ? { ...item, ...payload } : item, ); From 1015647277fd301fbe27c11378ced1cc593abf4d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:19:14 +0800 Subject: [PATCH 159/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20timer=20?= =?UTF-8?q?define?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/PhoneInput/index.tsx | 9 +++++---- .../js/pages/DashBoard/TokenSection/index.tsx | 13 +++++++------ .../js/pages/Login/components/Email.tsx | 9 +++++---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/mobile-app-did/js/components/PhoneInput/index.tsx b/packages/mobile-app-did/js/components/PhoneInput/index.tsx index bbc1e1378e..d728b26be8 100644 --- a/packages/mobile-app-did/js/components/PhoneInput/index.tsx +++ b/packages/mobile-app-did/js/components/PhoneInput/index.tsx @@ -14,8 +14,6 @@ import Svg from 'components/Svg'; import { defaultColors } from 'assets/theme'; import { TextM } from 'components/CommonText'; -let timer: string | number | NodeJS.Timeout | undefined; - interface PhoneInputProps extends CommonInputProps { selectCountry?: CountryItem; onCountryChange?: (country: CountryItem) => void; @@ -24,6 +22,7 @@ interface PhoneInputProps extends CommonInputProps { export default function PhoneInput({ selectCountry, onCountryChange, ...inputProps }: PhoneInputProps) { const { t } = useLanguage(); + const timer = useRef(null); const iptRef = useRef(); useEffectOnce(() => { @@ -38,14 +37,16 @@ export default function PhoneInput({ selectCountry, onCountryChange, ...inputPro useFocusEffect( useCallback(() => { if (!iptRef || !iptRef?.current) return; - timer = setTimeout(() => { + timer.current = setTimeout(() => { iptRef.current.focus(); }, 200); }, []), ); useEffect(() => { - return () => clearTimeout(timer); + return () => { + if (timer.current) clearTimeout(timer.current); + }; }, []); return ( diff --git a/packages/mobile-app-did/js/pages/DashBoard/TokenSection/index.tsx b/packages/mobile-app-did/js/pages/DashBoard/TokenSection/index.tsx index 2e8e23eb90..3cec223f86 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/TokenSection/index.tsx +++ b/packages/mobile-app-did/js/pages/DashBoard/TokenSection/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect, useRef } from 'react'; import { StyleSheet } from 'react-native'; import navigationService from 'utils/navigationService'; import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks/index'; @@ -15,8 +15,6 @@ import { fetchTokenListAsync } from '@portkey-wallet/store/store-ca/assets/slice import { REFRESH_TIME } from '@portkey-wallet/constants/constants-ca/assets'; import { useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; -let timer2: string | number | NodeJS.Timer | undefined; - export interface TokenSectionProps { getAccountBalance?: () => void; } @@ -33,6 +31,7 @@ export default function TokenSection({ getAccountBalance }: TokenSectionProps) { } = useCurrentWallet(); const [, getTokenPrice] = useGetCurrentAccountTokenPrice(); const [isFetching] = useState(false); + const timerRef = useRef(null); const onNavigate = useCallback((tokenItem: TokenItemShowType) => { navigationService.navigate('TokenDetail', { tokenInfo: tokenItem }); @@ -56,11 +55,13 @@ export default function TokenSection({ getAccountBalance }: TokenSectionProps) { }, [caAddressList, getAccountTokenList]); useEffect(() => { - if (timer2) clearInterval(timer2); - timer2 = setInterval(() => { + if (timerRef.current) clearInterval(timerRef.current); + timerRef.current = setInterval(() => { getAccountTokenList(); }, REFRESH_TIME); - return () => clearInterval(timer2); + return () => { + if (timerRef.current) clearInterval(timerRef.current); + }; }, [getAccountTokenList]); return ( diff --git a/packages/mobile-app-did/js/pages/Login/components/Email.tsx b/packages/mobile-app-did/js/pages/Login/components/Email.tsx index 5bd87aa9ff..025efb8ff2 100644 --- a/packages/mobile-app-did/js/pages/Login/components/Email.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/Email.tsx @@ -17,8 +17,6 @@ import TermsServiceButton from './TermsServiceButton'; import Button from './Button'; import { useFocusEffect } from '@react-navigation/native'; -let timer: string | number | NodeJS.Timeout | undefined; - const TitleMap = { [PageType.login]: { button: 'Log In', @@ -38,6 +36,7 @@ export default function Email({ const { t } = useLanguage(); const iptRef = useRef(); + const timerRef = useRef(null); const [loading] = useState(); const [loginAccount, setLoginAccount] = useState(); const [errorMessage, setErrorMessage] = useState(); @@ -66,14 +65,16 @@ export default function Email({ useFocusEffect( useCallback(() => { if (!iptRef || !iptRef?.current) return; - timer = setTimeout(() => { + timerRef.current = setTimeout(() => { iptRef.current.focus(); }, 200); }, []), ); useEffect(() => { - return () => clearTimeout(timer); + return () => { + if (timerRef.current) clearTimeout(timerRef.current); + }; }, []); return ( From 4b15cbb3c9a4c6027161b5ce9d30f9d069632b79 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:20:58 +0800 Subject: [PATCH 160/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20cr=20change?= =?UTF-8?q?=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/App.tsx | 2 -- .../TabsDrawer/TabsDrawerContent.tsx | 2 +- .../dapp/components/ConnectOverlay/index.tsx | 24 +++++++++---------- .../TransactionDataSection/index.tsx | 2 +- .../pages/Discover/DiscoverSearch/index.tsx | 13 +++++----- .../components/DiscoverWebsiteImage/index.tsx | 12 ++++------ 6 files changed, 25 insertions(+), 30 deletions(-) diff --git a/packages/mobile-app-did/App.tsx b/packages/mobile-app-did/App.tsx index 46373a30bd..2a082cc8ac 100644 --- a/packages/mobile-app-did/App.tsx +++ b/packages/mobile-app-did/App.tsx @@ -1,5 +1,3 @@ -import 'react-native-gesture-handler'; - import React, { useEffect } from 'react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { StatusBar, StatusBarProps } from 'react-native'; diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 5dbbea652d..3a86a30e7b 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -116,8 +116,8 @@ const TabsDrawerContent: React.FC = () => { } leftCallback={backToSearchPage} notHandleHardwareBackPress safeAreaColor={['blue', 'white']} diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index d0dfc8d271..98cf048076 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -39,18 +39,18 @@ const ConnectModal = (props: ConnectModalType) => { } = useAppCASelector(state => state.assets); const caInfoList = useMemo(() => { - return Object.entries(caInfo || {}) - .map(([key, value]) => { - const info = value as CAInfo; - return info?.caAddress - ? { - chaiId: key, - caAddress: info.caAddress, - ...accountTokenList.find(token => token.chainId === key && token.symbol === ELF_SYMBOL), - } - : undefined; - }) - .filter(item => !!item); + const list: any[] = []; + Object.entries(caInfo || {}).map(([key, value]) => { + const info = value as CAInfo; + if (info?.caAddress) { + list.push({ + chaiId: key, + caAddress: info.caAddress, + ...accountTokenList.find(token => token.chainId === key && token.symbol === ELF_SYMBOL), + }); + } + }); + return list; }, [accountTokenList, caInfo]); const buttonList = useMemo( diff --git a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx index 33ea225f2a..27b16182df 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx @@ -36,7 +36,7 @@ export const TransactionDataSection = (props: TransactionDataSectionType) => { Object.entries(dataInfo).map(([key, value], index) => ( {key} - {value} + {JSON.stringify(value)} ))} diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 6f3f71fdc6..687dabd3a4 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -19,14 +19,13 @@ import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; -let timer: any = null; - export default function DiscoverSearch() { const { t } = useLanguage(); const discoverGroupList = useDiscoverGroupList(); const jumpToWebview = useDiscoverJumpWithNetWork(); + const timerRef = useRef(null); const iptRef = useRef(); const [value, setValue] = useState(''); const [showRecord, setShowRecord] = useState(true); @@ -54,7 +53,7 @@ export default function DiscoverSearch() { }, [value]); const onSearch = useCallback(() => { - const newValue = value.trim().replace(' ', ''); + const newValue = value.replace(/\s+/g, ''); if (!newValue) return; if (checkIsUrl(newValue)) { @@ -67,7 +66,7 @@ export default function DiscoverSearch() { }); } else { // else search in Discover list - const filterList = flatList.filter(item => item.title.replace(' ', '').includes(newValue)); + const filterList = flatList.filter(item => item.title.replace(/\s+/g, '').includes(newValue)); setFilteredDiscoverList(filterList); setShowRecord(false); } @@ -76,7 +75,7 @@ export default function DiscoverSearch() { useFocusEffect( useCallback(() => { if (iptRef?.current && !isIOS) { - timer = setTimeout(() => { + timerRef.current = setTimeout(() => { iptRef.current.focus(); }, 300); } @@ -84,7 +83,9 @@ export default function DiscoverSearch() { ); useEffect(() => { - return () => clearTimeout(timer); + return () => { + if (timerRef.current) clearTimeout(timerRef.current); + }; }, []); return ( diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx index 437bd1ae16..0b55acdb9b 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx @@ -1,9 +1,10 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import Svg from 'components/Svg'; import { pTd } from 'utils/unit'; import { StyleSheet } from 'react-native'; import { defaultColors } from 'assets/theme'; import { Image } from 'react-native'; +import Default_Image from 'assets/image/pngs/default_record.png'; interface DiscoverWebsiteImageProps { imageUrl?: string; @@ -20,17 +21,12 @@ export default function DiscoverWebsiteImage(props: DiscoverWebsiteImageProps) { borderRadius: size, }; - const [isError, setError] = React.useState(false); - - if (isError) return ; return ( { - setError(true); - }} + defaultSource={Default_Image} /> ); } From 08046621b08dcf5b3a92a8dc806dfd67dd12da45 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:21:23 +0800 Subject: [PATCH 161/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20ui=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/components/Card/index.tsx | 9 +++++---- .../dapp/components/TransactionOverlay/styles/index.ts | 4 +++- packages/mobile-app-did/js/navigation/Tab.tsx | 2 +- .../js/pages/Discover/DiscoverHome/index.tsx | 6 ++---- .../Discover/components/DiscoverCmsListSection/index.tsx | 6 +++--- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx index 2654ebc657..928aa9616f 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/Card/index.tsx @@ -29,7 +29,7 @@ const Card: React.FC = (props: ICardsProps) => { - {item?.name ?? getHost(item?.url)} + {item?.name || getHost(item?.url)} dispatch(closeExistingTab({ id: item?.id, networkType }))}> @@ -52,15 +52,14 @@ export default Card; const tabShowItemStyle = StyleSheet.create({ cardWrap: { - overflow: 'hidden', borderRadius: pTd(8), width: pTd(160), height: pTd(214), marginTop: pTd(24), - shadowOffset: { width: 2, height: 8 }, + shadowOffset: { width: 2, height: 10 }, backgroundColor: defaultColors.bg1, shadowColor: defaultColors.shadow1, - shadowOpacity: 0.1, + shadowOpacity: 0.15, shadowRadius: 10, elevation: 2, }, @@ -79,5 +78,7 @@ const tabShowItemStyle = StyleSheet.create({ screenshot: { width: pTd(160), height: pTd(182), + overflow: 'hidden', + borderRadius: pTd(6), }, }); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts index 82488f094d..16f1e1d7b5 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts @@ -24,10 +24,12 @@ export const styles = StyleSheet.create({ marginTop: pTd(16), }, scrollSection: { + paddingLeft: pTd(20), + width: '100%', height: screenHeight / 2, }, blank: { - height: pTd(200), + height: pTd(100), }, error: { color: defaultColors.error, diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index 1c37d95921..776a145bf1 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -81,7 +81,7 @@ export default function TabRoot() { header: () => null, tabBarIcon: ({ focused }) => { const iconName: IconName = tabMenuList.find(tab => tab.name === route.name)?.icon ?? 'logo-icon'; - return ; + return ; }, })}> {tabMenuList.map(ele => ( diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 5641ec9814..76db5509ec 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { StyleSheet, View } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import navigationService from 'utils/navigationService'; @@ -10,13 +10,11 @@ import CustomHeader from 'components/CustomHeader'; import { BGStyles } from 'assets/theme/styles'; export default function DiscoverHome() { - const navigateToSearch = useCallback(() => navigationService.navigate('DiscoverSearch'), []); - return ( - + navigationService.navigate('DiscoverSearch')} /> diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 411997caae..4866b209df 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -4,7 +4,7 @@ import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; -import { TextM, TextS } from 'components/CommonText'; +import { TextL, TextM, TextS } from 'components/CommonText'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import React, { useCallback } from 'react'; import { ScrollView, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; @@ -38,9 +38,9 @@ export function DiscoverCmsListSection() { onJump(item)}> - + {item.title} - + {item.description} From 362119a13849f045d4dac60ee2b6bbfbce57d065 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 15 Jun 2023 18:33:43 +0800 Subject: [PATCH 162/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20portkey?= =?UTF-8?q?=20website=20url=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constants/constants-ca/backend-network.ts | 4 ++++ packages/types/types-ca/network.ts | 1 + .../web-extension-did/app/web/constants/index.ts | 8 -------- .../web-extension-did/app/web/utils/index.ts | 11 +++++++++++ .../app/web/utils/lib/serviceWorkerAction.ts | 16 +++++++++++----- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/constants/constants-ca/backend-network.ts b/packages/constants/constants-ca/backend-network.ts index 91bb00b4c3..0e721efce3 100644 --- a/packages/constants/constants-ca/backend-network.ts +++ b/packages/constants/constants-ca/backend-network.ts @@ -16,6 +16,7 @@ export const BackEndNetWorkMap: { tokenClaimContractAddress: '2UM9eusxdRyCztbmMZadGXzwgwKfFdk8pF4ckw58D769ehaPSR', cmsUrl: 'http://192.168.66.62:8055/graphql', s3Url: 'https://portkey-cms-dev.s3.ap-northeast-1.amazonaws.com', + portkeyFinanceUrl: 'https://portkey-website-dev.vercel.app/', }, 'back-end-test2': { name: 'aelf Mainnet', @@ -25,6 +26,7 @@ export const BackEndNetWorkMap: { apiUrl: 'https://localtest-applesign2.portkey.finance', graphqlUrl: 'http://192.168.67.51:8083/AElfIndexer_DApp/PortKeyIndexerCASchema/graphql', connectUrl: 'http://192.168.67.51:8080', + portkeyFinanceUrl: 'https://portkey-website-dev.vercel.app/', buyConfig: { ach: { appId: 'f83Is2y7L425rxl8', @@ -43,6 +45,7 @@ export const BackEndNetWorkMap: { tokenClaimContractAddress: '233wFn5JbyD4i8R5Me4cW4z6edfFGRn5bpWnGuY8fjR7b2kRsD', cmsUrl: 'https://cms-test.portkey.finance/graphql', s3Url: 'https://portkey-cms-testnet.s3.ap-northeast-1.amazonaws.com', + portkeyFinanceUrl: 'https://portkey-website-dev.vercel.app/', }, 'back-end-mainnet': { name: 'aelf Mainnet', @@ -54,6 +57,7 @@ export const BackEndNetWorkMap: { connectUrl: 'https://auth-portkey.portkey.finance', cmsUrl: 'https://cms.portkey.finance/graphql', s3Url: 'https://portkey-cms-mainnet.s3.ap-northeast-1.amazonaws.com', + portkeyFinanceUrl: 'https://portkey.finance', buyConfig: { ach: { appId: 'P0e0l39jipsNYT46', diff --git a/packages/types/types-ca/network.ts b/packages/types/types-ca/network.ts index 13719c014d..0d9b761ee0 100644 --- a/packages/types/types-ca/network.ts +++ b/packages/types/types-ca/network.ts @@ -11,6 +11,7 @@ export type NetworkItem = { tokenClaimContractAddress?: string; cmsUrl?: string; s3Url?: string; + portkeyFinanceUrl?: string; // portkey website url buyConfig?: { ach?: { appId?: string; diff --git a/packages/web-extension-did/app/web/constants/index.ts b/packages/web-extension-did/app/web/constants/index.ts index d1e44be57a..808aa8ccad 100644 --- a/packages/web-extension-did/app/web/constants/index.ts +++ b/packages/web-extension-did/app/web/constants/index.ts @@ -35,13 +35,5 @@ export const DEVICE_TYPE = (() => { // NFT export const PAGE_SIZE_IN_NFT_ITEM_PROMPT = 6; -// JOIN AUTH URL -export const AUTH_HOST = 'https://portkey-website-dev.vercel.app/'; -// export const AUTH_HOST = 'https://portkey.finance'; -// export const AUTH_HOST = 'https://portkey.finance'; -export const JOIN_AUTH_URL = `${AUTH_HOST}/join`; -export const AUTH_APPLE_URL = `${AUTH_HOST}/apple-auth`; -export const RECAPTCHA_URL = `${AUTH_HOST}/recaptcha-check`; - // after ach-sell, redirect url, then wake up extension. export const ACH_WITHDRAW_URL = `https://openlogin-test.portkey.finance/extension-rouse?method=${walletMessage.ACH_SELL_REDIRECT}`; // TODO: change url diff --git a/packages/web-extension-did/app/web/utils/index.ts b/packages/web-extension-did/app/web/utils/index.ts index 2921db74b3..049da4cfb4 100644 --- a/packages/web-extension-did/app/web/utils/index.ts +++ b/packages/web-extension-did/app/web/utils/index.ts @@ -1,3 +1,5 @@ +import { NetworkList } from '@portkey-wallet/constants/constants-ca/network'; +import { NetworkType } from '@portkey/provider-types'; import { ENVIRONMENT_TYPE_POPUP, ENVIRONMENT_TYPE_PROMPT, @@ -53,3 +55,12 @@ export const getEnvironmentType = (url: string) => { // from content js return ENVIRONMENT_TYPE_SERVICE_WORKER; }; + +export const getPortkeyFinanceUrl = (currentNetwork: NetworkType) => { + const host = NetworkList.find((item) => item.networkType === currentNetwork)?.portkeyFinanceUrl || ''; + return { + JOIN_AUTH_URL: `${host}/join`, + AUTH_APPLE_URL: `${host}/apple-auth`, + RECAPTCHA_URL: `${host}/recaptcha-check`, + }; +}; diff --git a/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts b/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts index e0014290b9..a31f83788f 100644 --- a/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts +++ b/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts @@ -1,13 +1,14 @@ import { NetworkType } from '@portkey-wallet/types'; import { ISocialLogin } from '@portkey-wallet/types/types-ca/wallet'; import { message } from 'antd'; -import { JOIN_AUTH_URL, RECAPTCHA_URL } from 'constants/index'; import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import { useCallback } from 'react'; import { CloseParams } from 'service/NotificationService'; import { CreatePromptType, ReCaptchaResponseParams, SendResponseParams } from 'types'; +import { getPortkeyFinanceUrl } from 'utils'; import { setLocalStorage } from 'utils/storage/chromeStorage'; +import { getWalletState } from './SWGetReduxStore'; export const completeRegistration = async () => { await setLocalStorage({ registerStatus: 'Registered' }); @@ -45,12 +46,17 @@ export const setPinAction = async (pin: string) => { await InternalMessage.payload(PortkeyMessageTypes.SET_SEED, pin).send(); }; -export const socialLoginAction = async (type: ISocialLogin, network: NetworkType): Promise => - await InternalMessage.payload(PortkeyMessageTypes.SOCIAL_LOGIN, { +export const socialLoginAction = async (type: ISocialLogin, network: NetworkType): Promise => { + const { JOIN_AUTH_URL } = getPortkeyFinanceUrl(network); + return await InternalMessage.payload(PortkeyMessageTypes.SOCIAL_LOGIN, { externalLink: `${JOIN_AUTH_URL}/${network}/${type}`, }).send(); +}; -export const reCAPTCHAAction = async (): Promise => - await InternalMessage.payload(PortkeyMessageTypes.OPEN_RECAPTCHA_PAGE, { +export const reCAPTCHAAction = async (): Promise => { + const wallet = await getWalletState(); + const { RECAPTCHA_URL } = getPortkeyFinanceUrl(wallet.currentNetwork); + return await InternalMessage.payload(PortkeyMessageTypes.OPEN_RECAPTCHA_PAGE, { externalLink: `${RECAPTCHA_URL}`, }).send(); +}; From 593708d3c4963553061dad0e0f989854f03be81a Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:36:25 +0800 Subject: [PATCH 163/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20data=20show?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TransactionDataSection/index.tsx | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx index 27b16182df..87f2550313 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionDataSection/index.tsx @@ -10,7 +10,7 @@ import GStyles from 'assets/theme/GStyles'; import Touchable from 'components/Touchable'; type TransactionDataSectionType = { - dataInfo: { [key: string]: any }; + dataInfo: { [key: string]: any } | string; style?: ViewStyle; }; @@ -19,7 +19,7 @@ export const TransactionDataSection = (props: TransactionDataSectionType) => { const [collapsed, setCollapsed] = useState(true); - const topSection = useMemo( + const TopSection = useMemo( () => ( setCollapsed(pre => !pre)}> Data @@ -29,16 +29,33 @@ export const TransactionDataSection = (props: TransactionDataSectionType) => { [collapsed], ); + const DataSection = useMemo(() => { + if (typeof dataInfo === 'string') { + return ( + + {dataInfo} + + ); + } else if (typeof dataInfo === 'object') { + return Object.entries(dataInfo).map(([key, value], index) => ( + + {key} + {JSON.stringify(value)} + + )); + } else { + return ( + + {JSON.stringify(dataInfo)} + + ); + } + }, [dataInfo]); + return ( - {topSection} - {!collapsed && - Object.entries(dataInfo).map(([key, value], index) => ( - - {key} - {JSON.stringify(value)} - - ))} + {TopSection} + {!collapsed && DataSection} ); }; From 39250e81e0f4c7eb3abf8fcf6315410ea04f9035 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:37:52 +0800 Subject: [PATCH 164/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20useless?= =?UTF-8?q?=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Discover/components/DiscoverWebsiteImage/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx index 0b55acdb9b..615fdea49d 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverWebsiteImage/index.tsx @@ -1,5 +1,4 @@ -import React, { useMemo } from 'react'; -import Svg from 'components/Svg'; +import React from 'react'; import { pTd } from 'utils/unit'; import { StyleSheet } from 'react-native'; import { defaultColors } from 'assets/theme'; From 9624e0a04db5e45cc700b8ae93d0bd986019bc5b Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Thu, 15 Jun 2023 18:50:36 +0800 Subject: [PATCH 165/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/dapp/dappEventBus.ts | 4 +++- .../js/dapp/dappMobileOperator.ts | 17 +++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/dappEventBus.ts b/packages/mobile-app-did/js/dapp/dappEventBus.ts index a2dc82f311..ef3700f5b4 100644 --- a/packages/mobile-app-did/js/dapp/dappEventBus.ts +++ b/packages/mobile-app-did/js/dapp/dappEventBus.ts @@ -15,6 +15,7 @@ import { changeNetworkType, setCAInfo } from '@portkey-wallet/store/store-ca/wal import { getWallet } from 'utils/redux'; import { handleAccounts, handleChainIds } from '@portkey-wallet/utils/dapp'; import { addDapp, removeDapp, resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { sleep } from '@portkey-wallet/utils'; export interface DappEventPack { eventName: T; @@ -33,7 +34,8 @@ export default class DappEventBus { public static unregisterOperator(operator: DappMobileOperator) { this.operators = this.operators.filter(item => item !== operator); } - public static emit(action: string, payload: any) { + public static async emit(action: string, payload: any) { + await sleep(100); switch (action) { case changeNetworkType.toString(): { const { currentNetwork } = getWallet(); diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index cfc1c37c62..cfc637181d 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -147,8 +147,6 @@ export default class DappMobileOperator extends Operator { }; protected handleRequestAccounts: SendRequest = async (eventName, params) => { - console.log('===========params=========================', params); - await this.dappManager.addDapp(params); return generateNormalResponse({ eventName, @@ -158,9 +156,6 @@ export default class DappMobileOperator extends Operator { protected handleSendTransaction: SendRequest = async (eventName, params) => { try { - if (!params || !params.params || !params.method || !params.contractAddress || !params.chainId) - return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); - const [chainInfo, caInfo] = await Promise.all([ this.dappManager.getChainInfo(params.chainId), this.dappManager.getCaInfo(params.chainId), @@ -248,7 +243,6 @@ export default class DappMobileOperator extends Operator { }) { // user confirm const response = await this.userConfirmation({ eventName, method, params }); - console.log('===response=================================', response); if (response) return response; return callBack(eventName, params); } @@ -279,12 +273,23 @@ export default class DappMobileOperator extends Operator { if (!isActive) return this.unauthenticated(eventName); callBack = this.handleSendTransaction; params = request.payload; + if ( + !params || + typeof params.params !== 'object' || + !params.method || + !params.contractAddress || + !params.chainId || + !params.rpcUrl + ) + return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); break; } case MethodsUnimplemented.GET_WALLET_SIGNATURE: { if (!isActive) return this.unauthenticated(eventName); callBack = this.handleSignature; params = request.payload; + if (!params || typeof params.data !== 'string') + return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); break; } } From 21b46e8d8fa3434b24ddb9fe5670fc3d517b0d55 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 15 Jun 2023 18:54:07 +0800 Subject: [PATCH 166/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20button=20?= =?UTF-8?q?loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/TransactionOverlay/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index d56c5de69b..24055251e7 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -77,6 +77,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { OverlayModal.hide(); }, disabled: disabled, + loading: disabled, }, ]; }, [isFetchingFee, noEnoughFee, onReject, onSign, t]); From 88c31a31380ebc17cc41c631a34e424277c73caa Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 15 Jun 2023 19:33:29 +0800 Subject: [PATCH 167/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20check=20tran?= =?UTF-8?q?sfer=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../methodController/AELFMethodController.ts | 281 +++++++++++------- .../app/web/pages/GetSignature/index.tsx | 4 - .../app/web/pages/SendTransactions/index.tsx | 31 +- .../app/web/utils/clearOpenTabs.ts | 4 +- 4 files changed, 196 insertions(+), 124 deletions(-) diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index a0366bb475..9af5bb507e 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -147,145 +147,224 @@ export default class AELFMethodController { }; getChainsInfo: RequestCommonHandler = async (sendResponse) => { - const data = await this.dappManager.chainsInfo(); - sendResponse({ ...errorHandler(0), data }); + try { + const data = await this.dappManager.chainsInfo(); + sendResponse({ ...errorHandler(0), data }); + } catch (error) { + console.log('getChainsInfo===', error); + sendResponse({ + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }); + } }; getAccounts: RequestCommonHandler = async (sendResponse, message) => { - const { origin } = message; - let accounts = {}; - const unlocked = this.isUnlocked(); - if (unlocked) accounts = await this.dappManager.accounts(origin); - console.log(accounts, 'accounts==='); - sendResponse({ ...errorHandler(0), data: accounts }); + try { + const { origin } = message; + let accounts = {}; + const unlocked = this.isUnlocked(); + if (unlocked) accounts = await this.dappManager.accounts(origin); + console.log(accounts, 'accounts==='); + sendResponse({ ...errorHandler(0), data: accounts }); + } catch (error) { + console.log('getAccounts===', error); + sendResponse({ + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }); + } }; getChainId: RequestCommonHandler = async (sendResponse) => { - const chainId = await this.dappManager.chainId(); - sendResponse({ ...errorHandler(0), data: chainId }); + try { + const chainId = await this.dappManager.chainId(); + sendResponse({ ...errorHandler(0), data: chainId }); + } catch (error) { + console.log('getChainId===', error); + sendResponse({ + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }); + } }; getChainIds: RequestCommonHandler = async (sendResponse) => { - const chainIds = await this.dappManager.chainIds(); - sendResponse({ ...errorHandler(0), data: chainIds }); - }; - - requestAccounts: RequestCommonHandler = async (sendResponse, message) => { - const isActive = await this.dappManager.isActive(message.origin); - if (isActive) return sendResponse({ ...errorHandler(0), data: await this.dappManager.accounts(message.origin) }); - const result = await this.approvalController.authorizedToConnect(message); - if (result.error === 200003) - return sendResponse({ - ...errorHandler(200003, 'User denied'), + try { + const chainIds = await this.dappManager.chainIds(); + sendResponse({ ...errorHandler(0), data: chainIds }); + } catch (error) { + console.log('getChainIds===', error); + sendResponse({ + ...errorHandler(100001), data: { - code: ResponseCode.USER_DENIED, + code: ResponseCode.INTERNAL_ERROR, }, }); - if (result.error !== 0) - return sendResponse({ - ...errorHandler(700002), + } + }; + + requestAccounts: RequestCommonHandler = async (sendResponse, message) => { + try { + const isActive = await this.dappManager.isActive(message.origin); + if (isActive) return sendResponse({ ...errorHandler(0), data: await this.dappManager.accounts(message.origin) }); + const result = await this.approvalController.authorizedToConnect(message); + if (result.error === 200003) + return sendResponse({ + ...errorHandler(200003, 'User denied'), + data: { + code: ResponseCode.USER_DENIED, + }, + }); + if (result.error !== 0) + return sendResponse({ + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }); + sendResponse({ ...errorHandler(0), data: await this.dappManager.accounts(message.origin) }); + } catch (error) { + console.log('requestAccounts===', error); + sendResponse({ + ...errorHandler(100001), data: { - code: ResponseCode.CONTRACT_ERROR, + code: ResponseCode.INTERNAL_ERROR, }, }); - // TODO - sendResponse({ ...errorHandler(0), data: await this.dappManager.accounts(message.origin) }); + } }; sendTransaction: RequestCommonHandler = async (sendResponse, message) => { - if (!message?.payload?.params) - return sendResponse({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); + try { + if (!message?.payload?.params) + return sendResponse({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); - if (!(await this.dappManager.isActive(message.origin))) - return sendResponse({ - ...errorHandler(200004), - data: { - code: ResponseCode.UNAUTHENTICATED, - }, - }); - const { payload, origin } = message; - const chainInfo = await this.dappManager.getChainInfo(payload.chainId); - const caInfo = await this.dappManager.getCaInfo(payload.chainId); - if (!chainInfo || !chainInfo.endPoint || !payload.params || !caInfo) - return sendResponse({ - ...errorHandler(200005), - data: { - code: 40001, - msg: 'invalid chain id', - }, - }); + if (!(await this.dappManager.isActive(message.origin))) + return sendResponse({ + ...errorHandler(200004), + data: { + code: ResponseCode.UNAUTHENTICATED, + }, + }); + const { payload, origin } = message; + const chainInfo = await this.dappManager.getChainInfo(payload.chainId); + const caInfo = await this.dappManager.getCaInfo(payload.chainId); + if (!chainInfo || !chainInfo.endPoint || !payload.params || !caInfo) + return sendResponse({ + ...errorHandler(200005), + data: { + code: 40001, + msg: 'invalid chain id', + }, + }); - const isForward = chainInfo?.caContractAddress !== payload?.contractAddress; - const method = isForward ? 'ManagerForwardCall' : payload?.method; + const isForward = chainInfo?.caContractAddress !== payload?.contractAddress; + const method = isForward ? 'ManagerForwardCall' : payload?.method; - if (!CA_METHOD_WHITELIST.includes(method)) - return sendResponse({ - ...errorHandler(400001), - data: { - code: ResponseCode.CONTRACT_ERROR, - msg: 'The current method is not supported', - }, - }); + if (!CA_METHOD_WHITELIST.includes(method)) + return sendResponse({ + ...errorHandler(400001), + data: { + code: ResponseCode.CONTRACT_ERROR, + msg: 'The current method is not supported', + }, + }); - const result = await this.approvalController.authorizedToSendTransactions({ - origin, - payload: message.payload, - }); - if (result.error === 200003) - return sendResponse({ - ...errorHandler(200003), - data: { - code: ResponseCode.USER_DENIED, - }, + const result = await this.approvalController.authorizedToSendTransactions({ + origin, + payload: message.payload, }); - if (result.error) - return sendResponse({ - ...errorHandler(700002), + if (result.error === 200003) + return sendResponse({ + ...errorHandler(200003), + data: { + code: ResponseCode.USER_DENIED, + }, + }); + if (result.error) + return sendResponse({ + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }); + sendResponse(result); + } catch (error) { + console.log('sendTransaction===', error); + sendResponse({ + ...errorHandler(100001), data: { - code: ResponseCode.CONTRACT_ERROR, + code: ResponseCode.INTERNAL_ERROR, }, }); - sendResponse(result); + } }; getSignature: RequestCommonHandler = async (sendResponse, message) => { - if (!message?.payload?.data) - return sendResponse({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); + try { + if (!message?.payload?.data || typeof message.payload.data !== 'string') + return sendResponse({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); - if (!(await this.dappManager.isActive(message.origin))) - return sendResponse({ - ...errorHandler(200004), - data: { - code: ResponseCode.UNAUTHENTICATED, - }, - }); + if (!(await this.dappManager.isActive(message.origin))) + return sendResponse({ + ...errorHandler(200004), + data: { + code: ResponseCode.UNAUTHENTICATED, + }, + }); - const result = await this.approvalController.authorizedToGetSignature({ - origin, - payload: { - data: message.payload.data, - origin: message.origin, - }, - }); - if (result.error === 200003) - return sendResponse({ - ...errorHandler(200003), - data: { - code: ResponseCode.USER_DENIED, + const result = await this.approvalController.authorizedToGetSignature({ + origin, + payload: { + data: message.payload.data, + origin: message.origin, }, }); - if (result.error) - return sendResponse({ - ...errorHandler(700002), + if (result.error === 200003) + return sendResponse({ + ...errorHandler(200003), + data: { + code: ResponseCode.USER_DENIED, + }, + }); + if (result.error) + return sendResponse({ + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }); + sendResponse(result); + } catch (error) { + console.log('getSignature===', error); + sendResponse({ + ...errorHandler(100001), data: { - code: ResponseCode.CONTRACT_ERROR, + code: ResponseCode.INTERNAL_ERROR, }, }); - sendResponse(result); + } }; getNetwork: RequestCommonHandler = async (sendResponse) => { - const networkType = await this.dappManager.networkType(); - sendResponse({ ...errorHandler(0), data: networkType }); + try { + const networkType = await this.dappManager.networkType(); + sendResponse({ ...errorHandler(0), data: networkType }); + } catch (error) { + console.log('getNetwork===', error); + sendResponse({ + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }); + } }; } diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx index 0163183b6f..1fb68e3fa7 100644 --- a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx @@ -47,10 +47,6 @@ export default function GetSignature() { const sendHandler = useCallback(async () => { try { - if (!payload?.data) { - closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); - return; - } if (!privateKey) throw 'Invalid user information, please check'; const manager = getWallet(privateKey); diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 4beb192714..a57b1e5d4a 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -101,22 +101,19 @@ export default function SendTransactions() { } }, [getTokenPrice, getTokensPrice, payload, isMainnet]); - const renderAccountInfo = useMemo( - () => - payload?.contractAddress ? ( -
    -
    {walletName}
    - -
    {`${payload?.contractAddress.slice(0, 10)}...${payload?.contractAddress.slice( - -4, - )}`}
    -
    -
    - ) : ( - <> - ), - [payload, walletName], - ); + const renderAccountInfo = useMemo(() => { + if (payload?.contractAddress || typeof payload.contractAddress !== 'string') return <>; + return ( +
    +
    {walletName}
    + +
    {`${payload.contractAddress.slice(0, 10)}...${payload.contractAddress.slice( + -4, + )}`}
    +
    +
    + ); + }, [payload, walletName]); // Transfer const renderDetail = useMemo(() => { @@ -180,7 +177,7 @@ export default function SendTransactions() { {Object.keys(params).map((item) => (
    {item}
    -
    {params[item]}
    +
    {JSON.stringify(params[item])}
    ))}
    diff --git a/packages/web-extension-did/app/web/utils/clearOpenTabs.ts b/packages/web-extension-did/app/web/utils/clearOpenTabs.ts index 6d503b3913..5b5e1d1be7 100644 --- a/packages/web-extension-did/app/web/utils/clearOpenTabs.ts +++ b/packages/web-extension-did/app/web/utils/clearOpenTabs.ts @@ -18,8 +18,8 @@ export default async function closeOpenTabs(keepCurTabAlive = false) { if (Number(tab) === curTabId) return; } const extId = apis.runtime.id; - const t = await apis.tabs.get(+tab); - if (t.pendingUrl?.includes(extId)) { + const opTab = await apis.tabs.get(+tab); + if (opTab.pendingUrl?.includes(extId)) { chrome.tabs.remove(Number(tab)); } }); From a554a7fcefed3ce6564293bee2b231cd00201b93 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 15 Jun 2023 21:13:38 +0800 Subject: [PATCH 168/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20close=20?= =?UTF-8?q?tab=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/openNewTabController/index.ts | 63 ++++++++++++++++--- .../app/web/hooks/useChangeNetwork.ts | 8 ++- .../web/service/NotificationService/index.ts | 6 +- .../serviceWorker/ServiceWorkerInstantiate.ts | 9 +-- .../app/web/utils/clearOpenTabs.ts | 51 --------------- 5 files changed, 69 insertions(+), 68 deletions(-) delete mode 100644 packages/web-extension-did/app/web/utils/clearOpenTabs.ts diff --git a/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts b/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts index 396bb65bf9..2c96b92ac3 100644 --- a/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts +++ b/packages/web-extension-did/app/web/controllers/openNewTabController/index.ts @@ -1,21 +1,68 @@ import { apis } from 'utils/BrowserApis'; -import { removeOpenTabs, saveOpenTabs } from 'utils/clearOpenTabs'; +import { getLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; export default class OpenNewTabController { - constructor() { - this.openNewTab(); - } - openNewTab() { - const createdListener = (tab: chrome.tabs.Tab) => { + static onOpenNewTab() { + const createdListener = async (tab: chrome.tabs.Tab) => { if (tab.id) { - saveOpenTabs(tab.id + ''); + OpenNewTabController.saveOpenTabs(tab.id + ''); } }; apis.tabs.onCreated.addListener(createdListener); const rmListener = (tabId: number) => { - removeOpenTabs(tabId); + OpenNewTabController.removeOpenTabs(tabId); }; apis.tabs.onRemoved.addListener(rmListener); } + + static async removeOpenTabs(tabId: string | number) { + if (!tabId) return; + tabId = String(tabId); + const openTabsId = await getLocalStorage('openTabsId'); + const newTabsId = openTabsId?.filter((id: string) => id !== tabId); + setLocalStorage({ openTabsId: newTabsId }); + } + + static async saveOpenTabs(tabId: string | number) { + if (!tabId) return; + const openedTabsId = await getLocalStorage('openTabsId'); + + let newTabsId = []; + if (openedTabsId) { + newTabsId = [...openedTabsId, tabId + '']; + } else { + newTabsId = [tabId]; + } + + setLocalStorage({ openTabsId: newTabsId }); + } + + static async closeOpenTabs(keepCurTabAlive = false) { + const openTabs = await getLocalStorage('openTabsId'); + let curTabId = 0; + if (keepCurTabAlive) { + const window = await chrome.windows.getCurrent(); + + const tabs = await chrome.tabs.query({ windowId: window.id, active: true }); + if (tabs.length !== 0 && !!tabs[0].id) { + curTabId = tabs[0].id; + } + } + + if (openTabs) { + openTabs.forEach(async (tab: string) => { + if (keepCurTabAlive) { + if (Number(tab) === curTabId) return; + } + const extId = apis.runtime.id; + const opTab = await apis.tabs.get(+tab); + if ((opTab.url || opTab?.pendingUrl)?.includes(extId)) { + chrome.tabs.remove(Number(tab)); + } + }); + const openTabsId = keepCurTabAlive ? [curTabId] : []; + setLocalStorage({ openTabsId }); + } + } } diff --git a/packages/web-extension-did/app/web/hooks/useChangeNetwork.ts b/packages/web-extension-did/app/web/hooks/useChangeNetwork.ts index 5a4c250fac..30788bf004 100644 --- a/packages/web-extension-did/app/web/hooks/useChangeNetwork.ts +++ b/packages/web-extension-did/app/web/hooks/useChangeNetwork.ts @@ -7,9 +7,9 @@ import { useCallback } from 'react'; import { useNavigate } from 'react-router'; import { useAppDispatch, useCommonState } from 'store/Provider/hooks'; import { useResetStore } from '@portkey-wallet/hooks/hooks-ca'; -import closeOpenTabs from 'utils/clearOpenTabs'; import { sleep } from '@portkey-wallet/utils'; import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; +import OpenNewTabController from 'controllers/openNewTabController'; export function useChangeNetwork() { const dispatch = useAppDispatch(); @@ -29,11 +29,14 @@ export function useChangeNetwork() { dispatch(setWalletNameAction('Wallet 01')); dispatch(changeNetworkType(network.networkType)); if (tmpCaInfo?.managerInfo && tmpCaInfo?.[tmpChainId]?.caAddress) { - await closeOpenTabs(true); if (!isPrompt) { + await OpenNewTabController.closeOpenTabs(); + await sleep(500); InternalMessage.payload(PortkeyMessageTypes.EXPAND_FULL_SCREEN).send(); } else { + await OpenNewTabController.closeOpenTabs(true); + navigate('/'); } } else { @@ -41,6 +44,7 @@ export function useChangeNetwork() { await sleep(500); InternalMessage.payload(PortkeyMessageTypes.REGISTER_START_WALLET).send(); } else { + await OpenNewTabController.closeOpenTabs(true); navigate('/register/start'); } } diff --git a/packages/web-extension-did/app/web/service/NotificationService/index.ts b/packages/web-extension-did/app/web/service/NotificationService/index.ts index 27898e1264..439fadd172 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/index.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/index.ts @@ -5,7 +5,7 @@ import errorHandler from 'utils/errorHandler'; import getPromptConfig from 'service/NotificationService/getPromptConfig'; import ExtensionPlatform from 'utils/platforms/extension'; import { sleep } from '@portkey-wallet/utils'; -import { removeOpenTabs, saveOpenTabs } from 'utils/clearOpenTabs'; +import OpenNewTabController from 'controllers/openNewTabController'; export interface NotificationType { sendResponse?: SendResponseFun; @@ -108,7 +108,7 @@ export default class NotificationService { }); this.openTag = tag; this.closeSender = { ...this.closeSender, [tag.id?.toString() ?? '']: notification }; - saveOpenTabs(tag.id || ''); + OpenNewTabController.saveOpenTabs(tag.id || ''); return tag; }; @@ -134,7 +134,7 @@ export default class NotificationService { // virus-like behavior as apps overflow the queue causing the user // to have to quit the apis to regain control. } else if (promptType === 'tabs' && this.openTag) { - removeOpenTabs(this.openTag.id || ''); + OpenNewTabController.removeOpenTabs(this.openTag.id || ''); this.platform.closeTab(this.openTag.id); this.openTag = null; } diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 7eb2f03873..35a9fa02ef 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -18,15 +18,14 @@ import { CreatePromptType, SendResponseFun } from 'types'; import errorHandler from 'utils/errorHandler'; import { apis } from 'utils/BrowserApis'; import SocialLoginController from 'controllers/socialLoginController'; -import OpenNewTabController from 'controllers/openNewTabController'; import { LocalStream } from 'utils/extensionStreams'; import { MethodsUnimplemented, MethodsBase } from '@portkey/provider-types'; import { getWalletState } from 'utils/lib/SWGetReduxStore'; +import OpenNewTabController from 'controllers/openNewTabController'; const notificationService = new NotificationService(); const socialLoginService = new SocialLoginController(); - -new OpenNewTabController(); +OpenNewTabController.onOpenNewTab(); // Get default data in redux const store = getStoreState(); @@ -269,7 +268,9 @@ export default class ServiceWorkerInstantiate { }; }; - static registerStartWallet = () => { + static registerStartWallet = async () => { + // close this(chrome.runtime.id) other tabs when register wallet + await OpenNewTabController.closeOpenTabs(); notificationService.openPrompt( { method: PromptRouteTypes.REGISTER_START_WALLET, diff --git a/packages/web-extension-did/app/web/utils/clearOpenTabs.ts b/packages/web-extension-did/app/web/utils/clearOpenTabs.ts deleted file mode 100644 index 5b5e1d1be7..0000000000 --- a/packages/web-extension-did/app/web/utils/clearOpenTabs.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { apis } from './BrowserApis'; -import { getLocalStorage, setLocalStorage } from './storage/chromeStorage'; - -export default async function closeOpenTabs(keepCurTabAlive = false) { - const openTabs = await getLocalStorage('openTabsId'); - let curTabId = 0; - if (keepCurTabAlive) { - const window = await chrome.windows.getCurrent(); - const tabs = await chrome.tabs.query({ windowId: window.id, active: true }); - if (tabs.length !== 0 && !!tabs[0].id) { - curTabId = tabs[0].id; - } - } - - if (openTabs) { - openTabs.forEach(async (tab: string) => { - if (keepCurTabAlive) { - if (Number(tab) === curTabId) return; - } - const extId = apis.runtime.id; - const opTab = await apis.tabs.get(+tab); - if (opTab.pendingUrl?.includes(extId)) { - chrome.tabs.remove(Number(tab)); - } - }); - const openTabsId = keepCurTabAlive ? [curTabId] : []; - setLocalStorage({ openTabsId }); - } -} - -export async function saveOpenTabs(tabId: string | number) { - if (!tabId) return; - const openedTabsId = await getLocalStorage('openTabsId'); - - let newTabsId = []; - if (openedTabsId) { - newTabsId = [...openedTabsId, tabId]; - } else { - newTabsId = [tabId]; - } - - setLocalStorage({ openTabsId: newTabsId }); -} - -export async function removeOpenTabs(tabId: string | number) { - if (!tabId) return; - tabId = String(tabId); - const openTabsId = await getLocalStorage('openTabsId'); - const newTabsId = openTabsId?.filter((id: string) => id !== tabId); - setLocalStorage({ openTabsId: newTabsId }); -} From 45e5fe7f3f7e544cbf274a6884a78b191d0094da Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 16 Jun 2023 10:15:46 +0800 Subject: [PATCH 169/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20params=20is=20str?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/TransactionOverlay/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 24055251e7..d958a570eb 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -313,7 +313,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {transferContent} {!isTransfer && ( )} From f068728e2dd95e58120b7e565b0dc27a90fb1392 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 16 Jun 2023 10:11:11 +0800 Subject: [PATCH 170/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20check=20type?= =?UTF-8?q?=20and=20change=20show?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/SendTransactions/index.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index a57b1e5d4a..b629ec1a04 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -174,12 +174,16 @@ export default function SendTransactions() {
    Message
    - {Object.keys(params).map((item) => ( -
    -
    {item}
    -
    {JSON.stringify(params[item])}
    -
    - ))} + {typeof params === 'object' ? ( + Object.keys(params).map((item) => ( +
    +
    {item}
    +
    {JSON.stringify(params[item])}
    +
    + )) + ) : ( +
    {`${params}`}
    + )}
    Transaction Fee
    From 9df244abdb8b6654484f24a6fc21008fc1716237 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 16 Jun 2023 10:44:03 +0800 Subject: [PATCH 171/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20activity=20progre?= =?UTF-8?q?ss=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Activity/ViewOnWebView/index.tsx | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx index ef11dd48a1..28d4d7d8b9 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useRef, useState } from 'react'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import WebView from 'react-native-webview'; import CustomHeader from 'components/CustomHeader'; @@ -10,6 +10,7 @@ import navigationService from 'utils/navigationService'; import { ACH_REDIRECT_URL, ACH_WITHDRAW_URL } from 'constants/common'; import { useHandleAchSell } from './hooks/useHandleAchSell'; import CommonToast from 'components/CommonToast'; +import Progressbar, { IProgressbar } from 'components/Progressbar'; const safeAreaColorMap = { white: defaultColors.bg1, @@ -44,6 +45,7 @@ const ViewOnWebView: React.FC = () => { const [browserInfo] = useState({ url, title }); const webViewRef = React.useRef(null); + const progressBarRef = React.useRef(null); const handleAchSell = useHandleAchSell(); const isAchSellHandled = useRef(false); @@ -72,18 +74,23 @@ const ViewOnWebView: React.FC = () => { }, [handleAchSell, params, webViewPageType], ); + return ( - + + + progressBarRef.current?.changeInnerBarWidth(nativeEvent.progress)} + /> + ); }; @@ -99,6 +106,10 @@ export const pageStyles = StyleSheet.create({ svgWrap: { marginRight: pTd(16), }, + contentWrap: { + position: 'relative', + flex: 1, + }, webViewContainer: { flex: 1, backgroundColor: 'red', From a15e3c821d3aa358ee5a113b82d91129a261b90e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 16 Jun 2023 10:44:51 +0800 Subject: [PATCH 172/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20justify=20dapp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/TransactionOverlay/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index d958a570eb..90ea8e8a12 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -313,7 +313,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {transferContent} {!isTransfer && ( )} From c0edcb312acf1565fa01b6e76a496a721d5b9d35 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 16 Jun 2023 10:52:15 +0800 Subject: [PATCH 173/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20add=20assets=20st?= =?UTF-8?q?ore=20persisit=20back?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/store/config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/mobile-app-did/js/store/config.ts b/packages/mobile-app-did/js/store/config.ts index 1dafe4bd67..30d44148ab 100644 --- a/packages/mobile-app-did/js/store/config.ts +++ b/packages/mobile-app-did/js/store/config.ts @@ -16,6 +16,7 @@ import switchSlice from '@portkey-wallet/store/store-ca/switch/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; +import assetsSlice from '@portkey-wallet/store/store-ca/assets/slice'; interface ThunkOptions { extraArgument: E; @@ -43,6 +44,7 @@ const reduxPersistConfig = { settingsSlice.name, chainSlice.name, recentSlice.name, + assetsSlice.name, activitySlice.name, switchSlice.name, dappSlice.name, From df3394b68a5e4005022bd88c881b092d9c608d58 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 16 Jun 2023 13:56:38 +0800 Subject: [PATCH 174/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/package.json | 8 ++++---- packages/types/package.json | 2 +- packages/utils/package.json | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index 7acafd4e52..f442b25ba8 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -51,10 +51,10 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/mobile-provider": "^0.0.1-alpha.22", - "@portkey/provider-types": "^0.0.1-alpha.22", - "@portkey/provider-utils": "^0.0.1-alpha.22", - "@portkey/providers": "^0.0.1-alpha.22", + "@portkey/mobile-provider": "0.0.1-alpha.22", + "@portkey/provider-types": "0.0.1-alpha.22", + "@portkey/provider-utils": "0.0.1-alpha.22", + "@portkey/providers": "0.0.1-alpha.22", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index 5f5e17747b..eadc0f9665 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "version": "0.0.1", "type": "commonjs", "dependencies": { - "@portkey/provider-types": "^0.0.1-alpha.22" + "@portkey/provider-types": "0.0.1-alpha.22" }, "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/utils/package.json b/packages/utils/package.json index f43eb0ee18..0ecbfe5177 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,8 +23,8 @@ "expo-local-authentication": "^12.3.0", "expo-secure-store": "^11.3.0", "@react-native-async-storage/async-storage": "^1.17.6", - "@portkey/provider-types": "^0.0.1-alpha.22", - "@portkey/provider-utils": "^0.0.1-alpha.22", - "@portkey/providers": "^0.0.1-alpha.22" + "@portkey/provider-types": "0.0.1-alpha.22", + "@portkey/provider-utils": "0.0.1-alpha.22", + "@portkey/providers": "0.0.1-alpha.22" } } From 1a502a13f962e62fd51ce280d68a817092f0296e Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 16 Jun 2023 15:07:44 +0800 Subject: [PATCH 175/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20GET=5FWALLET=5FSI?= =?UTF-8?q?GNATURE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/dappMobileOperator.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index cfc637181d..762322f203 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -257,7 +257,7 @@ export default class DappMobileOperator extends Operator { const isActive = await this.isActive(); - let callBack: SendRequest, params: any; + let callBack: SendRequest, payload: any; switch (method) { case MethodsBase.REQUEST_ACCOUNTS: { if (isActive) @@ -266,20 +266,20 @@ export default class DappMobileOperator extends Operator { data: await this.dappManager.accounts(this.dapp.origin), }); callBack = this.handleRequestAccounts; - params = this.dapp; + payload = this.dapp; break; } case MethodsBase.SEND_TRANSACTION: { if (!isActive) return this.unauthenticated(eventName); callBack = this.handleSendTransaction; - params = request.payload; + payload = request.payload; if ( - !params || - typeof params.params !== 'object' || - !params.method || - !params.contractAddress || - !params.chainId || - !params.rpcUrl + !payload || + typeof payload.params !== 'object' || + !payload.method || + !payload.contractAddress || + !payload.chainId || + !payload.rpcUrl ) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); break; @@ -287,15 +287,15 @@ export default class DappMobileOperator extends Operator { case MethodsUnimplemented.GET_WALLET_SIGNATURE: { if (!isActive) return this.unauthenticated(eventName); callBack = this.handleSignature; - params = request.payload; - if (!params || typeof params.data !== 'string') + payload = request.payload; + if (!payload || typeof payload.data !== 'string' || typeof payload.data !== 'number') return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); break; } } return this.sendRequest({ eventName, - params, + params: payload, method: method as any, callBack: callBack!, }); From a3761308bf4e761019a04370c180b8de3c8dbddd Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 16 Jun 2023 15:29:27 +0800 Subject: [PATCH 176/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/dappMobileOperator.ts | 12 ++-- packages/mobile-app-did/package.json | 8 +-- packages/types/package.json | 2 +- packages/utils/package.json | 6 +- packages/web-extension-did/app/web/content.ts | 4 +- .../methodController/AELFMethodController.ts | 14 ++--- .../serviceWorker/ServiceWorkerInstantiate.ts | 4 +- packages/web-extension-did/package.json | 8 +-- yarn.lock | 62 +++++++++---------- 9 files changed, 60 insertions(+), 60 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 762322f203..090a29dc66 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -5,7 +5,7 @@ import { IResponseType, ResponseCode, MethodsBase, - MethodsUnimplemented, + MethodsWallet, SendTransactionParams, NotificationEvents, WalletState, @@ -25,7 +25,7 @@ import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp const SEND_METHOD: { [key: string]: true } = { [MethodsBase.SEND_TRANSACTION]: true, [MethodsBase.REQUEST_ACCOUNTS]: true, - [MethodsUnimplemented.GET_WALLET_SIGNATURE]: true, + [MethodsWallet.GET_WALLET_SIGNATURE]: true, }; function getManager() { @@ -107,7 +107,7 @@ export default class DappMobileOperator extends Operator { data: await this.dappManager.chainsInfo(), }); } - case MethodsUnimplemented.GET_WALLET_NAME: { + case MethodsWallet.GET_WALLET_NAME: { const isActive = await this.isActive(); if (!isActive) return this.unauthenticated(eventName); return generateNormalResponse({ @@ -121,7 +121,7 @@ export default class DappMobileOperator extends Operator { data: await this.dappManager.networkType(), }); } - case MethodsUnimplemented.GET_WALLET_STATE: { + case MethodsWallet.GET_WALLET_STATE: { const [isActive, isLocked] = await Promise.all([this.isActive(), this.dappManager.isLocked()]); const data: WalletState = { isConnected: isActive, isUnlocked: !isLocked }; if (isActive) { @@ -284,11 +284,11 @@ export default class DappMobileOperator extends Operator { return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); break; } - case MethodsUnimplemented.GET_WALLET_SIGNATURE: { + case MethodsWallet.GET_WALLET_SIGNATURE: { if (!isActive) return this.unauthenticated(eventName); callBack = this.handleSignature; payload = request.payload; - if (!payload || typeof payload.data !== 'string' || typeof payload.data !== 'number') + if (!payload || (typeof payload.data !== 'string' && typeof payload.data !== 'number')) return generateErrorResponse({ eventName, code: ResponseCode.ERROR_IN_PARAMS }); break; } diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index f442b25ba8..b6447e8866 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -51,10 +51,10 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/mobile-provider": "0.0.1-alpha.22", - "@portkey/provider-types": "0.0.1-alpha.22", - "@portkey/provider-utils": "0.0.1-alpha.22", - "@portkey/providers": "0.0.1-alpha.22", + "@portkey/mobile-provider": "0.0.1-alpha.24", + "@portkey/provider-types": "0.0.1-alpha.24", + "@portkey/provider-utils": "0.0.1-alpha.24", + "@portkey/providers": "0.0.1-alpha.24", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index eadc0f9665..2f76d29b75 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "version": "0.0.1", "type": "commonjs", "dependencies": { - "@portkey/provider-types": "0.0.1-alpha.22" + "@portkey/provider-types": "0.0.1-alpha.24" }, "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/utils/package.json b/packages/utils/package.json index 0ecbfe5177..efb9c67052 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,8 +23,8 @@ "expo-local-authentication": "^12.3.0", "expo-secure-store": "^11.3.0", "@react-native-async-storage/async-storage": "^1.17.6", - "@portkey/provider-types": "0.0.1-alpha.22", - "@portkey/provider-utils": "0.0.1-alpha.22", - "@portkey/providers": "0.0.1-alpha.22" + "@portkey/provider-types": "0.0.1-alpha.24", + "@portkey/provider-utils": "0.0.1-alpha.24", + "@portkey/providers": "0.0.1-alpha.24" } } diff --git a/packages/web-extension-did/app/web/content.ts b/packages/web-extension-did/app/web/content.ts index 6b0434aa28..ada43e9499 100644 --- a/packages/web-extension-did/app/web/content.ts +++ b/packages/web-extension-did/app/web/content.ts @@ -5,7 +5,7 @@ import { runWorkerKeepAliveInterval } from 'utils/keepAlive'; import { checkForError } from 'utils'; import { ContentPostStream } from '@portkey/extension-provider'; import { - MethodsUnimplemented, + MethodsWallet, IRequestParams, MethodsType, ProviderError, @@ -22,7 +22,7 @@ import { isMethodsWalletMessage } from 'messages/utils'; * connected to the dapp or when the user is not interacting with the extension. * The keep-alive logic should not work for non-dapp pages. */ -const IGNORE_INIT_METHODS_FOR_KEEP_ALIVE: string[] = [MethodsUnimplemented.GET_WALLET_STATE]; +const IGNORE_INIT_METHODS_FOR_KEEP_ALIVE: string[] = [MethodsWallet.GET_WALLET_STATE]; const EXTENSION_CONTEXT_INVALIDATED_CHROMIUM_ERROR = 'Extension context invalidated.'; // The stream that connects between the content script and the website diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 9af5bb507e..2fdff620b8 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -6,7 +6,7 @@ import NotificationService from 'service/NotificationService'; import { SendResponseFun } from 'types'; import { IPageState, RequestCommonHandler, RequestMessageData } from 'types/SW'; import errorHandler from 'utils/errorHandler'; -import { MethodsBase, ResponseCode, MethodsUnimplemented } from '@portkey/provider-types'; +import { MethodsBase, ResponseCode, MethodsWallet } from '@portkey/provider-types'; import { ExtensionDappManager } from './ExtensionDappManager'; import { getSWReduxState } from 'utils/lib/SWGetReduxStore'; import ApprovalController from 'controllers/approval/ApprovalController'; @@ -26,10 +26,10 @@ const aelfMethodList = [ MethodsBase.CHAINS_INFO, MethodsBase.REQUEST_ACCOUNTS, MethodsBase.SEND_TRANSACTION, - MethodsUnimplemented.GET_WALLET_SIGNATURE, + MethodsWallet.GET_WALLET_SIGNATURE, MethodsBase.NETWORK, - MethodsUnimplemented.GET_WALLET_STATE, - MethodsUnimplemented.GET_WALLET_NAME, + MethodsWallet.GET_WALLET_STATE, + MethodsWallet.GET_WALLET_NAME, ]; interface AELFMethodControllerProps { notificationService: NotificationService; @@ -78,13 +78,13 @@ export default class AELFMethodController { case MethodsBase.NETWORK: this.getNetwork(sendResponse, message.payload); break; - case MethodsUnimplemented.GET_WALLET_SIGNATURE: + case MethodsWallet.GET_WALLET_SIGNATURE: this.getSignature(sendResponse, message.payload); break; - case MethodsUnimplemented.GET_WALLET_STATE: + case MethodsWallet.GET_WALLET_STATE: this.getWalletState(sendResponse, message.payload); break; - case MethodsUnimplemented.GET_WALLET_NAME: + case MethodsWallet.GET_WALLET_NAME: this.getWalletName(sendResponse, message.payload); break; default: diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 9078736d57..1f3d42e6a7 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -19,7 +19,7 @@ import errorHandler from 'utils/errorHandler'; import { apis } from 'utils/BrowserApis'; import SocialLoginController from 'controllers/socialLoginController'; import { LocalStream } from 'utils/extensionStreams'; -import { MethodsUnimplemented, MethodsBase } from '@portkey/provider-types'; +import { MethodsWallet, MethodsBase } from '@portkey/provider-types'; import { getWalletState } from 'utils/lib/SWGetReduxStore'; import OpenNewTabController from 'controllers/openNewTabController'; @@ -55,7 +55,7 @@ const permissionWhitelist = [ PortkeyMessageTypes.OPEN_RECAPTCHA_PAGE, WalletMessageTypes.SET_RECAPTCHA_CODE_V2, WalletMessageTypes.SOCIAL_LOGIN, - MethodsUnimplemented.GET_WALLET_STATE, + MethodsWallet.GET_WALLET_STATE, // The method that requires the dapp not to trigger the lock call MethodsBase.ACCOUNTS, MethodsBase.CHAIN_ID, diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index d68943b799..ae2cef0995 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.22", - "@portkey/provider-utils": "0.0.1-alpha.22", - "@portkey/provider-types": "0.0.1-alpha.22", - "@portkey/providers": "0.0.1-alpha.22", + "@portkey/extension-provider": "0.0.1-alpha.24", + "@portkey/provider-utils": "0.0.1-alpha.24", + "@portkey/provider-types": "0.0.1-alpha.24", + "@portkey/providers": "0.0.1-alpha.24", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index ca53198351..dc6d308fae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,51 +5184,51 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/chain@^0.0.1-alpha.22": - version "0.0.1-alpha.22" - resolved "https://registry.yarnpkg.com/@portkey/chain/-/chain-0.0.1-alpha.22.tgz#484f4ad889b8f3f02aa7b5dae48d3648eb225f93" - integrity sha512-2Z/yI6Ze0Of2HFdReOzVqXgTPULg4uEfKZ1GUqr4piRBYCbBmmrl0nhABuJIxOsNuW+5OQYRwFD5C3SJ4g26xA== +"@portkey/chain@^0.0.1-alpha.24": + version "0.0.1-alpha.24" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.24.tgz#f68a663b9c6e4b54abdffb6e5b8ac182ef0c30bb" + integrity sha512-ESCSHYFX8+cuX/j+KnafR8/K9E1xik1IYYQQc6gKB38mlClGeZvhO7KpX9ywfV9qF/a131+xQfn36v5xTpl60w== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.22" + "@portkey/provider-types" "^0.0.1-alpha.24" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.22": - version "0.0.1-alpha.22" - resolved "https://registry.yarnpkg.com/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.22.tgz#29ffc0a057a5fb5deeb538240a28718e15983fb3" - integrity sha512-tKAceyXZ1tD0JuwBC+0T1ZHD2umTmQz3lBQNF/LQ7RtIC5e7MyycXot2aBVYFBh8Ia0f4EjRAnl+EDm4eJII+Q== +"@portkey/extension-provider@0.0.1-alpha.24": + version "0.0.1-alpha.24" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.24.tgz#89df205929f66895744adaf6bd9b6dd99a68323d" + integrity sha512-OgCcUJOrxaMTN2nPCdIjTYlq6Du4UTdQSeodopFNc2Bznz0IWiq/Y0vTZkt9R0yAchuk+JBTVqOHC5C+ze1LvA== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.22" - "@portkey/providers" "^0.0.1-alpha.22" + "@portkey/provider-types" "^0.0.1-alpha.24" + "@portkey/providers" "^0.0.1-alpha.24" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/mobile-provider@^0.0.1-alpha.22": - version "0.0.1-alpha.22" - resolved "https://registry.yarnpkg.com/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.22.tgz#890de64cca9812bb02ee100d2dd3fcbfa5300215" - integrity sha512-d5pOIIRq4wWIZzEYp1R/dO99V4cEgJE4Sb1jJMJWaXltHCrBdQhxa0WMRm6LppTPTuH07RHlKtglG1ZKy0N+wg== +"@portkey/mobile-provider@0.0.1-alpha.24": + version "0.0.1-alpha.24" + resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.24.tgz#99e161a0a8ced345d24363da8a696989349cb187" + integrity sha512-zY3sGPmbPRqlX3SoA1XDrq52vmo+rwXrXPRvqui+P2CPscyOlVzgRCObbsgzdw2Utx0Bu/44NqIFdm7CtaaRaA== -"@portkey/provider-types@0.0.1-alpha.22", "@portkey/provider-types@^0.0.1-alpha.22": - version "0.0.1-alpha.22" - resolved "https://registry.yarnpkg.com/@portkey/provider-types/-/provider-types-0.0.1-alpha.22.tgz#89aef3b358650bcea3ddccfdc8c865e7bd7e814a" - integrity sha512-0EWV2a2v8+ZvHhs6RLtegMJUYPAHB3aeLWgzKMU5vUqG6OzOtmTAFbTvIBmZcW3xSHxyPDkkvMwjxPiSaSCigw== +"@portkey/provider-types@0.0.1-alpha.24", "@portkey/provider-types@^0.0.1-alpha.24": + version "0.0.1-alpha.24" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.24.tgz#47cbcffdbe680fc7e6b82a614a329ba1c219c4dd" + integrity sha512-7tdAY+mQkcxc1LXW3twODAJTnafdEjHKtwOpzK4H5co4glITYMMH8jEmcCRfxS6hSFeJeV7Io4N1eGIgylf/sg== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.22", "@portkey/provider-utils@^0.0.1-alpha.22": - version "0.0.1-alpha.22" - resolved "https://registry.yarnpkg.com/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.22.tgz#a9e25bb0ccec4c11626da2e30b4aae03d249753f" - integrity sha512-n8z4gGeWohRR/o2cDfqyxFCtMyCarXqgwu3pae1dT3FWRgEJi7dO1kPrQ7cK0zS2opltgfr4KbeARYZIwU4rDg== +"@portkey/provider-utils@0.0.1-alpha.24", "@portkey/provider-utils@^0.0.1-alpha.24": + version "0.0.1-alpha.24" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.24.tgz#fde4f7cd09f6f6d17ae54014f6299cd306a900f1" + integrity sha512-yC8r4Z1Jy2fABu/RV936MBzi56zqgO6d0M9q9k+r+y/o/ThWBGWWkvypUKGzh6aVmjlL0SmP+CQ0gGWqhumOdg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.22" + "@portkey/provider-types" "^0.0.1-alpha.24" -"@portkey/providers@0.0.1-alpha.22", "@portkey/providers@^0.0.1-alpha.22": - version "0.0.1-alpha.22" - resolved "https://registry.yarnpkg.com/@portkey/providers/-/providers-0.0.1-alpha.22.tgz#38b35865724dd6936df3f2901371413ca4f6e6c5" - integrity sha512-Rx7Rhuw56ibkB4iS1CglK5kA92iWSB4YOA2ylwZ+bY0jXJf3lw5grrk7s38ur133Ig8D7SW4AZp9jaD+G7TjLg== +"@portkey/providers@0.0.1-alpha.24", "@portkey/providers@^0.0.1-alpha.24": + version "0.0.1-alpha.24" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.24.tgz#a7f72aff434ca5a129049bc1064a035757e6c82e" + integrity sha512-OPgwrc7GBJhNxrxesTZI1wXA78PYW71u36fMyszYu1r2I9bRUzs+JsjknCMLbX1SSsU+ro7W5clrN7Sejey6uA== dependencies: - "@portkey/chain" "^0.0.1-alpha.22" - "@portkey/provider-types" "^0.0.1-alpha.22" - "@portkey/provider-utils" "^0.0.1-alpha.22" + "@portkey/chain" "^0.0.1-alpha.24" + "@portkey/provider-types" "^0.0.1-alpha.24" + "@portkey/provider-utils" "^0.0.1-alpha.24" "@types/readable-stream" "^2.3.15" lodash "^4.17.21" readable-stream "^4.4.0" From cc3dd0b996c34ad921b4ee6e4a1dbdeb0d7ece72 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 16 Jun 2023 15:46:15 +0800 Subject: [PATCH 177/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20check=20signature?= =?UTF-8?q?=20data=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controllers/methodController/AELFMethodController.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 2fdff620b8..760e7afa7e 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -309,7 +309,10 @@ export default class AELFMethodController { getSignature: RequestCommonHandler = async (sendResponse, message) => { try { - if (!message?.payload?.data || typeof message.payload.data !== 'string') + if ( + !message?.payload?.data || + (typeof message.payload.data !== 'string' && typeof message.payload.data !== 'number') // The problem left over from the browser history needs to pass the number type + ) return sendResponse({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }); if (!(await this.dappManager.isActive(message.origin))) From 516fee4531f171e6eb6bc3e81535da2b94eb2011 Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 16 Jun 2023 17:10:04 +0800 Subject: [PATCH 178/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20initValueSave=20h?= =?UTF-8?q?as=20been=20tampered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Buy/index.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 8c17d37d63..780e1cb246 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -163,14 +163,11 @@ export default function Buy() { setReceiveCase({ fiatQuantity, rampFee, cryptoQuantity }); setRate(cryptoPrice); setErrMsg(''); - if (!updateTimerRef.current && valueSaveRef.current.receive) { + valueSaveRef.current.isShowErrMsg = false; + if (!updateTimerRef.current) { resetTimer(); } } catch (error) { - setReceive(''); - valueSaveRef.current.receive = ''; - stopInterval(); - setErrMsg(''); console.log('error', error); } }; @@ -263,7 +260,7 @@ export default function Buy() { stopInterval(); setPage(e.target.value); // BUY - valueSaveRef.current = initValueSave; + valueSaveRef.current = { ...initValueSave }; valueSaveRef.current.side = e.target.value; setAmount(initCurrency); // SELL @@ -274,6 +271,7 @@ export default function Buy() { setCurFiat(initFiat); setErrMsg(''); + valueSaveRef.current.isShowErrMsg = false; setReceive(''); valueSaveRef.current.receive = ''; setRate(''); From 2b1965d8ab272e9977b1e0878aa1207c49c36f2b Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 16 Jun 2023 18:02:43 +0800 Subject: [PATCH 179/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20send=20t?= =?UTF-8?q?ransaction=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../approval/ApprovalController.ts | 6 +- .../methodController/AELFMethodController.ts | 8 ++ .../app/web/pages/SendTransactions/index.tsx | 106 +++++++++++------- .../app/web/utils/storage/storage.ts | 2 + 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts index 29ab693cc0..84a2fd0679 100644 --- a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts +++ b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts @@ -105,7 +105,11 @@ export default class ApprovalController { * */ // TODO format params - async authorizedToSendTransactions(params: any): Promise { + async authorizedToSendTransactions(params: { + origin: string; + transactionInfoId: string; + payload: any; + }): Promise { return this.notificationService.openPrompt({ method: PromptRouteTypes.SEND_TRANSACTION, search: JSON.stringify(params), diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 760e7afa7e..cc3035aef2 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -11,6 +11,8 @@ import { ExtensionDappManager } from './ExtensionDappManager'; import { getSWReduxState } from 'utils/lib/SWGetReduxStore'; import ApprovalController from 'controllers/approval/ApprovalController'; import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; +import { randomId } from '@portkey-wallet/utils'; +import { removeLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; const storeInSW = { getState: getSWReduxState, @@ -277,10 +279,16 @@ export default class AELFMethodController { }, }); + const key = randomId(); + setLocalStorage({ txPayload: { [key]: JSON.stringify(payload.params) } }); + delete message.payload?.params; const result = await this.approvalController.authorizedToSendTransactions({ origin, + transactionInfoId: key, payload: message.payload, }); + // TODO Only support open a window + removeLocalStorage('txPayload'); if (result.error === 200003) return sendResponse({ ...errorHandler(200003), diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index b629ec1a04..c383408443 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -18,17 +18,18 @@ import { callSendMethod } from 'utils/sandboxUtil/sendTransactions'; import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import getTransferFee from './utils/getTransferFee'; import { ResponseCode } from '@portkey/provider-types'; +import { getLocalStorage } from 'utils/storage/chromeStorage'; import './index.less'; export default function SendTransactions() { - const { payload } = usePromptSearch<{ + const { payload, transactionInfoId } = usePromptSearch<{ payload: { chainId: ChainId; contractAddress: string; method: string; rpcUrl: string; - params: any; }; + transactionInfoId: string; }>(); const chainInfo = useCurrentChain(payload?.chainId); const wallet = useCurrentWalletInfo(); @@ -38,7 +39,8 @@ export default function SendTransactions() { const { t } = useTranslation(); const { passwordSeed } = useUserInfo(); const amountInUsdShow = useAmountInUsdShow(); - const [_tokenPriceObject, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); const [fee, setFee] = useState(''); const [errMsg, setErrMsg] = useState(''); const isCAContract = useMemo(() => chainInfo?.caContractAddress === payload?.contractAddress, [chainInfo, payload]); @@ -46,6 +48,7 @@ export default function SendTransactions() { () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), [passwordSeed, wallet.AESEncryptPrivateKey], ); + const [txParams, setTxParams] = useState({}); const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { @@ -59,50 +62,68 @@ export default function SendTransactions() { [amountInUsdShow], ); - const getFee = useCallback(async () => { - if (!privateKey) return; - if (!chainInfo?.endPoint || !wallet?.caHash || !chainInfo.caContractAddress) return; - const method = isCAContract ? payload?.method : 'ManagerForwardCall'; - const paramsOption = isCAContract - ? payload?.params?.paramsOption - : { - caHash: wallet.caHash, - methodName: payload?.method, - contractAddress: payload?.contractAddress, - args: payload?.params?.paramsOption, - }; - const fee = await getTransferFee({ - rpcUrl: chainInfo.endPoint, - chainType: 'aelf', - methodName: method, - paramsOption, - privateKey, - contractAddress: chainInfo.caContractAddress, - }); - if (fee === '--') { - setFee('0'); - setErrMsg('Insufficient funds for transaction fee'); - } else { - setFee(fee); + const getFee = useCallback( + async (txInfo: any) => { + if (!privateKey) return; + if (!chainInfo?.endPoint || !wallet?.caHash || !chainInfo.caContractAddress) return; + const method = isCAContract ? payload?.method : 'ManagerForwardCall'; + const paramsOption = isCAContract + ? txInfo.paramsOption + : { + caHash: wallet.caHash, + methodName: payload?.method, + contractAddress: payload?.contractAddress, + args: txInfo.paramsOption, + }; + const fee = await getTransferFee({ + rpcUrl: chainInfo.endPoint, + chainType: 'aelf', + methodName: method, + paramsOption, + privateKey, + contractAddress: chainInfo.caContractAddress, + }); + if (fee === '--') { + setFee('0'); + setErrMsg('Insufficient funds for transaction fee'); + } else { + setFee(fee); + } + }, + [chainInfo, isCAContract, payload, privateKey, wallet], + ); + + const getTxPayload = useCallback(async () => { + const txPayload = await getLocalStorage<{ [x: string]: any }>('txPayload'); + + if (!txPayload[transactionInfoId]) { + closePrompt({ + ...errorHandler(400001), + data: { code: ResponseCode.ERROR_IN_PARAMS }, + }); + return; } - }, [chainInfo, isCAContract, payload, privateKey, wallet]); + const params = JSON.parse(txPayload[transactionInfoId]); + setTxParams(params); + getFee(params); + }, [getFee, transactionInfoId]); useEffect(() => { - getFee(); - }, [getFee]); + getTxPayload(); + }, [getTxPayload]); useEffect(() => { - const symbol = payload?.params?.paramsOption?.symbol; + const symbol = txParams.paramsOption?.symbol; if (!symbol || !isMainnet) return; if (symbol === 'ELF') { getTokenPrice(symbol); } else { getTokensPrice([symbol, 'ELF']); } - }, [getTokenPrice, getTokensPrice, payload, isMainnet]); + }, [getTokenPrice, getTokensPrice, payload, isMainnet, txParams.paramsOption?.symbol]); const renderAccountInfo = useMemo(() => { - if (payload?.contractAddress || typeof payload.contractAddress !== 'string') return <>; + if (payload?.contractAddress || typeof payload?.contractAddress !== 'string') return <>; return (
    {walletName}
    @@ -115,9 +136,8 @@ export default function SendTransactions() { ); }, [payload, walletName]); - // Transfer - const renderDetail = useMemo(() => { - const { symbol, amount } = payload?.params?.paramsOption || {}; + const renderTransfer = useMemo(() => { + const { symbol, amount } = txParams.paramsOption || {}; const decimals = symbol === 'ELF' ? 8 : 0; return ( @@ -166,10 +186,10 @@ export default function SendTransactions() {
    ); - }, [formatAmountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); + }, [txParams.paramsOption, isMainnet, formatAmountInUsdShow, fee]); const renderMessage = useMemo(() => { - const params = payload?.params?.paramsOption || {}; + const params = txParams.paramsOption || {}; return (
    Message
    @@ -194,7 +214,7 @@ export default function SendTransactions() {
    ); - }, [formatAmountInUsdShow, fee, isMainnet, payload?.params?.paramsOption]); + }, [txParams.paramsOption, fee, isMainnet, formatAmountInUsdShow]); const sendHandler = useCallback(async () => { try { @@ -207,7 +227,7 @@ export default function SendTransactions() { return; } - let paramsOption = payload?.params?.paramsOption; + let paramsOption = txParams.paramsOption; const functionName = isCAContract ? payload?.method : 'ManagerForwardCall'; @@ -237,7 +257,7 @@ export default function SendTransactions() { console.error(error, 'error===detail'); message.error(handleErrorMessage(error)); } - }, [chainInfo, wallet, payload, isCAContract, privateKey]); + }, [chainInfo, wallet.caHash, payload, txParams, isCAContract, privateKey]); return (
    @@ -250,7 +270,7 @@ export default function SendTransactions() {
    Method
    {payload?.method}
    - {payload?.method.toLowerCase() === 'transfer' ? renderDetail : renderMessage} + {payload?.method.toLowerCase() === 'transfer' ? renderTransfer : renderMessage} {errMsg &&
    {errMsg}
    }
    -
    From c5354665b80cbdc150ca1b4ef885bee461002c65 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 19 Jun 2023 12:10:14 +0800 Subject: [PATCH 185/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20loading=20and=20c?= =?UTF-8?q?lose=20btn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/components/ConnectOverlay/index.tsx | 2 +- .../mobile-app-did/js/dapp/components/SignOverlay/index.tsx | 2 +- .../js/dapp/components/TransactionOverlay/index.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 98cf048076..67453f89c3 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -76,7 +76,7 @@ const ConnectModal = (props: ConnectModalType) => { ); return ( - + {t('Wallet')} diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx index d8d3bd1f1f..4d46a21efd 100644 --- a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -47,7 +47,7 @@ const SignModal = (props: SignModalPropsType) => { ); return ( - + Sign Message diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 8a849df645..fbbdb69125 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -268,7 +268,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, }); - if (!TransactionFee && !TransactionFee?.ELF) return setNoEnoughFee(true); + if (!TransactionFee && !TransactionFee?.ELF) setNoEnoughFee(true); setFee(TransactionFee?.ELF || '0'); setIsFetchingFee(false); @@ -303,7 +303,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, [getTokenPrice, getTokensPrice, transactionInfo?.params?.paramsOption?.symbol]); return ( - + {transactionInfo?.method} From 04fdc526e554f3862e80298c55202ba8808a1384 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 19 Jun 2023 18:33:55 +0800 Subject: [PATCH 186/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20add=20=20outter?= =?UTF-8?q?=20modal=20area=20=20press=20callback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/OverlayModal/index.tsx | 17 ++++++++++++++--- .../js/dapp/components/ConnectOverlay/index.tsx | 1 + .../js/dapp/components/SignOverlay/index.tsx | 1 + .../components/TransactionOverlay/index.tsx | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/components/OverlayModal/index.tsx b/packages/mobile-app-did/js/components/OverlayModal/index.tsx index 6d4404b5be..2ca1b4d36d 100644 --- a/packages/mobile-app-did/js/components/OverlayModal/index.tsx +++ b/packages/mobile-app-did/js/components/OverlayModal/index.tsx @@ -32,6 +32,7 @@ export type OverlayModalProps = { autoKeyboardInsets?: boolean; animated?: boolean; enabledNestScrollView?: boolean; + onCloseRequest?: () => void; }; export function OverlayTransformView({ @@ -80,13 +81,23 @@ export default class OverlayModal extends React.Component { } propsStyle && style.push(propsStyle); propsContainerStyle && containerStyle.push(propsContainerStyle); - let overlayView; + let overlayView, currentRef: OverlayInterface | undefined; + const onCloseRequest = props.onCloseRequest; + if (onCloseRequest) { + props.onCloseRequest = () => { + currentRef ? currentRef.close?.() : OverlayModal.hide(); + onCloseRequest(); + }; + } if (position === 'bottom') { overlayView = ( elements.push(v)} + ref={(v: OverlayInterface) => { + currentRef = v; + elements.push(v); + }} {...props}> {component} @@ -105,7 +116,7 @@ export default class OverlayModal extends React.Component { ); } - return Overlay.show(overlayView); + return Overlay.show(overlayView) as number; } static hide() { diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 67453f89c3..3ad4ea6f3b 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -106,6 +106,7 @@ export const showConnectModal = (props: ConnectModalType) => { OverlayModal.show(, { position: 'bottom', enabledNestScrollView: true, + onCloseRequest: props.onReject, }); }; diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx index 4d46a21efd..885a21aaa6 100644 --- a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -61,6 +61,7 @@ export const showSignModal = (props: SignModalPropsType) => { OverlayModal.show(, { position: 'bottom', enabledNestScrollView: true, + onCloseRequest: props.onReject, }); }; diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index fbbdb69125..8e6b19687f 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -326,6 +326,7 @@ export const showTransactionModal = (props: TransactionModalPropsType) => { OverlayModal.show(, { position: 'bottom', enabledNestScrollView: true, + onCloseRequest: props.onReject, }); }; From 1edf60b96ccafa093c03ef7393b861ed392c189b Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 20 Jun 2023 10:53:15 +0800 Subject: [PATCH 187/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20change=20por?= =?UTF-8?q?tkeyFinanceUrl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/backend-network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/constants/constants-ca/backend-network.ts b/packages/constants/constants-ca/backend-network.ts index c97ddfbf6f..bc132f7e59 100644 --- a/packages/constants/constants-ca/backend-network.ts +++ b/packages/constants/constants-ca/backend-network.ts @@ -57,7 +57,7 @@ export const BackEndNetWorkMap: { connectUrl: 'https://auth-portkey.portkey.finance', cmsUrl: 'https://cms.portkey.finance/graphql', s3Url: 'https://portkey-cms-mainnet.s3.ap-northeast-1.amazonaws.com', - portkeyFinanceUrl: 'https://test.portkey.finance', // TODO: change to https://portkey.finance + portkeyFinanceUrl: 'https://portkey.finance', buyConfig: { ach: { appId: 'P0e0l39jipsNYT46', From 28233d541bc8cb7951ce7c2f7e28b8793b9d4974 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 20 Jun 2023 11:22:43 +0800 Subject: [PATCH 188/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20deny=20android=20?= =?UTF-8?q?user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/app/src/main/res/xml/network_security_config.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml b/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml index 5858b57488..26d031a557 100644 --- a/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml +++ b/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml @@ -3,7 +3,6 @@ - - + From 8a46f99dcb73f046ae8011d9cda0e3fa3af00b6f Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 20 Jun 2023 14:32:48 +0800 Subject: [PATCH 189/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20webview?= =?UTF-8?q?=20=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/BrowserTab/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 60fba8b8be..13104e6c4b 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -54,7 +54,6 @@ export default memo(BrowserTab, (prevProps: BrowserTabProps, nextProps: BrowserT export const styles = StyleSheet.create({ webViewContainerHidden: { - flex: 0, opacity: 0, display: 'none', width: 0, @@ -62,7 +61,6 @@ export const styles = StyleSheet.create({ position: 'relative', }, webViewContainer: { - width: screenWidth, - height: screenHeight, + flex: 1, }, }); From d0873b437f4d301383e101bf35a51d4921576e9c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 20 Jun 2023 14:38:12 +0800 Subject: [PATCH 190/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20format=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/app/src/main/res/xml/network_security_config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml b/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml index 26d031a557..d7b4192e18 100644 --- a/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml +++ b/packages/mobile-app-did/android/app/src/main/res/xml/network_security_config.xml @@ -3,6 +3,6 @@ - + From 6c8d37ea8cbd3685875a94afb1509576f7c337cf Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 20 Jun 2023 16:45:38 +0800 Subject: [PATCH 191/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20logout=20close=20?= =?UTF-8?q?drawer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/useLogOut.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/hooks/useLogOut.ts b/packages/mobile-app-did/js/hooks/useLogOut.ts index 6f7f9ef537..015759687d 100644 --- a/packages/mobile-app-did/js/hooks/useLogOut.ts +++ b/packages/mobile-app-did/js/hooks/useLogOut.ts @@ -25,7 +25,7 @@ import { useGetChainInfo } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { ChainId } from '@portkey-wallet/types'; import { getWalletInfo, isCurrentCaHash } from 'utils/redux'; import { resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; -import { resetDiscover } from '@portkey-wallet/store/store-ca/discover/slice'; +import { changeDrawerOpenStatus, resetDiscover } from '@portkey-wallet/store/store-ca/discover/slice'; export default function useLogOut() { const dispatch = useAppDispatch(); @@ -38,6 +38,7 @@ export default function useLogOut() { resetStore(); dispatch(resetDappList(currentNetwork)); dispatch(resetDiscover(currentNetwork)); + dispatch(changeDrawerOpenStatus(false)); if (otherNetworkLogged) { dispatch(resetCaInfo(currentNetwork)); From e260c3476d82926545d7adb0cb08a9b83c04aebd Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 20 Jun 2023 16:53:10 +0800 Subject: [PATCH 192/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20providers=2025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/dapp/dappMobileOperator.ts | 2 +- .../mobile-app-did/js/dapp/mobileStream.ts | 2 +- packages/mobile-app-did/package.json | 8 +-- packages/types/package.json | 2 +- packages/utils/package.json | 6 +- packages/web-extension-did/package.json | 8 +-- yarn.lock | 62 +++++++++---------- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 090a29dc66..a424e06f99 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -15,7 +15,7 @@ import DappEventBus from './dappEventBus'; import { generateNormalResponse, generateErrorResponse } from '@portkey/provider-utils'; import { IDappManager } from '@portkey-wallet/types/types-ca/dapp'; import { IDappOverlay } from './dappOverlay'; -import { Operator } from '@portkey/providers/dist/operator'; +import { Operator } from '@portkey/providers'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getManagerAccount, getPin } from 'utils/redux'; diff --git a/packages/mobile-app-did/js/dapp/mobileStream.ts b/packages/mobile-app-did/js/dapp/mobileStream.ts index 0ef4ce664f..df3495e06c 100644 --- a/packages/mobile-app-did/js/dapp/mobileStream.ts +++ b/packages/mobile-app-did/js/dapp/mobileStream.ts @@ -1,4 +1,4 @@ -import { DappInteractionStream } from '@portkey/providers/dist/dappStream'; +import { DappInteractionStream } from '@portkey/providers'; import type WebView from 'react-native-webview'; export class MobileStream extends DappInteractionStream { private _webViewRef: WebView; diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index b6447e8866..d39ba652dc 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -51,10 +51,10 @@ "@portkey-wallet/i18n": "^0.0.1", "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/mobile-provider": "0.0.1-alpha.24", - "@portkey/provider-types": "0.0.1-alpha.24", - "@portkey/provider-utils": "0.0.1-alpha.24", - "@portkey/providers": "0.0.1-alpha.24", + "@portkey/mobile-provider": "0.0.1-alpha.25", + "@portkey/provider-types": "0.0.1-alpha.25", + "@portkey/provider-utils": "0.0.1-alpha.25", + "@portkey/providers": "0.0.1-alpha.25", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index 2f76d29b75..05e567338d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "version": "0.0.1", "type": "commonjs", "dependencies": { - "@portkey/provider-types": "0.0.1-alpha.24" + "@portkey/provider-types": "0.0.1-alpha.25" }, "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/utils/package.json b/packages/utils/package.json index efb9c67052..bd04ba936e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,8 +23,8 @@ "expo-local-authentication": "^12.3.0", "expo-secure-store": "^11.3.0", "@react-native-async-storage/async-storage": "^1.17.6", - "@portkey/provider-types": "0.0.1-alpha.24", - "@portkey/provider-utils": "0.0.1-alpha.24", - "@portkey/providers": "0.0.1-alpha.24" + "@portkey/provider-types": "0.0.1-alpha.25", + "@portkey/provider-utils": "0.0.1-alpha.25", + "@portkey/providers": "0.0.1-alpha.25" } } diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index ae2cef0995..58245d4364 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^0.0.1", "@portkey-wallet/types": "^0.0.1", "@portkey-wallet/utils": "^0.0.1", - "@portkey/extension-provider": "0.0.1-alpha.24", - "@portkey/provider-utils": "0.0.1-alpha.24", - "@portkey/provider-types": "0.0.1-alpha.24", - "@portkey/providers": "0.0.1-alpha.24", + "@portkey/extension-provider": "0.0.1-alpha.25", + "@portkey/provider-utils": "0.0.1-alpha.25", + "@portkey/provider-types": "0.0.1-alpha.25", + "@portkey/providers": "0.0.1-alpha.25", "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index dc6d308fae..d13190f088 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,51 +5184,51 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/chain@^0.0.1-alpha.24": - version "0.0.1-alpha.24" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.24.tgz#f68a663b9c6e4b54abdffb6e5b8ac182ef0c30bb" - integrity sha512-ESCSHYFX8+cuX/j+KnafR8/K9E1xik1IYYQQc6gKB38mlClGeZvhO7KpX9ywfV9qF/a131+xQfn36v5xTpl60w== +"@portkey/chain@^0.0.1-alpha.25": + version "0.0.1-alpha.25" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.25.tgz#1fb79d5dc56067faed534fdfb639350c6f0e8e26" + integrity sha512-0dwAfghdWUWZYwQZoUyOl7yHvzVUUkd8+JJIbM2srG3c9AEdRticG18fNQvUARAUseEX6O1bd1lzXmlMMutfLg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.24" + "@portkey/provider-types" "^0.0.1-alpha.25" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.24": - version "0.0.1-alpha.24" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.24.tgz#89df205929f66895744adaf6bd9b6dd99a68323d" - integrity sha512-OgCcUJOrxaMTN2nPCdIjTYlq6Du4UTdQSeodopFNc2Bznz0IWiq/Y0vTZkt9R0yAchuk+JBTVqOHC5C+ze1LvA== +"@portkey/extension-provider@0.0.1-alpha.25": + version "0.0.1-alpha.25" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.25.tgz#c6931098315cc1e12e551a9758064dbc2a778555" + integrity sha512-hVBsvINepbqrnR/7CvxJDOfiZ8F4joAzr7uOGB28Fg/VFHpjonWpdk6/sw4hZ0jG+gmYJi/LMjj3t5xW3znfQQ== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.24" - "@portkey/providers" "^0.0.1-alpha.24" + "@portkey/provider-types" "^0.0.1-alpha.25" + "@portkey/providers" "^0.0.1-alpha.25" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/mobile-provider@0.0.1-alpha.24": - version "0.0.1-alpha.24" - resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.24.tgz#99e161a0a8ced345d24363da8a696989349cb187" - integrity sha512-zY3sGPmbPRqlX3SoA1XDrq52vmo+rwXrXPRvqui+P2CPscyOlVzgRCObbsgzdw2Utx0Bu/44NqIFdm7CtaaRaA== +"@portkey/mobile-provider@0.0.1-alpha.25": + version "0.0.1-alpha.25" + resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.25.tgz#e7bf00dd87a3ac4bef5595085ac3051fbe2842a2" + integrity sha512-aOqWkFqqtgYr1IFTQVVIdgkt4q3SCLpiX5NcIuO9YVFJzM4suTIJoMfC8Ag0Jtwklrm50Bl1KRji0MBIHPcrfQ== -"@portkey/provider-types@0.0.1-alpha.24", "@portkey/provider-types@^0.0.1-alpha.24": - version "0.0.1-alpha.24" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.24.tgz#47cbcffdbe680fc7e6b82a614a329ba1c219c4dd" - integrity sha512-7tdAY+mQkcxc1LXW3twODAJTnafdEjHKtwOpzK4H5co4glITYMMH8jEmcCRfxS6hSFeJeV7Io4N1eGIgylf/sg== +"@portkey/provider-types@0.0.1-alpha.25", "@portkey/provider-types@^0.0.1-alpha.25": + version "0.0.1-alpha.25" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.25.tgz#d1de731f939e4cee8dba188f7128c5bf7cf2527a" + integrity sha512-0rQh9qUMytI3ILFdavCFiOVvteJif7BLRIUt4ojC4Ae6cQYclKYHsXnBZifQw8tJjR0aPIN6tBsLNBs2FdHAXw== dependencies: "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.24", "@portkey/provider-utils@^0.0.1-alpha.24": - version "0.0.1-alpha.24" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.24.tgz#fde4f7cd09f6f6d17ae54014f6299cd306a900f1" - integrity sha512-yC8r4Z1Jy2fABu/RV936MBzi56zqgO6d0M9q9k+r+y/o/ThWBGWWkvypUKGzh6aVmjlL0SmP+CQ0gGWqhumOdg== +"@portkey/provider-utils@0.0.1-alpha.25", "@portkey/provider-utils@^0.0.1-alpha.25": + version "0.0.1-alpha.25" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.25.tgz#072872ee290bf329daacdaef3786eb6d55b70763" + integrity sha512-S7bGEdhOqUTR7BhBoe67rJ3umy/Z0drJf5AK53huaLs8VGo8ll14Zo9hm9GGkHec3BIkBkuqTUmJziE6EaELtg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.24" + "@portkey/provider-types" "^0.0.1-alpha.25" -"@portkey/providers@0.0.1-alpha.24", "@portkey/providers@^0.0.1-alpha.24": - version "0.0.1-alpha.24" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.24.tgz#a7f72aff434ca5a129049bc1064a035757e6c82e" - integrity sha512-OPgwrc7GBJhNxrxesTZI1wXA78PYW71u36fMyszYu1r2I9bRUzs+JsjknCMLbX1SSsU+ro7W5clrN7Sejey6uA== +"@portkey/providers@0.0.1-alpha.25", "@portkey/providers@^0.0.1-alpha.25": + version "0.0.1-alpha.25" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.25.tgz#f083a81f9ac38207cc62e6f986eba1c3946969fd" + integrity sha512-MDVjkCM0DsV4/zKP0GU4SUGBnB18Bu28NSHNBKsP8PvSQC9fPKlZqGb9nedyReKdMg2Lnf74WgH0Vbhdf9c6mw== dependencies: - "@portkey/chain" "^0.0.1-alpha.24" - "@portkey/provider-types" "^0.0.1-alpha.24" - "@portkey/provider-utils" "^0.0.1-alpha.24" + "@portkey/chain" "^0.0.1-alpha.25" + "@portkey/provider-types" "^0.0.1-alpha.25" + "@portkey/provider-utils" "^0.0.1-alpha.25" "@types/readable-stream" "^2.3.15" lodash "^4.17.21" readable-stream "^4.4.0" From c53dc96cad6e0ff743681ffdbbef2afe62dafa86 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 20 Jun 2023 17:27:59 +0800 Subject: [PATCH 193/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20android=20injectJ?= =?UTF-8?q?avaScript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/ProviderWebview/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx index d49d0bbc5a..8d15dde44a 100644 --- a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx +++ b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx @@ -29,6 +29,7 @@ const ProviderWebview = forwardRef(function const getEntryScriptWeb3 = async () => { const script = await EntryScriptWeb3.get(); setEntryScriptWeb3(script); + if (!isIos) webViewRef.current?.injectJavaScript(script); }; getEntryScriptWeb3(); @@ -39,10 +40,9 @@ const ProviderWebview = forwardRef(function const initOperator = useCallback( (origin: string) => { - if (!isIos) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - webViewRef.current?.injectJavaScript(entryScriptWeb3!); - } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (!isIos) webViewRef.current?.injectJavaScript(entryScriptWeb3!); + operatorRef.current = new DappMobileOperator({ origin, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion From 4b7dce6c449a5558f3ff91d6b44fb3cf19d2adcc Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 20 Jun 2023 19:53:54 +0800 Subject: [PATCH 194/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20onAchTx?= =?UTF-8?q?AddressReceived=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/network.ts | 2 +- packages/hooks/hooks-ca/payment.ts | 2 +- packages/socket/socket-sell/index.ts | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/constants/constants-ca/network.ts b/packages/constants/constants-ca/network.ts index acc046ba08..cedcba6d2e 100644 --- a/packages/constants/constants-ca/network.ts +++ b/packages/constants/constants-ca/network.ts @@ -1 +1 @@ -export * from './network-test1'; +export * from './network-mainnet'; diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 9e44faed74..5bdbb39306 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -56,7 +56,7 @@ export const useSellTransfer = () => { resolve(null); }, SELL_SOCKET_TIMEOUT), ); - const signalrSellPromise = new Promise(resolve => { + const signalrSellPromise = new Promise(resolve => { const { remove } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, data => { resolve(data); remove(); diff --git a/packages/socket/socket-sell/index.ts b/packages/socket/socket-sell/index.ts index 901f11ea29..cb482eaad5 100644 --- a/packages/socket/socket-sell/index.ts +++ b/packages/socket/socket-sell/index.ts @@ -13,14 +13,13 @@ export class SignalrSell extends Signalr { public onAchTxAddressReceived( { orderId }: { clientId: string; orderId: string }, - callback: (data: AchTxAddressReceivedType) => void, + callback: (data: AchTxAddressReceivedType | null) => void, ) { return this.listen('onAchTxAddressReceived', (data: { body: AchTxAddressReceivedType }) => { - console.log('onAchTxAddressReceived: data', data); if (data?.body?.orderId === orderId) { callback(data.body); } else { - throw new Error('onAchTxAddressReceived error'); + callback(null); } }); } From b8e7e1bea07728aaf3af322bdefe3ecaeb364bde Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 21 Jun 2023 10:47:57 +0800 Subject: [PATCH 195/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20sell=20fait?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Buy/components/BuyFrom/index.tsx | 5 ++++ .../Buy/components/CurrencyInput/index.tsx | 4 ++++ .../Buy/components/CustomDrawer/index.tsx | 4 ++++ .../Buy/components/CustomModal/index.tsx | 4 ++++ .../pages/Buy/components/SelectList/index.tsx | 24 +++++++++++++++---- .../pages/Buy/components/SellFrom/index.tsx | 3 +++ .../Buy/components/SuffixSelect/index.tsx | 6 ++++- .../pages/Buy/components/TokenInput/index.tsx | 13 +++++++++- .../app/web/pages/Buy/index.tsx | 2 ++ 9 files changed, 58 insertions(+), 7 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx index c584f4a4c6..2449ef600d 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx @@ -2,6 +2,7 @@ import { PartialFiatType } from 'pages/Buy/const'; import CurrencyInput from '../CurrencyInput'; import TokenInput, { ICurToken } from '../TokenInput'; import { IKeyDownParams } from 'types'; +import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; export interface IBuyOrSellFromProps { currencyVal: string; @@ -17,6 +18,7 @@ export interface IBuyOrSellFromProps { curToken: ICurToken; errMsg: string; + side: PaymentTypeEnum; } export default function BuyFrom({ @@ -33,6 +35,7 @@ export default function BuyFrom({ curToken, errMsg, + side, }: IBuyOrSellFromProps) { return ( <> @@ -40,6 +43,7 @@ export default function BuyFrom({
    {`I want to pay`}
    void; readOnly: boolean; onKeyDown: (e: IKeyDownParams) => void; @@ -17,6 +19,7 @@ export interface ICurrencyInputProps { export default function CurrencyInput({ value, + side, onChange, readOnly, onKeyDown, @@ -45,6 +48,7 @@ export default function CurrencyInput({ /> setOpenDrawer(false)} onSelect={onSelect} diff --git a/packages/web-extension-did/app/web/pages/Buy/components/CustomDrawer/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/CustomDrawer/index.tsx index bf7a109bb7..627dec2dab 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/CustomDrawer/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/CustomDrawer/index.tsx @@ -3,12 +3,14 @@ import BaseDrawer from 'components/BaseDrawer'; import { DrawerType } from 'pages/Buy/const'; import SelectList from '../SelectList'; import './index.less'; +import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; interface CustomSelectProps extends DrawerProps { onChange?: (v: any) => void; onClose?: () => void; searchPlaceHolder?: string; drawerType: DrawerType; + side: PaymentTypeEnum; } export default function CustomDrawer({ @@ -17,6 +19,7 @@ export default function CustomDrawer({ title, searchPlaceHolder, drawerType, + side, ...props }: CustomSelectProps) { return ( @@ -24,6 +27,7 @@ export default function CustomDrawer({ void; onClose: () => void; searchPlaceHolder?: string; drawerType: DrawerType; + side: PaymentTypeEnum; } export default function CustomModal({ @@ -17,6 +19,7 @@ export default function CustomModal({ title, searchPlaceHolder, drawerType, + side, ...props }: CustomSelectProps) { return ( @@ -24,6 +27,7 @@ export default function CustomModal({ void; @@ -14,6 +16,7 @@ export interface ICustomTokenListProps { title?: ReactNode; searchPlaceHolder?: string; drawerType: DrawerType; + side: PaymentTypeEnum; } const tokenList = [ @@ -23,21 +26,32 @@ const tokenList = [ }, ]; -export default function CustomList({ onChange, onClose, title, searchPlaceHolder, drawerType }: ICustomTokenListProps) { +export default function CustomList({ + onChange, + onClose, + title, + searchPlaceHolder, + drawerType, + side, +}: ICustomTokenListProps) { const { t } = useTranslation(); const [openDrop, setOpenDrop] = useState(false); const [filterWord, setFilterWord] = useState(''); const isTestNet = useIsTestnet(); - const { buyFiatList } = usePayment(); + const { buyFiatList, sellFiatList } = usePayment(); + const fiatList: FiatType[] = useMemo(() => { + return side === PaymentTypeEnum.SELL ? sellFiatList : buyFiatList; + }, [buyFiatList, sellFiatList, side]); + const showFiatList = useMemo(() => { return filterWord === '' - ? buyFiatList - : buyFiatList.filter( + ? fiatList + : fiatList.filter( (item) => item.currency.toLowerCase().includes(filterWord.toLowerCase()) || item.countryName?.toLowerCase().includes(filterWord.toLowerCase()), ); - }, [buyFiatList, filterWord]); + }, [fiatList, filterWord]); const showTokenList = useMemo(() => { return filterWord === '' ? tokenList : tokenList.filter((item) => filterWord === item.symbol); }, [filterWord]); diff --git a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx index 00d4104ef3..f2dbadbf3b 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx @@ -16,6 +16,7 @@ export default function SellFrom({ curToken, errMsg, + side, }: IBuyOrSellFromProps) { const tokenChange = (val: string) => { const arr = val.split('.'); @@ -33,6 +34,7 @@ export default function SellFrom({
    {`I want to sell`}
    {`I will receive≈`}
    void; onSelect: (v: PartialFiatType) => void; } @@ -16,7 +18,7 @@ const SelectCurrency = 'Select Currency'; const SearchCrypto = 'Search crypto'; const SearchCurrency = 'Search currency'; -export default function SuffixSelect({ drawerType, open, onClose, onSelect }: ISuffixSelectProps) { +export default function SuffixSelect({ drawerType, open, side, onClose, onSelect }: ISuffixSelectProps) { const { isPrompt } = useCommonState(); const title = useMemo(() => (drawerType === DrawerType.token ? SelectCrypto : SelectCurrency), [drawerType]); const searchPlaceHolder = useMemo( @@ -28,6 +30,7 @@ export default function SuffixSelect({ drawerType, open, onClose, onSelect }: IS open={open} drawerType={drawerType} title={title} + side={side} searchPlaceHolder={searchPlaceHolder} onClose={onClose} onChange={onSelect} @@ -37,6 +40,7 @@ export default function SuffixSelect({ drawerType, open, onClose, onSelect }: IS open={open} drawerType={drawerType} title={title} + side={side} searchPlaceHolder={searchPlaceHolder} height="528" maskClosable={true} diff --git a/packages/web-extension-did/app/web/pages/Buy/components/TokenInput/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/TokenInput/index.tsx index 9281ea52d0..255125857c 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/TokenInput/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/TokenInput/index.tsx @@ -4,6 +4,7 @@ import { DrawerType, PartialFiatType } from 'pages/Buy/const'; import { useState } from 'react'; import SuffixSelect from '../SuffixSelect'; import { IKeyDownParams } from 'types'; +import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; export interface ICurToken { crypto: string; @@ -12,6 +13,7 @@ export interface ICurToken { export interface ITokenInputProps { value: string; + side: PaymentTypeEnum; onChange: (val: string) => void; readOnly: boolean; onKeyDown: (e: IKeyDownParams) => void; @@ -19,7 +21,15 @@ export interface ITokenInputProps { onSelect: (v: PartialFiatType) => void; } -export default function TokenInput({ value, onChange, readOnly, onKeyDown, curToken, onSelect }: ITokenInputProps) { +export default function TokenInput({ + value, + side, + onChange, + readOnly, + onKeyDown, + curToken, + onSelect, +}: ITokenInputProps) { const [openDrawer, setOpenDrawer] = useState(false); return ( @@ -40,6 +50,7 @@ export default function TokenInput({ value, onChange, readOnly, onKeyDown, curTo /> setOpenDrawer(false)} onSelect={onSelect} diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 780e1cb246..b1f91ff990 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -424,6 +424,7 @@ export default function Buy() { handleTokenSelect={(v) => handleSelect(v, DrawerType.token)} curToken={curToken} errMsg={errMsg} + side={PaymentTypeEnum.BUY} /> )} {page === PaymentTypeEnum.SELL && ( @@ -439,6 +440,7 @@ export default function Buy() { handleCurrencySelect={(v) => handleSelect(v, DrawerType.currency)} curFiat={curFiat} errMsg={errMsg} + side={PaymentTypeEnum.SELL} /> )} {rate !== '' && renderRate} From 6c5ef60bf87a781c2ba485c4a846645859e140b0 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 21 Jun 2023 11:09:00 +0800 Subject: [PATCH 196/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20fix=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controllers/methodController/AELFMethodController.ts | 5 ++++- packages/web-extension-did/app/web/types/SW.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index de6e818563..92823d0d09 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -223,7 +223,10 @@ export default class AELFMethodController { }); return sendResponse({ ...errorHandler(0), data: await this.dappManager.accounts(message.origin) }); } - const result = await this.approvalController.authorizedToConnect(message); + const result = await this.approvalController.authorizedToConnect({ + ...message, + appLogo: message?.icon || '', + }); if (result.error === 200003) return sendResponse({ ...errorHandler(200003, 'User denied'), diff --git a/packages/web-extension-did/app/web/types/SW.ts b/packages/web-extension-did/app/web/types/SW.ts index 82cbbb896c..195d29ba7c 100644 --- a/packages/web-extension-did/app/web/types/SW.ts +++ b/packages/web-extension-did/app/web/types/SW.ts @@ -31,6 +31,7 @@ export interface BaseRequestMessagePayload { href: string; method: string; origin: string; + icon?: string; } export interface RequestMessagePayload extends BaseRequestMessagePayload { From f506b4ff4be4109c0a66aa65565b14d91b86a829 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 21 Jun 2023 12:54:52 +0800 Subject: [PATCH 197/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?transferMethod?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 9 +++++++-- .../ViewOnWebView/hooks/useHandleAchSell.ts | 15 +++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 5bdbb39306..afc5fc116b 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -44,6 +44,7 @@ export const useSellTransfer = () => { if (!isMainnet || merchantName !== ACH_MERCHANT_NAME) return; let achTxAddressReceived: AchTxAddressReceivedType; + let signalrSellRemove: (() => void) | undefined; try { const clientId = randomId(); await signalrSell.doOpen({ @@ -59,13 +60,13 @@ export const useSellTransfer = () => { const signalrSellPromise = new Promise(resolve => { const { remove } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, data => { resolve(data); - remove(); }); + signalrSellRemove = remove; signalrSell.requestAchTxAddress(clientId, orderId); }); const signalrSellResult = await Promise.race([timerPromise, signalrSellPromise]); - signalrSell.stop(); + if (signalrSellResult === null) { throw new Error('Transaction failed.'); } @@ -75,6 +76,10 @@ export const useSellTransfer = () => { code: 'TIMEOUT', message: 'Transaction failed.', }; + } finally { + signalrSellRemove?.(); + signalrSellRemove = undefined; + signalrSell.stop(); } try { diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index e01db6e706..e52969c441 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -11,7 +11,7 @@ import Loading from 'components/Loading'; import { usePin } from 'hooks/store'; import { useCallback, useMemo } from 'react'; import { getManagerAccount } from 'utils/redux'; -import sameChainTransfer from 'utils/transfer/sameChainTransfer'; +import { managerTransfer } from 'utils/transfer/managerTransfer'; export const useHandleAchSell = () => { const sellTransfer = useSellTransfer(); @@ -53,12 +53,15 @@ export const useHandleAchSell = () => { }); const amount = timesDecimals(params.cryptoAmount, decimals).toNumber(); - return await sameChainTransfer({ + + return managerTransfer({ contract, - tokenInfo: { ...aelfToken, address: aelfToken.tokenContractAddress || '' }, - caHash: caHash, - amount, - toAddress: `ELF_${params.address}_AELF`, + paramsOption: { + caHash, + symbol: aelfToken.symbol, + to: params.address, + amount, + }, }); }, [aelfToken, chainInfo, pin, wallet], From 6760fc4914824902699a30c67f5abc09923a4cce Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 21 Jun 2023 14:39:41 +0800 Subject: [PATCH 198/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20remove=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/hooks/useInitData.ts | 4 ++-- .../ViewOnWebView/hooks/useHandleAchSell.ts | 23 ++++++++++++------- .../js/pages/Buy/BuyHome/index.tsx | 13 +++++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index 2846c3bc3f..7a81084d28 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -1,6 +1,6 @@ import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { fetchBuyFiatListAsync, fetchSellFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; +import { fetchBuyFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; import { getSymbolImagesAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import { getWalletNameAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useCallback } from 'react'; @@ -23,7 +23,7 @@ export default function useInitData() { // mainnet only if (isMainNetwork) { dispatch(fetchBuyFiatListAsync()); - dispatch(fetchSellFiatListAsync()); + // dispatch(fetchSellFiatListAsync()); } getCurrentCAViewContract(); dispatch(getWalletNameAsync()); diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index e52969c441..8e4a9e0f97 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -11,7 +11,7 @@ import Loading from 'components/Loading'; import { usePin } from 'hooks/store'; import { useCallback, useMemo } from 'react'; import { getManagerAccount } from 'utils/redux'; -import { managerTransfer } from 'utils/transfer/managerTransfer'; +import sameChainTransfer from 'utils/transfer/sameChainTransfer'; export const useHandleAchSell = () => { const sellTransfer = useSellTransfer(); @@ -54,14 +54,21 @@ export const useHandleAchSell = () => { const amount = timesDecimals(params.cryptoAmount, decimals).toNumber(); - return managerTransfer({ + // return managerTransfer({ + // contract, + // paramsOption: { + // caHash, + // symbol: aelfToken.symbol, + // to: params.address, + // amount, + // }, + // }); + return await sameChainTransfer({ contract, - paramsOption: { - caHash, - symbol: aelfToken.symbol, - to: params.address, - amount, - }, + tokenInfo: { ...aelfToken, address: aelfToken.tokenContractAddress || '' }, + caHash: caHash, + amount, + toAddress: `ELF_${params.address}_AELF`, }); }, [aelfToken, chainInfo, pin, wallet], diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx index 6b590f6892..8f666ac97a 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx @@ -11,6 +11,7 @@ import { FontStyles } from 'assets/theme/styles'; import BuyForm from './components/BuyForm'; import SellForm from './components/SellForm'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import ActionSheet from 'components/ActionSheet'; type TabItemType = { name: string; @@ -47,6 +48,18 @@ export default function BuyHome() { { + if (tabItem.type === PaymentTypeEnum.SELL) { + ActionSheet.alert({ + title2: ( + + Off-ramp is currently not supported. It will be launched in the coming weeks. + + ), + buttons: [{ title: 'OK' }], + }); + return; + } + setSelectTab(tabItem.type); }}> From 59f8426e5fcbc166f4d44215937b7dd3c89faf79 Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 21 Jun 2023 14:58:20 +0800 Subject: [PATCH 199/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20hidden=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Buy/index.tsx | 8 ++++++++ .../app/web/pages/Home/components/MyBalance/index.tsx | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index b1f91ff990..6ab7f07904 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -16,6 +16,7 @@ import { initValueSave, MAX_UPDATE_TIME, PartialFiatType, + sellSoonText, } from './const'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { useCommonState, useLoading } from 'store/Provider/hooks'; @@ -34,6 +35,7 @@ import SellFrom from './components/SellFrom'; import { useEffectOnce } from 'react-use'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import BigNumber from 'bignumber.js'; +import CustomTipModal from 'pages/components/CustomModal'; export default function Buy() { const { t } = useTranslation(); @@ -257,6 +259,12 @@ export default function Buy() { const handlePageChange = useCallback( async (e: RadioChangeEvent) => { + if (e.target.value === PaymentTypeEnum.SELL) { + CustomTipModal({ + content: sellSoonText, + }); + return; + } stopInterval(); setPage(e.target.value); // BUY diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index d43cc3a7e6..3a4541d5a0 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -20,7 +20,7 @@ import { fetchAllTokenListAsync, getSymbolImagesAsync } from '@portkey-wallet/st import { getWalletNameAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import CustomTokenModal from 'pages/components/CustomTokenModal'; import { AccountAssetItem } from '@portkey-wallet/types/types-ca/token'; -import { fetchBuyFiatListAsync, fetchSellFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; +import { fetchBuyFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; import { useFreshTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; import useVerifierList from 'hooks/useVerifierList'; @@ -95,7 +95,7 @@ export default function MyBalance() { useEffect(() => { getGuardianList({ caHash: walletInfo?.caHash }); isMainNet && appDispatch(fetchBuyFiatListAsync()); - isMainNet && appDispatch(fetchSellFiatListAsync()); + // isMainNet && appDispatch(fetchSellFiatListAsync()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMainNet]); From 0c6e9c3090bdb45379df1e94d9213f223336d23d Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Sun, 25 Jun 2023 10:19:41 +0800 Subject: [PATCH 200/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20manager=20sync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/network.ts | 19 +++++--- packages/mobile-app-did/js/hooks/wallet.ts | 38 ++++++++++++++++ packages/store/store-ca/wallet/actions.ts | 4 ++ packages/store/store-ca/wallet/slice.ts | 11 +++++ packages/store/store-ca/wallet/type.ts | 1 + packages/types/types-ca/wallet.ts | 1 + packages/utils/guardian.ts | 2 +- .../web-extension-did/app/web/hooks/wallet.ts | 44 +++++++++++++++++++ 8 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 packages/mobile-app-did/js/hooks/wallet.ts create mode 100644 packages/web-extension-did/app/web/hooks/wallet.ts diff --git a/packages/hooks/hooks-ca/network.ts b/packages/hooks/hooks-ca/network.ts index ae96a5c6dd..ac5ae9af01 100644 --- a/packages/hooks/hooks-ca/network.ts +++ b/packages/hooks/hooks-ca/network.ts @@ -7,8 +7,13 @@ export function useNetworkList() { return NetworkList; } -export function useCurrentNetworkInfo() { +export function useCurrentNetwork() { const { currentNetwork } = useCurrentWallet(); + return useMemo(() => currentNetwork, [currentNetwork]); +} + +export function useCurrentNetworkInfo() { + const currentNetwork = useCurrentNetwork(); const networkList = useNetworkList(); return useMemo( () => networkList.find(item => item.networkType === currentNetwork) || networkList[0], @@ -17,8 +22,8 @@ export function useCurrentNetworkInfo() { } export function useCurrentApiUrl() { - const currentNetwork = useCurrentNetworkInfo(); - return useMemo(() => currentNetwork.apiUrl, [currentNetwork.apiUrl]); + const currentNetworkInfo = useCurrentNetworkInfo(); + return useMemo(() => currentNetworkInfo.apiUrl, [currentNetworkInfo.apiUrl]); } export function useVerifierList() { @@ -27,11 +32,11 @@ export function useVerifierList() { } export function useIsTestnet() { - const currentNetwork = useCurrentNetworkInfo(); - return useMemo(() => currentNetwork.networkType === 'TESTNET', [currentNetwork]); + const currentNetwork = useCurrentNetwork(); + return useMemo(() => currentNetwork === 'TESTNET', [currentNetwork]); } export function useIsMainnet() { - const currentNetwork = useCurrentNetworkInfo(); - return useMemo(() => currentNetwork.networkType === 'MAIN', [currentNetwork]); + const currentNetwork = useCurrentNetwork(); + return useMemo(() => currentNetwork === 'MAIN', [currentNetwork]); } diff --git a/packages/mobile-app-did/js/hooks/wallet.ts b/packages/mobile-app-did/js/hooks/wallet.ts new file mode 100644 index 0000000000..e99797c02e --- /dev/null +++ b/packages/mobile-app-did/js/hooks/wallet.ts @@ -0,0 +1,38 @@ +import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { ChainId } from '@portkey-wallet/types'; +import { useCallback } from 'react'; +import { useGetChainInfo } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useGetHolderInfoByViewContract } from './guardian'; +import { useAppDispatch } from 'store/hooks'; +import { updateCASyncState } from '@portkey-wallet/store/store-ca/wallet/actions'; +export const useCheckManagerSyncState = () => { + const getHolderInfoByViewContract = useGetHolderInfoByViewContract(); + const getChainInfo = useGetChainInfo(); + const walletInfo = useCurrentWalletInfo(); + const dispatch = useAppDispatch(); + return useCallback( + async (chainId: ChainId) => { + try { + const currentCaInfo = walletInfo?.[chainId]; + + if (!currentCaInfo) return false; + if (currentCaInfo?.isSync) return true; + const chainInfo = await getChainInfo(chainId); + const info = await getHolderInfoByViewContract({ caHash: currentCaInfo.caHash }, chainInfo); + + if (info.error) return false; + + const { managerInfos }: { managerInfos: { address: string }[] } = info.data; + if (managerInfos.some(item => item.address === walletInfo.address)) { + dispatch(updateCASyncState({ chainId })); + return true; + } + + return false; + } catch (error) { + return false; + } + }, + [dispatch, getChainInfo, getHolderInfoByViewContract, walletInfo], + ); +}; diff --git a/packages/store/store-ca/wallet/actions.ts b/packages/store/store-ca/wallet/actions.ts index 3f9bb9f574..8651b25caf 100644 --- a/packages/store/store-ca/wallet/actions.ts +++ b/packages/store/store-ca/wallet/actions.ts @@ -54,6 +54,10 @@ export const setCAInfo = createAction<{ networkType?: NetworkType; }>('wallet/setCAInfo'); +export const updateCASyncState = createAction<{ + chainId: ChainId; + networkType?: NetworkType; +}>('wallet/updateCASyncState'); export const setCAInfoType = createAction<{ caInfo: CAInfoType; pin: string; diff --git a/packages/store/store-ca/wallet/slice.ts b/packages/store/store-ca/wallet/slice.ts index c5e8a44e5a..50b0ba13c1 100644 --- a/packages/store/store-ca/wallet/slice.ts +++ b/packages/store/store-ca/wallet/slice.ts @@ -13,6 +13,7 @@ import { setManagerInfo, setOriginChainId, setWalletNameAction, + updateCASyncState, } from './actions'; import { WalletError, WalletState } from './type'; import { changeEncryptStr } from '../../wallet/utils'; @@ -60,6 +61,16 @@ export const walletSlice = createSlice({ [chainId]: caInfo, } as any; }) + .addCase(updateCASyncState, (state, action) => { + const { chainId, networkType } = action.payload; + // check pin + const currentNetwork = networkType || state.currentNetwork || initialState.currentNetwork; + if (!state.walletInfo?.AESEncryptMnemonic) throw new Error(WalletError.noCreateWallet); + const caInfo = state.walletInfo.caInfo[currentNetwork][chainId]; + if (!caInfo) throw new Error(WalletError.caAccountNotExists); + state.walletInfo.caInfo[currentNetwork][chainId] = { ...caInfo, isSync: true }; + }) + .addCase(setManagerInfo, (state, action) => { const { pin, managerInfo } = action.payload; // check pin diff --git a/packages/store/store-ca/wallet/type.ts b/packages/store/store-ca/wallet/type.ts index 7a3f43f914..5e40cc9018 100644 --- a/packages/store/store-ca/wallet/type.ts +++ b/packages/store/store-ca/wallet/type.ts @@ -11,6 +11,7 @@ export enum BaseWalletError { invalidPrivateKey = 'Invalid Private Key', walletExists = 'Wallet Already Exists!', caAccountExists = 'Account Already Exists!', + caAccountNotExists = 'CA Account Not Exists!', } export const WalletError = Object.assign({}, BaseWalletError, PinErrorMessage); diff --git a/packages/types/types-ca/wallet.ts b/packages/types/types-ca/wallet.ts index ed757c80f9..59295c91bf 100644 --- a/packages/types/types-ca/wallet.ts +++ b/packages/types/types-ca/wallet.ts @@ -26,6 +26,7 @@ export type ISocialLogin = LoginKey; export interface CAInfo { caAddress: string; caHash: string; + isSync?: boolean; // TODO: id } export type CAInfoType = { diff --git a/packages/utils/guardian.ts b/packages/utils/guardian.ts index 7afa2d235e..e4e3077b52 100644 --- a/packages/utils/guardian.ts +++ b/packages/utils/guardian.ts @@ -3,9 +3,9 @@ import { UserGuardianItem } from '@portkey-wallet/store/store-ca/guardians/type' import { GuardiansInfo } from '@portkey-wallet/types/types-ca/guardian'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import { VerifierItem } from '@portkey-wallet/types/verifier'; -const APPROVAL_COUNT = ZERO.plus(3).div(5); import BigNumber from 'bignumber.js'; +const APPROVAL_COUNT = ZERO.plus(3).div(5); export function getApprovalCount(length: number) { if (length <= 3) return length; return APPROVAL_COUNT.times(length).dp(0, BigNumber.ROUND_DOWN).plus(1).toNumber(); diff --git a/packages/web-extension-did/app/web/hooks/wallet.ts b/packages/web-extension-did/app/web/hooks/wallet.ts new file mode 100644 index 0000000000..c63ffe354b --- /dev/null +++ b/packages/web-extension-did/app/web/hooks/wallet.ts @@ -0,0 +1,44 @@ +import { useCallback } from 'react'; +import { useGetChainInfo } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { getHolderInfoByContract } from 'utils/sandboxUtil/getHolderInfo'; +import { useAppDispatch } from 'store/Provider/hooks'; +import { ChainId } from '@portkey/provider-types'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { ManagerInfo } from '@portkey-wallet/graphql/contract/__generated__/types'; +import { updateCASyncState } from '@portkey-wallet/store/store-ca/wallet/actions'; + +export const useCheckManagerSyncState = () => { + const getChainInfo = useGetChainInfo(); + const walletInfo = useCurrentWalletInfo(); + const dispatch = useAppDispatch(); + const networkInfo = useCurrentNetworkInfo(); + return useCallback( + async (chainId: ChainId) => { + try { + const currentCaInfo = walletInfo?.[chainId]; + if (!currentCaInfo) return false; + if (currentCaInfo?.isSync) return true; + const chainInfo = await getChainInfo(chainId); + const info = await getHolderInfoByContract({ + rpcUrl: chainInfo.endPoint, + chainType: networkInfo.walletType, + address: chainInfo.caContractAddress, + paramsOption: { + caHash: currentCaInfo.caHash, + }, + }); + if (!info.result) return false; + const { managerInfos } = info.result as { managerInfos: ManagerInfo[] }; + if (managerInfos.some((item) => item.address === walletInfo.address)) { + dispatch(updateCASyncState({ chainId })); + return true; + } + return false; + } catch (error) { + return false; + } + }, + [dispatch, getChainInfo, networkInfo.walletType, walletInfo], + ); +}; From 29c360c5a93febddd5f541c88a2305dec8f6c0b8 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 26 Jun 2023 15:22:53 +0800 Subject: [PATCH 201/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/utils/Logger.ts | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 packages/mobile-app-did/js/utils/Logger.ts diff --git a/packages/mobile-app-did/js/utils/Logger.ts b/packages/mobile-app-did/js/utils/Logger.ts new file mode 100644 index 0000000000..5afbc18ec0 --- /dev/null +++ b/packages/mobile-app-did/js/utils/Logger.ts @@ -0,0 +1,90 @@ +import { addBreadcrumb, captureException, withScope } from '@sentry/react-native'; + +const DEBUG = '@PortkeyDID:'; +const AGREED = 'agreed'; +/** + * Wrapper class that allows us to override + * console.log and console.error and in the future + * we will have flags to do different actions based on + * the environment, for ex. log to a remote server if prod + */ +export default class Logger { + /** + * console.log wrapper + * + * @param {object} args - data to be logged + * @returns - void + */ + static async log(...args: any[]) { + // Check if user passed accepted opt-in to metrics + const metricsOptIn = AGREED; + if (__DEV__) { + args.unshift(DEBUG); + console.log.apply(null, args); + } else if (metricsOptIn === AGREED) { + addBreadcrumb({ + message: JSON.stringify(args), + }); + } + } + /** + * console.error wrapper + * + * @param {Error|string|object} error - error to be logged + * @param {string|object} extra - Extra error info + * @returns - void + */ + static async error(error: any, extra: any) { + // Check if user passed accepted opt-in to metrics + const metricsOptIn = AGREED; + if (__DEV__) { + console.warn(DEBUG, error); + } else if (metricsOptIn === AGREED) { + let exception = error; + + if (!error) { + if (!extra) return console.warn('No error nor extra info provided'); + + const typeExtra = typeof extra; + switch (typeExtra) { + case 'string': + exception = new Error(extra); + break; + case 'object': + if (extra.message) { + exception = new Error(extra.message); + } else { + exception = new Error(JSON.stringify(extra)); + } + break; + default: + exception = new Error('error to capture is not an error instance'); + } + } else if (!(error instanceof Error)) { + const type = typeof error; + switch (type) { + case 'string': + exception = new Error(error); + break; + case 'object': + exception = new Error(JSON.stringify(error)); + break; + default: + exception = new Error('error to capture is not an error instance'); + } + exception.originalError = error; + } + if (extra) { + if (typeof extra === 'string') { + extra = { message: extra }; + } + withScope(scope => { + scope.setExtras(extra); + captureException(exception); + }); + } else { + captureException(exception); + } + } + } +} From 5fa32ef7605efac1151668e1296e95ad77209149 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 26 Jun 2023 15:28:07 +0800 Subject: [PATCH 202/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20buyButton?= =?UTF-8?q?=20GraphQL&hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constants/constants-ca/backend-network.ts | 2 + .../cms/__generated__/hooks/buyButton.ts | 107 ++++++++++++++++++ .../__generated__/hooks/buyButtonCustom.ts | 59 ++++++++++ .../operation/queries/buyButton.gql | 32 ++++++ .../__generated__/operation/queries/index.js | 1 + packages/graphql/cms/__generated__/types.ts | 15 +++ .../graphql/cms/custom/buyButtonCustom.gql | 8 ++ packages/graphql/cms/queries.ts | 17 ++- packages/graphql/schema/cms_schema.graphql | 14 +++ packages/hooks/hooks-ca/cms.ts | 57 +++++++++- packages/store/store-ca/cms/actions.ts | 19 +++- packages/store/store-ca/cms/slice.ts | 12 +- packages/store/store-ca/cms/types.ts | 9 ++ 13 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 packages/graphql/cms/__generated__/hooks/buyButton.ts create mode 100644 packages/graphql/cms/__generated__/hooks/buyButtonCustom.ts create mode 100644 packages/graphql/cms/__generated__/operation/queries/buyButton.gql create mode 100644 packages/graphql/cms/custom/buyButtonCustom.gql diff --git a/packages/constants/constants-ca/backend-network.ts b/packages/constants/constants-ca/backend-network.ts index bc132f7e59..df2777f6a8 100644 --- a/packages/constants/constants-ca/backend-network.ts +++ b/packages/constants/constants-ca/backend-network.ts @@ -26,6 +26,8 @@ export const BackEndNetWorkMap: { apiUrl: 'https://localtest-applesign2.portkey.finance', graphqlUrl: 'http://192.168.67.51:8083/AElfIndexer_DApp/PortKeyIndexerCASchema/graphql', connectUrl: 'http://192.168.67.51:8080', + cmsUrl: 'http://192.168.66.62:8055/graphql', + s3Url: 'https://portkey-cms-dev.s3.ap-northeast-1.amazonaws.com', portkeyFinanceUrl: 'https://portkey-website-dev.vercel.app/', buyConfig: { ach: { diff --git a/packages/graphql/cms/__generated__/hooks/buyButton.ts b/packages/graphql/cms/__generated__/hooks/buyButton.ts new file mode 100644 index 0000000000..614f7e6bdf --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/buyButton.ts @@ -0,0 +1,107 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BuyButtonQueryVariables = Types.Exact<{ [key: string]: never }>; + +export type BuyButtonQuery = { + __typename?: 'Query'; + buyButton?: { + __typename?: 'buyButton'; + date_created?: any | null; + date_updated?: any | null; + id: string; + isBuySectionShow?: boolean | null; + isSellSectionShow?: boolean | null; + status?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; +}; + +export const BuyButtonDocument = gql` + query buyButton { + buyButton { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + isBuySectionShow + isSellSectionShow + status + user_created + user_updated + } + } +`; + +/** + * __useBuyButtonQuery__ + * + * To run a query within a React component, call `useBuyButtonQuery` and pass it any options that fit your needs. + * When your component renders, `useBuyButtonQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBuyButtonQuery({ + * variables: { + * }, + * }); + */ +export function useBuyButtonQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(BuyButtonDocument, options); +} +export function useBuyButtonLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(BuyButtonDocument, options); +} +export type BuyButtonQueryHookResult = ReturnType; +export type BuyButtonLazyQueryHookResult = ReturnType; +export type BuyButtonQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/hooks/buyButtonCustom.ts b/packages/graphql/cms/__generated__/hooks/buyButtonCustom.ts new file mode 100644 index 0000000000..7cb23fa397 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/buyButtonCustom.ts @@ -0,0 +1,59 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type BuyButtonCustomQueryVariables = Types.Exact<{ [key: string]: never }>; + +export type BuyButtonCustomQuery = { + __typename?: 'Query'; + buyButton?: { + __typename?: 'buyButton'; + id: string; + isBuySectionShow?: boolean | null; + isSellSectionShow?: boolean | null; + status?: string | null; + } | null; +}; + +export const BuyButtonCustomDocument = gql` + query buyButtonCustom { + buyButton { + id + isBuySectionShow + isSellSectionShow + status + } + } +`; + +/** + * __useBuyButtonCustomQuery__ + * + * To run a query within a React component, call `useBuyButtonCustomQuery` and pass it any options that fit your needs. + * When your component renders, `useBuyButtonCustomQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useBuyButtonCustomQuery({ + * variables: { + * }, + * }); + */ +export function useBuyButtonCustomQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery(BuyButtonCustomDocument, options); +} +export function useBuyButtonCustomLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery(BuyButtonCustomDocument, options); +} +export type BuyButtonCustomQueryHookResult = ReturnType; +export type BuyButtonCustomLazyQueryHookResult = ReturnType; +export type BuyButtonCustomQueryResult = Apollo.QueryResult; diff --git a/packages/graphql/cms/__generated__/operation/queries/buyButton.gql b/packages/graphql/cms/__generated__/operation/queries/buyButton.gql new file mode 100644 index 0000000000..b281a7053d --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/buyButton.gql @@ -0,0 +1,32 @@ +query buyButton{ + buyButton{ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + isBuySectionShow + isSellSectionShow + status + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/index.js b/packages/graphql/cms/__generated__/operation/queries/index.js index 7e76e89f17..cc08c922f1 100644 --- a/packages/graphql/cms/__generated__/operation/queries/index.js +++ b/packages/graphql/cms/__generated__/operation/queries/index.js @@ -10,6 +10,7 @@ module.exports.bottomSecondMenu_aggregated = fs.readFileSync( path.join(__dirname, 'bottomSecondMenu_aggregated.gql'), 'utf8', ); +module.exports.buyButton = fs.readFileSync(path.join(__dirname, 'buyButton.gql'), 'utf8'); module.exports.deviceBrand = fs.readFileSync(path.join(__dirname, 'deviceBrand.gql'), 'utf8'); module.exports.deviceBrand_by_id = fs.readFileSync(path.join(__dirname, 'deviceBrand_by_id.gql'), 'utf8'); module.exports.deviceBrand_aggregated = fs.readFileSync(path.join(__dirname, 'deviceBrand_aggregated.gql'), 'utf8'); diff --git a/packages/graphql/cms/__generated__/types.ts b/packages/graphql/cms/__generated__/types.ts index db4b9aa6e2..91aafbe85a 100644 --- a/packages/graphql/cms/__generated__/types.ts +++ b/packages/graphql/cms/__generated__/types.ts @@ -28,6 +28,7 @@ export type Query = { bottomSecondMenu: Array; bottomSecondMenu_aggregated: Array; bottomSecondMenu_by_id?: Maybe; + buyButton?: Maybe; deviceBrand: Array; deviceBrand_aggregated: Array; deviceBrand_by_id?: Maybe; @@ -604,6 +605,20 @@ export type BottomSecondMenu_Filter = { user_updated?: InputMaybe; }; +export type BuyButton = { + __typename?: 'buyButton'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + isBuySectionShow?: Maybe; + isSellSectionShow?: Maybe; + status?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + export type Count_Function_Filter_Operators = { count?: InputMaybe; }; diff --git a/packages/graphql/cms/custom/buyButtonCustom.gql b/packages/graphql/cms/custom/buyButtonCustom.gql new file mode 100644 index 0000000000..fc8c84c079 --- /dev/null +++ b/packages/graphql/cms/custom/buyButtonCustom.gql @@ -0,0 +1,8 @@ +query buyButtonCustom { + buyButton { + id + isBuySectionShow + isSellSectionShow + status + } +} diff --git a/packages/graphql/cms/queries.ts b/packages/graphql/cms/queries.ts index 8114f26e15..af59f812aa 100644 --- a/packages/graphql/cms/queries.ts +++ b/packages/graphql/cms/queries.ts @@ -17,6 +17,12 @@ import { DiscoverGroupCustomQueryVariables, } from './__generated__/hooks/discoverGroupCustom'; +import { + BuyButtonCustomDocument, + BuyButtonCustomQuery, + BuyButtonCustomQueryVariables, +} from './__generated__/hooks/buyButtonCustom'; + // SocialMedia const getSocialMedia = async (network: NetworkType, params: SocialMediaCustomQueryVariables) => { const apolloClient = getApolloClient(network); @@ -46,4 +52,13 @@ const getDiscoverGroup = async (network: NetworkType, params: DiscoverGroupCusto return result; }; -export { getSocialMedia, getTabMenu, getDiscoverGroup }; +const getBuyButton = async (network: NetworkType, params: BuyButtonCustomQueryVariables) => { + const apolloClient = getApolloClient(network); + const result = await apolloClient.query({ + query: BuyButtonCustomDocument, + variables: params, + }); + return result; +}; + +export { getSocialMedia, getTabMenu, getDiscoverGroup, getBuyButton }; diff --git a/packages/graphql/schema/cms_schema.graphql b/packages/graphql/schema/cms_schema.graphql index 671d032536..aa055e6a95 100644 --- a/packages/graphql/schema/cms_schema.graphql +++ b/packages/graphql/schema/cms_schema.graphql @@ -5,6 +5,7 @@ type Query { bottomSecondMenu(filter: bottomSecondMenu_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [bottomSecondMenu!]! bottomSecondMenu_by_id(id: ID!): bottomSecondMenu bottomSecondMenu_aggregated(groupBy: [String], filter: bottomSecondMenu_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [bottomSecondMenu_aggregated!]! + buyButton: buyButton deviceBrand(filter: deviceBrand_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [deviceBrand!]! deviceBrand_by_id(id: ID!): deviceBrand deviceBrand_aggregated(groupBy: [String], filter: deviceBrand_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [deviceBrand_aggregated!]! @@ -190,6 +191,19 @@ type bottomSecondMenu_aggregated_fields { type: Float } +type buyButton { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + isBuySectionShow: Boolean + isSellSectionShow: Boolean + status: String + user_created: String + user_updated: String +} + type count_functions { count: Int } diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index 09bebff68e..caae659a0c 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -1,8 +1,12 @@ -import { useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useAppCASelector } from '.'; import { useAppCommonDispatch } from '../index'; import { useCurrentNetworkInfo, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; -import { getDiscoverGroupAsync, getSocialMediaAsync } from '@portkey-wallet/store/store-ca/cms/actions'; +import { + getDiscoverGroupAsync, + getSocialMediaAsync, + getBuyButtonAsync, +} from '@portkey-wallet/store/store-ca/cms/actions'; export const useCMS = () => useAppCASelector(state => state.cms); @@ -60,3 +64,52 @@ export function useDiscoverGroupList(isInit = false) { return discoverGroupList; } + +export const useBuyButton = (isInit = false) => { + const dispatch = useAppCommonDispatch(); + const { buyButtonNetMap } = useCMS(); + const { networkType } = useCurrentNetworkInfo(); + const networkList = useNetworkList(); + + const buyButtonNet = useMemo(() => buyButtonNetMap?.[networkType] || undefined, [networkType, buyButtonNetMap]); + + useEffect(() => { + if (isInit) { + networkList.forEach(item => { + dispatch(getBuyButtonAsync(item.networkType)); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (!isInit) { + dispatch(getBuyButtonAsync(networkType)); + } + }, [dispatch, isInit, networkType]); + + return buyButtonNet; +}; + +export const useBuyButtonShow = () => { + const buyButton = useBuyButton(); + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const isBuyButtonShow = useMemo(() => { + buyButton?.isBuySectionShow || buyButton?.isSellSectionShow; + }, [buyButton]); + + const refreshBuyButton = useCallback(async () => { + const result = await dispatch(getBuyButtonAsync(networkType)); + console.log(result); + return result; + }, [dispatch, networkType]); + + return { + isBuyButtonShow, + isBuySectionShow: buyButton?.isBuySectionShow || false, + isSellSectionShow: buyButton?.isSellSectionShow || false, + refreshBuyButton, + }; +}; diff --git a/packages/store/store-ca/cms/actions.ts b/packages/store/store-ca/cms/actions.ts index 229a88ef4d..b81ecf0315 100644 --- a/packages/store/store-ca/cms/actions.ts +++ b/packages/store/store-ca/cms/actions.ts @@ -1,7 +1,7 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { CMSState } from './types'; import { NetworkType } from '@portkey-wallet/types'; -import { getDiscoverGroup, getSocialMedia, getTabMenu } from '@portkey-wallet/graphql/cms/queries'; +import { getDiscoverGroup, getSocialMedia, getTabMenu, getBuyButton } from '@portkey-wallet/graphql/cms/queries'; export const getSocialMediaAsync = createAsyncThunk>, NetworkType>( 'cms/getSocialMediaAsync', @@ -84,3 +84,20 @@ export const getDiscoverGroupAsync = createAsyncThunk>, NetworkType>( + 'cms/getBuyButtonAsync', + async (network: NetworkType) => { + const result = await getBuyButton(network, {}); + + if (result.data.buyButton) { + return { + buyButtonNetMap: { + [network]: result.data.buyButton, + }, + }; + } else { + throw new Error('discoverGroupListNetMap error'); + } + }, +); diff --git a/packages/store/store-ca/cms/slice.ts b/packages/store/store-ca/cms/slice.ts index c5f9bd71e6..69e0963f22 100644 --- a/packages/store/store-ca/cms/slice.ts +++ b/packages/store/store-ca/cms/slice.ts @@ -1,11 +1,12 @@ import { createSlice } from '@reduxjs/toolkit'; -import { getDiscoverGroupAsync, getSocialMediaAsync, getTabMenuAsync } from './actions'; +import { getDiscoverGroupAsync, getSocialMediaAsync, getTabMenuAsync, getBuyButtonAsync } from './actions'; import { CMSState } from './types'; const initialState: CMSState = { socialMediaListNetMap: {}, tabMenuListNetMap: {}, discoverGroupListNetMap: {}, + buyButtonNetMap: {}, }; export const cmsSlice = createSlice({ name: 'cms', @@ -39,6 +40,15 @@ export const cmsSlice = createSlice({ }) .addCase(getDiscoverGroupAsync.rejected, (_state, action) => { console.log('getDiscoverGroupAsync error', action); + }) + .addCase(getBuyButtonAsync.fulfilled, (state, action) => { + state.buyButtonNetMap = { + ...state.buyButtonNetMap, + ...action.payload.buyButtonNetMap, + }; + }) + .addCase(getBuyButtonAsync.rejected, (_state, action) => { + console.log('getBuyButtonAsync error', action); }); }, }); diff --git a/packages/store/store-ca/cms/types.ts b/packages/store/store-ca/cms/types.ts index 6ddf41af4d..3b7fe83672 100644 --- a/packages/store/store-ca/cms/types.ts +++ b/packages/store/store-ca/cms/types.ts @@ -34,6 +34,12 @@ export interface DiscoverGroup { items: DiscoverItem[]; } +export interface BuyButton { + id: string; + isBuySectionShow: boolean; + isSellSectionShow: boolean; +} + export interface CMSState { socialMediaListNetMap: { [T in NetworkType]?: SocialMediaItem[]; @@ -44,4 +50,7 @@ export interface CMSState { discoverGroupListNetMap: { [T in NetworkType]?: DiscoverGroup[]; }; + buyButtonNetMap?: { + [T in NetworkType]?: BuyButton; + }; } From 95b063890e088da9da6dae6cc7290e257038f175 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 26 Jun 2023 15:53:39 +0800 Subject: [PATCH 203/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20useBuyB?= =?UTF-8?q?uttonShow=20=20isBuyButtonShow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index caae659a0c..d8e820175a 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useMemo } from 'react'; import { useAppCASelector } from '.'; import { useAppCommonDispatch } from '../index'; -import { useCurrentNetworkInfo, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; +import { useCurrentNetworkInfo, useIsMainnet, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; import { getDiscoverGroupAsync, getSocialMediaAsync, @@ -94,11 +94,23 @@ export const useBuyButton = (isInit = false) => { export const useBuyButtonShow = () => { const buyButton = useBuyButton(); const { networkType } = useCurrentNetworkInfo(); + const isMainnet = useIsMainnet(); const dispatch = useAppCommonDispatch(); - const isBuyButtonShow = useMemo(() => { - buyButton?.isBuySectionShow || buyButton?.isSellSectionShow; - }, [buyButton]); + const isBuyButtonShow = useMemo( + () => isMainnet && (buyButton?.isBuySectionShow || buyButton?.isSellSectionShow || false), + [buyButton?.isBuySectionShow, buyButton?.isSellSectionShow, isMainnet], + ); + + const isBuySectionShow = useMemo( + () => isMainnet && (buyButton?.isBuySectionShow || false), + [buyButton?.isBuySectionShow, isMainnet], + ); + + const isSellSectionShow = useMemo( + () => isMainnet && (buyButton?.isSellSectionShow || false), + [buyButton?.isSellSectionShow, isMainnet], + ); const refreshBuyButton = useCallback(async () => { const result = await dispatch(getBuyButtonAsync(networkType)); @@ -108,8 +120,8 @@ export const useBuyButtonShow = () => { return { isBuyButtonShow, - isBuySectionShow: buyButton?.isBuySectionShow || false, - isSellSectionShow: buyButton?.isSellSectionShow || false, + isBuySectionShow, + isSellSectionShow, refreshBuyButton, }; }; From 7b26b5e1d43f5cd0ad24b058c71ccce7218e24cd Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 26 Jun 2023 15:53:39 +0800 Subject: [PATCH 204/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20qr=20code=20scan?= =?UTF-8?q?=20toast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/QrScanner/index.tsx | 8 ++-- packages/mobile-app-did/js/utils/qrcode.ts | 43 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 12d9516ba9..4523eb0965 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -8,7 +8,7 @@ import { defaultColors } from 'assets/theme'; import { useLanguage } from 'i18n/hooks'; import { useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { handleQRCodeData, invalidQRCode, RouteInfoType } from 'utils/qrcode'; +import { handleQRCodeData, invalidQRCode, InvalidQRCodeText, RouteInfoType } from 'utils/qrcode'; import { useFocusEffect, useNavigation } from '@react-navigation/native'; import * as ImagePicker from 'expo-image-picker'; import { TextM } from 'components/CommonText'; @@ -18,7 +18,6 @@ import { isIos, screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/d import { Camera } from 'expo-camera'; import { expandQrData } from '@portkey-wallet/utils/qrCode'; - interface QrScannerProps { route?: any; type?: 'login' | 'send'; @@ -46,12 +45,13 @@ const QrScanner: React.FC = () => { const qrCodeData = expandQrData(JSON.parse(data)); // if not currentNetwork - if (currentNetwork !== qrCodeData.netWorkType) return invalidQRCode(); + if (currentNetwork !== qrCodeData.netWorkType) return invalidQRCode(InvalidQRCodeText.DIFFERENT_NETWORK); + handleQRCodeData(qrCodeData, previousRouteInfo, setRefresh); } } catch (error) { console.log(error); - return invalidQRCode(); + return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); } }, [currentNetwork, previousRouteInfo], diff --git a/packages/mobile-app-did/js/utils/qrcode.ts b/packages/mobile-app-did/js/utils/qrcode.ts index 30fd8fb9f9..8ac443de10 100644 --- a/packages/mobile-app-did/js/utils/qrcode.ts +++ b/packages/mobile-app-did/js/utils/qrcode.ts @@ -12,37 +12,40 @@ export interface RouteInfoType { }; } -export function invalidQRCode() { - CommonToast.fail('Invalid QR code, please re-confirm'); - navigationService.goBack(); +export enum InvalidQRCodeText { + DIFFERENT_NETWORK = 'Please ensure that you are connected to the same network', + INVALID_QR_CODE = 'The QR code is invalid', +} + +export function invalidQRCode(text: InvalidQRCodeText, isBack?: boolean) { + CommonToast.fail(text); + isBack && navigationService.goBack(); } export function handleQRCodeData(data: QRData, previousRouteInfo: RouteInfoType, setRefresh: (v: boolean) => void) { const { type, address, chainType } = data; - if (!isAddress(address, chainType)) return invalidQRCode(); + if (!isAddress(address, chainType)) return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); - if (type !== 'login') { + if (type === 'login') { + navigationService.navigate('ScanLogin', { data: data as LoginQRData }); + } else { // send event - const newData: SendTokenQRDataType = { ...data } as SendTokenQRDataType; - if ( - previousRouteInfo.name === 'SendHome' && - previousRouteInfo.params.assetInfo.symbol !== newData.assetInfo.symbol - ) { - return invalidQRCode(); - } - if (previousRouteInfo.name !== 'Tab') { - const previousAssetsInfo = { ...previousRouteInfo.params.assetInfo }; - navigationService.navigate('SendHome', { - ...newData, - assetInfo: { ...newData.assetInfo, ...previousAssetsInfo }, - }); + if (previousRouteInfo.name === 'SendHome') { + if (previousRouteInfo.params.assetInfo.symbol !== newData.assetInfo.symbol) { + // different symbol + return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE, false); + } else { + const previousAssetsInfo = { ...previousRouteInfo.params.assetInfo }; + navigationService.navigate('SendHome', { + ...newData, + assetInfo: { ...newData.assetInfo, ...previousAssetsInfo }, + }); + } } else { navigationService.navigate('SendHome', newData); } - } else { - navigationService.navigate('ScanLogin', { data: data as LoginQRData }); } setRefresh(true); } From 1af1a6782a1321e96c5c596f88264f342eedfff4 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 26 Jun 2023 16:48:59 +0800 Subject: [PATCH 205/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20type=20?= =?UTF-8?q?BuyButtonType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.ts | 8 ++++++-- packages/store/store-ca/cms/types.ts | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index d8e820175a..72f8b9c77f 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -7,6 +7,7 @@ import { getSocialMediaAsync, getBuyButtonAsync, } from '@portkey-wallet/store/store-ca/cms/actions'; +import { BuyButtonType } from '@portkey-wallet/store/store-ca/cms/types'; export const useCMS = () => useAppCASelector(state => state.cms); @@ -114,8 +115,11 @@ export const useBuyButtonShow = () => { const refreshBuyButton = useCallback(async () => { const result = await dispatch(getBuyButtonAsync(networkType)); - console.log(result); - return result; + const buyButtonResult: BuyButtonType = result?.payload?.buyButtonNetMap?.[networkType] || { + isBuySectionShow: false, + isSellSectionShow: false, + }; + return buyButtonResult; }, [dispatch, networkType]); return { diff --git a/packages/store/store-ca/cms/types.ts b/packages/store/store-ca/cms/types.ts index 3b7fe83672..98c6eafe3a 100644 --- a/packages/store/store-ca/cms/types.ts +++ b/packages/store/store-ca/cms/types.ts @@ -34,8 +34,7 @@ export interface DiscoverGroup { items: DiscoverItem[]; } -export interface BuyButton { - id: string; +export interface BuyButtonType { isBuySectionShow: boolean; isSellSectionShow: boolean; } @@ -51,6 +50,6 @@ export interface CMSState { [T in NetworkType]?: DiscoverGroup[]; }; buyButtonNetMap?: { - [T in NetworkType]?: BuyButton; + [T in NetworkType]?: BuyButtonType; }; } From d70fca2f16e3685ad707afe6eb028827426b6b6b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 26 Jun 2023 17:06:56 +0800 Subject: [PATCH 206/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20guarden?= =?UTF-8?q?=20recover=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/login.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/hooks/login.ts b/packages/mobile-app-did/js/hooks/login.ts index 129969cb60..ea883a5aa9 100644 --- a/packages/mobile-app-did/js/hooks/login.ts +++ b/packages/mobile-app-did/js/hooks/login.ts @@ -105,7 +105,7 @@ export function useOnManagerAddressAndQueryResult() { verifierInfo?: VerifierInfo; guardiansApproved?: GuardiansApproved; }) => { - showLoading && Loading.show({ text: t('Creating address on the chain...') }); + showLoading && Loading.show({ text: t('Working on recovery...') }); await sleep(500); const isRecovery = managerInfo.verificationType === VerificationType.communityRecovery; try { From 7c21138aef99b7d4049afd6f8460cb54748137fb Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 26 Jun 2023 17:29:47 +0800 Subject: [PATCH 207/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20ErrorBoundary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/App.tsx | 43 ++++++++------- .../js/components/ErrorBoundary/index.tsx | 45 ++++++++++++++++ .../js/components/Fallback/index.tsx | 26 ++++++++++ packages/mobile-app-did/js/store/config.ts | 1 + .../js/utils/errorHandler/ExceptionHandler.ts | 40 +------------- .../mobile-app-did/js/utils/errorUtils.ts | 10 ++++ packages/mobile-app-did/js/utils/logBox.ts | 4 ++ packages/utils/ExceptionManager.ts | 6 --- packages/utils/errorBoundary.tsx | 52 +++++++++++++++++++ 9 files changed, 163 insertions(+), 64 deletions(-) create mode 100644 packages/mobile-app-did/js/components/ErrorBoundary/index.tsx create mode 100644 packages/mobile-app-did/js/components/Fallback/index.tsx create mode 100644 packages/mobile-app-did/js/utils/errorUtils.ts create mode 100644 packages/utils/errorBoundary.tsx diff --git a/packages/mobile-app-did/App.tsx b/packages/mobile-app-did/App.tsx index 2a082cc8ac..8259a17937 100644 --- a/packages/mobile-app-did/App.tsx +++ b/packages/mobile-app-did/App.tsx @@ -19,11 +19,12 @@ import TopView from 'rn-teaset/components/Overlay/TopView'; import AppListener from 'components/AppListener/index'; import InterfaceProvider from 'contexts/useInterface'; import GlobalStyleHandler from 'components/GlobalStyleHandler'; +import ErrorBoundary from 'components/ErrorBoundary'; import { lockScreenOrientation } from 'utils/screenOrientation'; import Updater from 'components/Updater'; import CodePush from 'react-native-code-push'; import 'utils/sentryInit'; - +import 'utils/logBox'; const codePushOptions = { updateDialog: false, installMode: CodePush.InstallMode.ON_NEXT_RESTART, @@ -53,25 +54,27 @@ const App = () => { }, []); return ( - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + ); }; diff --git a/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx b/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx new file mode 100644 index 0000000000..113b987e60 --- /dev/null +++ b/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx @@ -0,0 +1,45 @@ +import React, { ReactNode, useCallback } from 'react'; +import { Fallback, IErrorBoundary } from 'components/Fallback'; +import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; +import { Severity } from '@portkey-wallet/utils/ExceptionManager'; +import ReactErrorBoundary from '@portkey-wallet/utils/errorBoundary'; +import * as errorUtils from 'utils/errorUtils'; +export type ErrorBoundaryProps = { + children: ReactNode; + view: string; +}; + +class ReactNativeErrorBoundary extends ReactErrorBoundary { + componentDidMount() { + const originHandler = errorUtils.getGlobalHandler(); + errorUtils.setGlobalHandler((error: any, isFatal?: boolean) => { + if (isFatal && !__DEV__) { + const componentStack = error.stack; + this.setState({ hasError: true, error, componentStack }); + this.props.onError?.(error, componentStack); + return; + } + originHandler?.(error, isFatal); + }); + } +} + +export default function ErrorBoundary({ children, view }: ErrorBoundaryProps) { + const onCaptureException = useCallback( + ({ error, componentStack }: IErrorBoundary) => { + const message = error.toString(); + const sendError = new Error(message); + sendError.stack = componentStack || ''; + sendError.name = `Message:${message}, View:${view}`; + exceptionManager.reportError(sendError, Severity.Error); + }, + [view], + ); + return ( + onCaptureException({ error, componentStack })} + fallback={errorData => }> + {children} + + ); +} diff --git a/packages/mobile-app-did/js/components/Fallback/index.tsx b/packages/mobile-app-did/js/components/Fallback/index.tsx new file mode 100644 index 0000000000..a5bc160949 --- /dev/null +++ b/packages/mobile-app-did/js/components/Fallback/index.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import SafeAreaBox from 'components/SafeAreaBox'; +import { ScrollView, Text } from 'react-native'; +import CommonButton from 'components/CommonButton'; + +export interface IErrorBoundary { + error: Error; + componentStack: string | null; + eventId?: string | null; + resetError?(): void; +} + +export type FallbackProps = IErrorBoundary; +export function Fallback({ error, componentStack, resetError }: FallbackProps) { + return ( + + + + {error.toString()} + {componentStack} + + + + + ); +} diff --git a/packages/mobile-app-did/js/store/config.ts b/packages/mobile-app-did/js/store/config.ts index 30d44148ab..30d2d5ec6e 100644 --- a/packages/mobile-app-did/js/store/config.ts +++ b/packages/mobile-app-did/js/store/config.ts @@ -61,6 +61,7 @@ const defaultMiddlewareOptions: DefaultMiddlewareOptions = { serializableCheck: { // https://redux-toolkit.js.org/usage/usage-guide#use-with-redux-persist ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], + ignoreState: true, }, }; diff --git a/packages/mobile-app-did/js/utils/errorHandler/ExceptionHandler.ts b/packages/mobile-app-did/js/utils/errorHandler/ExceptionHandler.ts index 1a8f5ef8be..97c740c474 100644 --- a/packages/mobile-app-did/js/utils/errorHandler/ExceptionHandler.ts +++ b/packages/mobile-app-did/js/utils/errorHandler/ExceptionHandler.ts @@ -1,43 +1,7 @@ import * as Sentry from '@sentry/react-native'; -import { ExceptionManager, Severity } from '@portkey-wallet/utils/ExceptionManager'; +import { ExceptionManager } from '@portkey-wallet/utils/ExceptionManager'; -interface Global { - ErrorUtils: { - setGlobalHandler: any; - reportFatalError: any; - getGlobalHandler: any; - }; -} - -declare let global: Global; - -class SentryRNExceptionManager extends ExceptionManager { - initGlobalJSErrorHandler() { - const originalHandler = global.ErrorUtils?.getGlobalHandler(); - global.ErrorUtils?.setGlobalHandler?.((error: any, isFatal: boolean) => { - if (!error || !(error instanceof Error) || !error.stack) return {}; - this.reportError(error, Severity.Fatal); - if (__DEV__) { - originalHandler?.(error, isFatal); - } - }); - console.error = (message, error) => (global as any).ErrorUtils?.reportError?.(error); - } - initGlobalUnHandledPromiseErr() { - // require('promise/setimmediate/rejection-tracking').enable({ - // allRejections: true, - // onUnhandled: (id: any, error: any) => { - // const {message, stack} = error; - // const warning = - // `Possible Unhandled Promise Rejection (id: ${id}):\n` + - // (message == null ? '' : `${message}\n`) + - // (stack == null ? '' : stack); - // console.warn(warning); - // this.reportError(error, Severity.Warning); - // }, - // }); - } -} +class SentryRNExceptionManager extends ExceptionManager {} const exceptionManager = new SentryRNExceptionManager(Sentry); diff --git a/packages/mobile-app-did/js/utils/errorUtils.ts b/packages/mobile-app-did/js/utils/errorUtils.ts new file mode 100644 index 0000000000..2e6a4a04c9 --- /dev/null +++ b/packages/mobile-app-did/js/utils/errorUtils.ts @@ -0,0 +1,10 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +export const setGlobalHandler = (errorHandler: (error: any, isFatal?: boolean) => void) => { + // @ts-ignore + global.ErrorUtils.setGlobalHandler(errorHandler); +}; + +export const getGlobalHandler = () => { + // @ts-ignore + return global.ErrorUtils?.getGlobalHandler(); +}; diff --git a/packages/mobile-app-did/js/utils/logBox.ts b/packages/mobile-app-did/js/utils/logBox.ts index 9660a89ce0..55bb620008 100644 --- a/packages/mobile-app-did/js/utils/logBox.ts +++ b/packages/mobile-app-did/js/utils/logBox.ts @@ -1,2 +1,6 @@ import { LogBox } from 'react-native'; LogBox.ignoreLogs(['Non-serializable values were found in the navigation state']); +if (!__DEV__) { + // eslint-disable-next-line @typescript-eslint/no-empty-function + console.log = () => {}; +} diff --git a/packages/utils/ExceptionManager.ts b/packages/utils/ExceptionManager.ts index 771c242078..198911f96c 100644 --- a/packages/utils/ExceptionManager.ts +++ b/packages/utils/ExceptionManager.ts @@ -20,8 +20,6 @@ export interface SentryInstance { } export interface IExceptionManager { - initGlobalJSErrorHandler(): void; - initGlobalUnHandledPromiseErr(): void; reportErrorMessage(error: any, level: Severity, extra?: Extras): void; reportError(error: any, level: Severity, extra?: Extras): void; } @@ -34,8 +32,6 @@ export abstract class ExceptionManager implements IExceptionManager { private init(sentryInstance: SentryInstance) { this.sentryInstance = sentryInstance; - this.initGlobalJSErrorHandler(); - this.initGlobalUnHandledPromiseErr(); } public setSentryInstance(sentryInstance: SentryInstance) { this.sentryInstance = sentryInstance; @@ -52,6 +48,4 @@ export abstract class ExceptionManager implements IExceptionManager { extra, }); } - abstract initGlobalJSErrorHandler(): void; - abstract initGlobalUnHandledPromiseErr(): void; } diff --git a/packages/utils/errorBoundary.tsx b/packages/utils/errorBoundary.tsx new file mode 100644 index 0000000000..466d432603 --- /dev/null +++ b/packages/utils/errorBoundary.tsx @@ -0,0 +1,52 @@ +import { Component, ReactNode } from 'react'; + +export declare type FallbackRender = (errorData: { + error: Error; + componentStack: string | null; + resetError(): void; +}) => React.ReactElement; + +export type ReactErrorBoundaryProps = { + children: ReactNode; + fallback: FallbackRender; + onError?(error: Error, componentStack: string): void; +}; + +export type ErrorBoundaryTrue = { + hasError: true; + error: Error; + componentStack: string; +}; + +export type ErrorBoundaryFalse = { + hasError: false; +}; +export default class ReactErrorBoundary extends Component< + ReactErrorBoundaryProps, + ErrorBoundaryTrue | ErrorBoundaryFalse +> { + constructor(props: Readonly) { + super(props); + this.state = { hasError: false }; + } + + resetError = () => { + this.setState({ hasError: false, error: undefined, componentStack: undefined }); + }; + + componentDidCatch(error: Error & { cause?: Error }, { componentStack }: React.ErrorInfo) { + this.setState({ hasError: true, error, componentStack }); + this.props.onError?.(error, componentStack); + } + render() { + if (this.state.hasError) { + // You can render any custom fallback UI + return this.props.fallback({ + error: this.state.error, + componentStack: this.state.componentStack, + resetError: this.resetError, + }); + } + return this.props.children; + } +} From 5070cf9509616ca74e651ab76038661374b82c10 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 26 Jun 2023 17:47:31 +0800 Subject: [PATCH 208/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20ErrorBoundary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/ErrorBoundary/index.tsx | 15 ++-- packages/utils/errorBoundary.tsx | 14 ++++ .../web/components/ErrorBoundary/index.tsx | 78 ++++++++----------- .../app/web/store/Provider/index.tsx | 8 +- 4 files changed, 54 insertions(+), 61 deletions(-) diff --git a/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx b/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx index 113b987e60..101f813d8b 100644 --- a/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx +++ b/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx @@ -1,17 +1,16 @@ import React, { ReactNode, useCallback } from 'react'; -import { Fallback, IErrorBoundary } from 'components/Fallback'; +import { Fallback } from 'components/Fallback'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import { Severity } from '@portkey-wallet/utils/ExceptionManager'; -import ReactErrorBoundary from '@portkey-wallet/utils/errorBoundary'; +import ReactErrorBoundary, { ErrorBoundaryTrue, handleReportError } from '@portkey-wallet/utils/errorBoundary'; import * as errorUtils from 'utils/errorUtils'; export type ErrorBoundaryProps = { children: ReactNode; view: string; }; - +const originHandler = errorUtils.getGlobalHandler(); class ReactNativeErrorBoundary extends ReactErrorBoundary { componentDidMount() { - const originHandler = errorUtils.getGlobalHandler(); errorUtils.setGlobalHandler((error: any, isFatal?: boolean) => { if (isFatal && !__DEV__) { const componentStack = error.stack; @@ -26,12 +25,8 @@ class ReactNativeErrorBoundary extends ReactErrorBoundary { export default function ErrorBoundary({ children, view }: ErrorBoundaryProps) { const onCaptureException = useCallback( - ({ error, componentStack }: IErrorBoundary) => { - const message = error.toString(); - const sendError = new Error(message); - sendError.stack = componentStack || ''; - sendError.name = `Message:${message}, View:${view}`; - exceptionManager.reportError(sendError, Severity.Error); + ({ error, componentStack }: Omit) => { + exceptionManager.reportError(handleReportError({ error, componentStack: componentStack, view }), Severity.Error); }, [view], ); diff --git a/packages/utils/errorBoundary.tsx b/packages/utils/errorBoundary.tsx index 466d432603..1410af3c1b 100644 --- a/packages/utils/errorBoundary.tsx +++ b/packages/utils/errorBoundary.tsx @@ -50,3 +50,17 @@ export default class ReactErrorBoundary extends Component< return this.props.children; } } + +export function handleReportError({ + error, + componentStack, + view, +}: Omit & { + view: string; +}) { + const message = error.toString(); + const sendError = new Error(message); + sendError.stack = componentStack || ''; + sendError.name = `Message:${message}, View:${view}`; + return sendError; +} diff --git a/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx b/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx index a74efe5c3b..179424fc5f 100644 --- a/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx +++ b/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx @@ -1,51 +1,35 @@ -import React, { Component } from 'react'; +import { ReactNode, useCallback } from 'react'; +import ReactErrorBoundary, { ErrorBoundaryTrue, handleReportError } from '@portkey-wallet/utils/errorBoundary'; -interface ErrorBoundaryProps { - message?: React.ReactNode; - description?: React.ReactNode; - children?: React.ReactNode; -} - -const whiteSpace = 'pre-wrap'; - -export default class ErrorBoundary extends Component< - ErrorBoundaryProps, - { - error?: Error | null; - info: any; - } -> { - constructor(props: ErrorBoundaryProps) { - super(props); - this.state = { error: null, info: null }; - } - - // static getDerivedStateFromError(error:any) { - // // Update state so the next render will show the fallback UI. - // return { error: error }; - // } - - componentDidCatch(error: Error | null, info: any) { - // Display fallback UI - this.setState({ error, info }); - // You can also log the error to an error reporting service - // logErrorToMyService(error, info); - } +export type ErrorBoundaryProps = { + children: ReactNode; + view: string; +}; - render() { - if (this.state.error) { - // You can render any custom fallback UI - return ( - <> -

    Something went wrong.

    -
    - {this.state.error && this.state.error.toString()} +export default function ErrorBoundary({ children, view }: ErrorBoundaryProps) { + const onError = useCallback( + ({ error, componentStack }: Omit) => { + const sendError = handleReportError({ error, componentStack, view }); + console.log(sendError, '====sendError'); + // TODO: reportError + }, + [view], + ); + return ( + onError({ error, componentStack })} + fallback={({ error, componentStack, resetError }) => { + return ( + <> +

    Something went wrong.

    + {error.toString()}
    - {this.state.info.componentStack} -
    - - ); - } - return this.props.children; - } + {componentStack} + + + ); + }}> + {children} + + ); } diff --git a/packages/web-extension-did/app/web/store/Provider/index.tsx b/packages/web-extension-did/app/web/store/Provider/index.tsx index 80e591a8d8..991ea5e1f8 100644 --- a/packages/web-extension-did/app/web/store/Provider/index.tsx +++ b/packages/web-extension-did/app/web/store/Provider/index.tsx @@ -53,8 +53,8 @@ export default function ContextProviders({ }, [language]); return ( - - + + @@ -63,7 +63,7 @@ export default function ContextProviders({ {children} - - + + ); } From 03be4c4c57aaf386e06b10384b11f59dbb208490 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 26 Jun 2023 18:03:12 +0800 Subject: [PATCH 209/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20BuyPage=20?= =?UTF-8?q?show&hide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/Updater/index.tsx | 3 +- .../mobile-app-did/js/hooks/useInitData.ts | 4 +- .../mobile-app-did/js/hooks/useSwitchBuy.ts | 7 -- .../Buy/BuyHome/components/BuyForm/index.tsx | 24 ++++++- .../Buy/BuyHome/components/SellForm/index.tsx | 21 +++++- .../js/pages/Buy/BuyHome/index.tsx | 66 ++++++++++++++----- .../js/pages/Buy/BuyPreview/index.tsx | 31 ++++++++- .../js/pages/DashBoard/Card/index.tsx | 8 +-- .../js/pages/Token/TokenDetail/index.tsx | 12 ++-- 9 files changed, 136 insertions(+), 40 deletions(-) diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index cfc843e720..c4ad462cde 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -15,7 +15,7 @@ import { useCheckManagerOnLogout } from 'hooks/useLogOut'; import socket from '@portkey-wallet/socket/socket-did'; import CommonToast from 'components/CommonToast'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; -import { useDiscoverGroupList, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useBuyButton, useDiscoverGroupList, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; @@ -61,5 +61,6 @@ export default function Updater() { useSocialMediaList(true); useTabMenuList(true); useDiscoverGroupList(true); + useBuyButton(true); return null; } diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index 7a81084d28..2846c3bc3f 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -1,6 +1,6 @@ import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { fetchBuyFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; +import { fetchBuyFiatListAsync, fetchSellFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; import { getSymbolImagesAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import { getWalletNameAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useCallback } from 'react'; @@ -23,7 +23,7 @@ export default function useInitData() { // mainnet only if (isMainNetwork) { dispatch(fetchBuyFiatListAsync()); - // dispatch(fetchSellFiatListAsync()); + dispatch(fetchSellFiatListAsync()); } getCurrentCAViewContract(); dispatch(getWalletNameAsync()); diff --git a/packages/mobile-app-did/js/hooks/useSwitchBuy.ts b/packages/mobile-app-did/js/hooks/useSwitchBuy.ts index 6c5eefcde9..90c0528b6c 100644 --- a/packages/mobile-app-did/js/hooks/useSwitchBuy.ts +++ b/packages/mobile-app-did/js/hooks/useSwitchBuy.ts @@ -1,15 +1,8 @@ -import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { fetchIsShowBuyFeatureAsync } from '@portkey-wallet/store/store-ca/switch/slice'; import { useEffect } from 'react'; import { isIOS } from '@rneui/base'; -export const useIsShowBuy = (): boolean => { - const { isShowBuyFeature } = useAppCASelector(state => state.switch); - return true; - return isIOS ? isShowBuyFeature : true; -}; - export const useFetchIsShowBuyButton = () => { const dispatch = useAppCommonDispatch(); useEffect(() => { diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx index 20e043b416..cf2b9e6e66 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/BuyForm/index.tsx @@ -29,9 +29,12 @@ import { useReceive } from 'pages/Buy/hooks'; import BigNumber from 'bignumber.js'; import { ZERO } from '@portkey-wallet/constants/misc'; import { PaymentLimitType, PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import CommonToast from 'components/CommonToast'; export default function BuyForm() { const { buyFiatList: fiatList } = usePayment(); + const { refreshBuyButton } = useBuyButtonShow(); const [fiat, setFiat] = useState( fiatList.find(item => item.currency === 'USD' && item.country === 'US'), @@ -134,16 +137,33 @@ export default function BuyForm() { }); return; } + + Loading.show(); + let isBuySectionShow = false; + try { + const result = await refreshBuyButton(); + isBuySectionShow = result.isBuySectionShow; + } catch (error) { + console.log(error); + } + if (!isBuySectionShow) { + CommonToast.fail('Sorry, the service you are using is temporarily unavailable.'); + navigationService.navigate('Tab'); + Loading.hide(); + return; + } + let _rate = rate, _receiveAmount = receiveAmount; if (isRefreshReceiveValid.current === false) { - Loading.show(); const rst = await refreshReceiveRef.current(); Loading.hide(); if (!rst) return; _rate = rst.rate; _receiveAmount = rst.receiveAmount; + } else { + Loading.hide(); } navigationService.navigate('BuyPreview', { amount, @@ -153,7 +173,7 @@ export default function BuyForm() { receiveAmount: _receiveAmount, rate: _rate, }); - }, [amount, fiat, rate, receiveAmount, token]); + }, [amount, fiat, rate, receiveAmount, refreshBuyButton, token]); return ( diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index 6e462d91fd..7cd4180635 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -36,9 +36,12 @@ import { ZERO } from '@portkey-wallet/constants/misc'; import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; import BigNumber from 'bignumber.js'; import { PaymentLimitType, PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import CommonToast from 'components/CommonToast'; export default function SellForm() { const { sellFiatList: fiatList } = usePayment(); + const { refreshBuyButton } = useBuyButtonShow(); const [fiat, setFiat] = useState( fiatList.find(item => item.currency === 'USD' && item.country === 'US'), @@ -161,8 +164,22 @@ export default function SellForm() { if (!tokenContractAddress || decimals === undefined || !symbol || !chainId) return; if (!pin || !endPoint) return; + Loading.show(); + let isSellSectionShow = false; + try { + const result = await refreshBuyButton(); + isSellSectionShow = result.isSellSectionShow; + } catch (error) { + console.log(error); + } + if (!isSellSectionShow) { + CommonToast.fail('Sorry, the service you are using is temporarily unavailable.'); + navigationService.navigate('Tab'); + Loading.hide(); + return; + } + try { - Loading.show(); if (ZERO.plus(amount).isLessThanOrEqualTo(DEFAULT_FEE)) { throw new Error('Insufficient funds'); } @@ -205,7 +222,7 @@ export default function SellForm() { receiveAmount: _receiveAmount, rate: _rate, }); - }, [amount, rate, receiveAmount, aelfToken, chainInfo, pin, fiat, token, wallet]); + }, [amount, rate, receiveAmount, aelfToken, chainInfo, pin, refreshBuyButton, fiat, token, wallet]); return ( diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx index 8f666ac97a..48113efc1d 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx @@ -1,5 +1,5 @@ import { defaultColors } from 'assets/theme'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { pTd } from 'utils/unit'; import PageContainer from 'components/PageContainer'; @@ -12,6 +12,10 @@ import BuyForm from './components/BuyForm'; import SellForm from './components/SellForm'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import ActionSheet from 'components/ActionSheet'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useIsFocused } from '@react-navigation/native'; +import navigationService from 'utils/navigationService'; +import CommonToast from 'components/CommonToast'; type TabItemType = { name: string; @@ -34,7 +38,51 @@ const tabList: TabItemType[] = [ export default function BuyHome() { const { t } = useLanguage(); - const [selectTab, setSelectTab] = useState(PaymentTypeEnum.BUY); + const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); + const isFocused = useIsFocused(); + const [selectTab, setSelectTab] = useState( + isBuySectionShow ? PaymentTypeEnum.BUY : PaymentTypeEnum.SELL, + ); + + useEffect(() => { + if (!isFocused) return; + if ( + (selectTab === PaymentTypeEnum.BUY && !isBuySectionShow) || + (selectTab === PaymentTypeEnum.SELL && !isSellSectionShow) + ) { + CommonToast.fail('Sorry, the service you are using is temporarily unavailable.'); + navigationService.navigate('Tab'); + return; + } + }, [isBuySectionShow, isFocused, isSellSectionShow, selectTab]); + + const onTabPress = (type: PaymentTypeEnum) => { + if (type === PaymentTypeEnum.BUY && !isBuySectionShow) { + ActionSheet.alert({ + title2: ( + + On-ramp is currently not supported. It will be launched in the coming weeks. + + ), + buttons: [{ title: 'OK' }], + }); + refreshBuyButton(); + return; + } + if (type === PaymentTypeEnum.SELL && !isSellSectionShow) { + ActionSheet.alert({ + title2: ( + + Off-ramp is currently not supported. It will be launched in the coming weeks. + + ), + buttons: [{ title: 'OK' }], + }); + refreshBuyButton(); + return; + } + setSelectTab(type); + }; return ( { - if (tabItem.type === PaymentTypeEnum.SELL) { - ActionSheet.alert({ - title2: ( - - Off-ramp is currently not supported. It will be launched in the coming weeks. - - ), - buttons: [{ title: 'OK' }], - }); - return; - } - - setSelectTab(tabItem.type); + onTabPress(tabItem.type); }}> diff --git a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx index 228786dd0d..63fd770547 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx @@ -28,6 +28,7 @@ import { ACH_REDIRECT_URL, ACH_WITHDRAW_URL } from 'constants/common'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; interface RouterParams { type?: PaymentTypeEnum; @@ -54,6 +55,7 @@ export default function BuyPreview() { const apiUrl = useCurrentApiUrl(); const wallet = useCurrentWalletInfo(); const { buyConfig } = useCurrentNetworkInfo(); + const { refreshBuyButton } = useBuyButtonShow(); const getAchTokenInfo = useGetAchTokenInfo(); const goPayPage = useCallback( @@ -62,6 +64,21 @@ export default function BuyPreview() { const baseUrl = buyConfig?.ach?.baseUrl; if (!amount || !receiveAmount || !fiat || !token || !appId || !baseUrl) return; Loading.show(); + + let isSectionShow = false; + try { + const result = await refreshBuyButton(); + isSectionShow = type === PaymentTypeEnum.BUY ? result.isBuySectionShow : result.isSellSectionShow; + } catch (error) { + console.log(error); + } + if (!isSectionShow) { + CommonToast.fail('Sorry, the service you are using is temporarily unavailable.'); + navigationService.navigate('Tab'); + Loading.hide(); + return; + } + try { const callbackUrl = encodeURIComponent(`${apiUrl}${paymentApi.updateAchOrder}`); let achUrl = `${baseUrl}/?crypto=${token.crypto}&network=${token.network}&country=${fiat.country}&fiat=${fiat.currency}&appId=${appId}&callbackUrl=${callbackUrl}`; @@ -122,7 +139,19 @@ export default function BuyPreview() { } Loading.hide(); }, - [amount, apiUrl, buyConfig, fiat, getAchTokenInfo, receiveAmount, token, type, wallet.AELF?.caAddress], + [ + amount, + apiUrl, + buyConfig?.ach?.appId, + buyConfig?.ach?.baseUrl, + fiat, + getAchTokenInfo, + receiveAmount, + refreshBuyButton, + token, + type, + wallet.AELF?.caAddress, + ], ); return ( diff --git a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx index 87152b1339..20b078045f 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx +++ b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { View, Text } from 'react-native'; import Svg from 'components/Svg'; import { styles } from './style'; @@ -17,16 +17,16 @@ import { useLanguage } from 'i18n/hooks'; import BuyButton from 'components/BuyButton'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; -import { useIsShowBuy } from 'hooks/useSwitchBuy'; import FaucetButton from 'components/FaucetButton'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; const Card: React.FC = () => { const { t } = useLanguage(); const isMainnet = useIsMainnet(); const { walletName } = useWallet(); - const isShowBuy = useIsShowBuy(); const accountBalanceUSD = useAccountBalanceUSD(); const [, requestQrPermission] = useQrScanPermission(); + const { isBuyButtonShow } = useBuyButtonShow(); const showDialog = useCallback( () => @@ -59,7 +59,7 @@ const Card: React.FC = () => { {isMainnet ? `$${accountBalanceUSD}` : 'Dev Mode'} {walletName} - {isMainnet && isShowBuy && ( + {isBuyButtonShow && ( <> diff --git a/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx b/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx index 3624e6d7f2..58f4d1fd3f 100644 --- a/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx @@ -31,8 +31,8 @@ import BuyButton from 'components/BuyButton'; import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useGetCurrentAccountTokenPrice, useIsTokenHasPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; -import { useIsShowBuy } from 'hooks/useSwitchBuy'; import FaucetButton from 'components/FaucetButton'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; interface RouterParams { tokenInfo: TokenItemShowType; @@ -50,7 +50,6 @@ const TokenDetail: React.FC = () => { const isMainnet = useIsMainnet(); const currentWallet = useCurrentWallet(); - const isShowBuy = useIsShowBuy(); const caAddressInfos = useCaAddressInfoList(); const navigation = useNavigation(); const dispatch = useAppCommonDispatch(); @@ -58,6 +57,7 @@ const TokenDetail: React.FC = () => { const { accountToken } = useAppCASelector(state => state.assets); const isTokenHasPrice = useIsTokenHasPrice(tokenInfo.symbol); const [tokenPriceObject, getTokenPrice] = useGetCurrentAccountTokenPrice(); + const { isBuyButtonShow: isBuyButtonShowStore } = useBuyButtonShow(); const [reFreshing, setFreshing] = useState(false); @@ -127,13 +127,13 @@ const TokenDetail: React.FC = () => { }); const isBuyButtonShow = useMemo( - () => isMainnet && tokenInfo.symbol === ELF_SYMBOL && tokenInfo.chainId === 'AELF' && isShowBuy, - [isMainnet, isShowBuy, tokenInfo.chainId, tokenInfo.symbol], + () => tokenInfo.symbol === ELF_SYMBOL && tokenInfo.chainId === 'AELF' && isBuyButtonShowStore, + [isBuyButtonShowStore, tokenInfo.chainId, tokenInfo.symbol], ); const isFaucetButtonShow = useMemo( - () => !isMainnet && tokenInfo.symbol === ELF_SYMBOL && tokenInfo.chainId === 'AELF' && isShowBuy, - [isMainnet, isShowBuy, tokenInfo.chainId, tokenInfo.symbol], + () => !isMainnet && tokenInfo.symbol === ELF_SYMBOL && tokenInfo.chainId === 'AELF', + [isMainnet, tokenInfo.chainId, tokenInfo.symbol], ); return ( From d601098244ba9329d41ad909d816348bbecc7081 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 26 Jun 2023 18:54:57 +0800 Subject: [PATCH 210/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20buy=20show?= =?UTF-8?q?=20or=20hidden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/Preview/index.tsx | 32 +++++++++-- .../app/web/pages/Buy/const.ts | 2 + .../app/web/pages/Buy/index.tsx | 57 ++++++++++++++++--- .../pages/Home/components/MyBalance/index.tsx | 11 ++-- .../app/web/pages/Token/Detail/index.tsx | 12 +++- .../app/web/store/Provider/Updater.tsx | 3 +- 6 files changed, 98 insertions(+), 19 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index 90f4924368..fc20a01301 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -4,7 +4,7 @@ import { Button, message } from 'antd'; import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import { useLocation, useNavigate } from 'react-router'; -import { disclaimer, initPreviewData, MAX_UPDATE_TIME } from '../const'; +import { disclaimer, initPreviewData, MAX_UPDATE_TIME, serviceUnavailableText } from '../const'; import { getAchSignature, getOrderQuote, getPaymentOrderNo } from '@portkey-wallet/api/api-did/payment/util'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; import { useCommonState, useLoading } from 'store/Provider/hooks'; @@ -20,6 +20,7 @@ import './index.less'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import { ACH_WITHDRAW_URL } from 'constants/index'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; export default function Preview() { const { t } = useTranslation(); @@ -32,6 +33,7 @@ export default function Preview() { const { setLoading } = useLoading(); const wallet = useCurrentWalletInfo(); const { buyConfig } = useCurrentNetworkInfo(); + const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); const data = useMemo(() => ({ ...initPreviewData, ...state }), [state]); const showRateText = useMemo(() => `1 ${data.crypto} ≈ ${formatAmountShow(rate, 2)} ${data.fiat}`, [data, rate]); @@ -76,8 +78,9 @@ export default function Preview() { }, [data, setReceiveCase]); useEffect(() => { + refreshBuyButton(); updateReceive(); - }, [updateReceive]); + }, [refreshBuyButton, updateReceive]); useEffect(() => { const timer = setInterval(() => { @@ -94,12 +97,22 @@ export default function Preview() { }, []); const goPayPage = useCallback(async () => { + const { side } = data; + const result = await refreshBuyButton(); + const isBuySectionShow = result.isBuySectionShow; + const isSellSectionShow = result.isSellSectionShow; + // Compatible with the situation where the function is turned off when the user is on the page. + if ((side === PaymentTypeEnum.BUY && !isBuySectionShow) || (side === PaymentTypeEnum.SELL && !isSellSectionShow)) { + message.error(serviceUnavailableText); + return navigate('/'); + } + const appId = buyConfig?.ach?.appId; const baseUrl = buyConfig?.ach?.baseUrl; if (!appId || !baseUrl) return; try { setLoading(true); - const { network, country, fiat, side, amount, crypto } = data; + const { network, country, fiat, amount, crypto } = data; let achUrl = `${baseUrl}/?crypto=${crypto}&network=${network}&country=${country}&fiat=${fiat}&appId=${appId}&callbackUrl=${encodeURIComponent( `${apiUrl}${paymentApi.updateAchOrder}`, )}`; @@ -142,7 +155,18 @@ export default function Preview() { } finally { setLoading(false); } - }, [apiUrl, buyConfig, data, getAchTokenInfo, navigate, setLoading, wallet?.AELF?.caAddress]); + }, [ + apiUrl, + buyConfig?.ach?.appId, + buyConfig?.ach?.baseUrl, + data, + getAchTokenInfo, + isBuySectionShow, + isSellSectionShow, + navigate, + setLoading, + wallet?.AELF?.caAddress, + ]); const showDisclaimerTipModal = useCallback(() => { CustomModal({ diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index c17305bb03..d8addd8646 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -5,6 +5,8 @@ import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; export const sellSoonText = 'Off-ramp is currently not supported. It will be launched in the coming weeks.'; +export const serviceUnavailableText = 'Sorry, the service you are using is temporarily unavailable.'; + export const soonText = 'On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.'; export const disclaimer = diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 6ab7f07904..60a357c032 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, Radio, RadioChangeEvent } from 'antd'; +import { Button, Radio, RadioChangeEvent, message } from 'antd'; import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import { useLocation, useNavigate } from 'react-router'; @@ -17,6 +17,7 @@ import { MAX_UPDATE_TIME, PartialFiatType, sellSoonText, + serviceUnavailableText, } from './const'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { useCommonState, useLoading } from 'store/Provider/hooks'; @@ -36,6 +37,7 @@ import { useEffectOnce } from 'react-use'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import BigNumber from 'bignumber.js'; import CustomTipModal from 'pages/components/CustomModal'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; export default function Buy() { const { t } = useTranslation(); @@ -54,6 +56,7 @@ export default function Buy() { const { setLoading } = useLoading(); const [curFiat, setCurFiat] = useState(initFiat); const [rateUpdateTime, setRateUpdateTime] = useState(MAX_UPDATE_TIME); + const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); const disabled = useMemo(() => !!errMsg || !amount, [errMsg, amount]); const showRateText = useMemo( @@ -62,6 +65,8 @@ export default function Buy() { ); useEffectOnce(() => { + refreshBuyButton(); // fetch on\off ramp is display + if (state && state.amount !== undefined) { const { amount, country, fiat, crypto, network, side } = state; setAmount(amount); @@ -82,6 +87,13 @@ export default function Buy() { }; updateCrypto(); } else { + if (!isBuySectionShow && isSellSectionShow) { + const side = PaymentTypeEnum.SELL; + setPage(side); + valueSaveRef.current.side = side; + setAmount(initCrypto); + valueSaveRef.current.amount = initCrypto; + } updateCrypto(); } return () => { @@ -259,20 +271,28 @@ export default function Buy() { const handlePageChange = useCallback( async (e: RadioChangeEvent) => { - if (e.target.value === PaymentTypeEnum.SELL) { + refreshBuyButton(); // fetch on\off ramp is display + + const side = e.target.value; + // Compatible with the situation where the function is turned off when the user is on the page. + if ( + (side === PaymentTypeEnum.BUY && !isBuySectionShow) || + (side === PaymentTypeEnum.SELL && !isSellSectionShow) + ) { CustomTipModal({ content: sellSoonText, }); return; } + stopInterval(); - setPage(e.target.value); + setPage(side); // BUY valueSaveRef.current = { ...initValueSave }; - valueSaveRef.current.side = e.target.value; + valueSaveRef.current.side = side; setAmount(initCurrency); // SELL - if (e.target.value === PaymentTypeEnum.SELL) { + if (side === PaymentTypeEnum.SELL) { setAmount(initCrypto); valueSaveRef.current.amount = initCrypto; } @@ -292,7 +312,7 @@ export default function Buy() { setLoading(false); } }, - [setLoading, stopInterval, updateCrypto], + [isBuySectionShow, isSellSectionShow, refreshBuyButton, setLoading, stopInterval, updateCrypto], ); const handleSelect = useCallback( @@ -339,7 +359,17 @@ export default function Buy() { }, [stopInterval]); const handleNext = useCallback(async () => { - if (valueSaveRef.current.side === PaymentTypeEnum.SELL) { + const { side } = valueSaveRef.current; + const result = await refreshBuyButton(); + const isBuySectionShow = result.isBuySectionShow; + const isSellSectionShow = result.isSellSectionShow; + // Compatible with the situation where the function is turned off when the user is on the page. + if ((side === PaymentTypeEnum.BUY && !isBuySectionShow) || (side === PaymentTypeEnum.SELL && !isSellSectionShow)) { + message.error(serviceUnavailableText); + return navigate('/'); + } + + if (side === PaymentTypeEnum.SELL) { if (!currentChain) return; // search balance from contract const result = await getBalance({ @@ -361,7 +391,7 @@ export default function Buy() { } } - const { amount, currency, country, crypto, network, side } = valueSaveRef.current; + const { amount, currency, country, crypto, network } = valueSaveRef.current; navigate('/buy/preview', { state: { crypto, @@ -373,7 +403,16 @@ export default function Buy() { tokenInfo: state ? state.tokenInfo : null, }, }); - }, [accountTokenList, currentChain, currentNetwork.walletType, navigate, setInsufficientFundsMsg, state, wallet]); + }, [ + accountTokenList, + currentChain, + currentNetwork.walletType, + navigate, + refreshBuyButton, + setInsufficientFundsMsg, + state, + wallet, + ]); const handleBack = useCallback(() => { if (state && state.tokenInfo) { diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index 3a4541d5a0..a426c7cf60 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -20,7 +20,7 @@ import { fetchAllTokenListAsync, getSymbolImagesAsync } from '@portkey-wallet/st import { getWalletNameAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import CustomTokenModal from 'pages/components/CustomTokenModal'; import { AccountAssetItem } from '@portkey-wallet/types/types-ca/token'; -import { fetchBuyFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; +import { fetchBuyFiatListAsync, fetchSellFiatListAsync } from '@portkey-wallet/store/store-ca/payment/actions'; import { useFreshTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; import useVerifierList from 'hooks/useVerifierList'; @@ -31,6 +31,7 @@ import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import AccountConnect from 'pages/components/AccountConnect'; import './index.less'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; export interface TransactionResult { total: number; @@ -80,6 +81,7 @@ export default function MyBalance() { const getGuardianList = useGuardianList(); useFreshTokenPrice(); useVerifierList(); + const { isBuyButtonShow, refreshBuyButton } = useBuyButtonShow(); useEffect(() => { if (state?.key) { @@ -90,12 +92,13 @@ export default function MyBalance() { appDispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray })); appDispatch(getWalletNameAsync()); appDispatch(getSymbolImagesAsync()); - }, [passwordSeed, appDispatch, caAddresses, chainIdArray, caAddressInfos, isMainNet, state?.key]); + refreshBuyButton(); + }, [passwordSeed, appDispatch, caAddresses, chainIdArray, caAddressInfos, isMainNet, state?.key, refreshBuyButton]); useEffect(() => { getGuardianList({ caHash: walletInfo?.caHash }); isMainNet && appDispatch(fetchBuyFiatListAsync()); - // isMainNet && appDispatch(fetchSellFiatListAsync()); + isMainNet && appDispatch(fetchSellFiatListAsync()); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMainNet]); @@ -181,7 +184,7 @@ export default function MyBalance() {
    { setNavTarget('send'); diff --git a/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx b/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx index c9c20fb070..c570b0daed 100644 --- a/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx @@ -12,6 +12,8 @@ import { useFreshTokenPrice, useAmountInUsdShow } from '@portkey-wallet/hooks/ho import { FaucetUrl } from '@portkey-wallet/constants/constants-ca/payment'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import './index.less'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useEffectOnce } from 'react-use'; export enum TokenTransferStatus { CONFIRMED = 'Confirmed', @@ -23,10 +25,18 @@ function TokenDetail() { const { state: currentToken } = useLocation(); const isMainNet = useIsMainnet(); const { isPrompt } = useCommonState(); - const isShowBuy = useMemo(() => currentToken.symbol === 'ELF' && currentToken.chainId === 'AELF', [currentToken]); + const { isBuyButtonShow, refreshBuyButton } = useBuyButtonShow(); + const isShowBuy = useMemo( + () => currentToken.symbol === 'ELF' && currentToken.chainId === 'AELF' && isBuyButtonShow, + [currentToken.chainId, currentToken.symbol, isBuyButtonShow], + ); const amountInUsdShow = useAmountInUsdShow(); useFreshTokenPrice(); + useEffectOnce(() => { + refreshBuyButton(); + }); + const handleBuy = useCallback(() => { if (isMainNet) { navigate('/buy', { state: { tokenInfo: currentToken } }); diff --git a/packages/web-extension-did/app/web/store/Provider/Updater.tsx b/packages/web-extension-did/app/web/store/Provider/Updater.tsx index a4c0945824..954e8a9975 100644 --- a/packages/web-extension-did/app/web/store/Provider/Updater.tsx +++ b/packages/web-extension-did/app/web/store/Provider/Updater.tsx @@ -16,7 +16,7 @@ import { useCheckManager } from '@portkey-wallet/hooks/hooks-ca/graphql'; import { useCheckUpdate } from 'hooks/useCheckUpdate'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; import { useLocation } from 'react-router'; -import { useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useBuyButton, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; keepAliveOnPages({}); @@ -56,5 +56,6 @@ export default function Updater() { }, [onLocking]); usePhoneCountryCode(true); useSocialMediaList(true); + useBuyButton(true); return null; } From 3944a854e46222c6c80be5f71335a693961129f1 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 26 Jun 2023 19:40:31 +0800 Subject: [PATCH 211/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20delete=20refresh?= =?UTF-8?q?BuyButton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/Preview/index.tsx | 10 +++++----- packages/web-extension-did/app/web/pages/Buy/index.tsx | 9 ++++++--- .../app/web/pages/Home/components/MyBalance/index.tsx | 5 ++--- .../app/web/pages/Token/Detail/index.tsx | 7 +------ 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index fc20a01301..ce10a49d24 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -78,9 +78,8 @@ export default function Preview() { }, [data, setReceiveCase]); useEffect(() => { - refreshBuyButton(); updateReceive(); - }, [refreshBuyButton, updateReceive]); + }, [updateReceive]); useEffect(() => { const timer = setInterval(() => { @@ -98,18 +97,20 @@ export default function Preview() { const goPayPage = useCallback(async () => { const { side } = data; + setLoading(true); const result = await refreshBuyButton(); const isBuySectionShow = result.isBuySectionShow; const isSellSectionShow = result.isSellSectionShow; // Compatible with the situation where the function is turned off when the user is on the page. if ((side === PaymentTypeEnum.BUY && !isBuySectionShow) || (side === PaymentTypeEnum.SELL && !isSellSectionShow)) { + setLoading(false); message.error(serviceUnavailableText); return navigate('/'); } const appId = buyConfig?.ach?.appId; const baseUrl = buyConfig?.ach?.baseUrl; - if (!appId || !baseUrl) return; + if (!appId || !baseUrl) return setLoading(false); try { setLoading(true); const { network, country, fiat, amount, crypto } = data; @@ -161,9 +162,8 @@ export default function Preview() { buyConfig?.ach?.baseUrl, data, getAchTokenInfo, - isBuySectionShow, - isSellSectionShow, navigate, + refreshBuyButton, setLoading, wallet?.AELF?.caAddress, ]); diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 60a357c032..d783db9a21 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -65,8 +65,6 @@ export default function Buy() { ); useEffectOnce(() => { - refreshBuyButton(); // fetch on\off ramp is display - if (state && state.amount !== undefined) { const { amount, country, fiat, crypto, network, side } = state; setAmount(amount); @@ -360,17 +358,19 @@ export default function Buy() { const handleNext = useCallback(async () => { const { side } = valueSaveRef.current; + setLoading(true); const result = await refreshBuyButton(); const isBuySectionShow = result.isBuySectionShow; const isSellSectionShow = result.isSellSectionShow; // Compatible with the situation where the function is turned off when the user is on the page. if ((side === PaymentTypeEnum.BUY && !isBuySectionShow) || (side === PaymentTypeEnum.SELL && !isSellSectionShow)) { + setLoading(false); message.error(serviceUnavailableText); return navigate('/'); } if (side === PaymentTypeEnum.SELL) { - if (!currentChain) return; + if (!currentChain) return setLoading(false); // search balance from contract const result = await getBalance({ rpcUrl: currentChain.endPoint, @@ -381,6 +381,7 @@ export default function Buy() { symbol: 'ELF', }, }); + setLoading(false); const balance = result.result.balance; if ( @@ -390,6 +391,7 @@ export default function Buy() { return; } } + setLoading(false); const { amount, currency, country, crypto, network } = valueSaveRef.current; navigate('/buy/preview', { @@ -410,6 +412,7 @@ export default function Buy() { navigate, refreshBuyButton, setInsufficientFundsMsg, + setLoading, state, wallet, ]); diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index a426c7cf60..34a73936a8 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -81,7 +81,7 @@ export default function MyBalance() { const getGuardianList = useGuardianList(); useFreshTokenPrice(); useVerifierList(); - const { isBuyButtonShow, refreshBuyButton } = useBuyButtonShow(); + const { isBuyButtonShow } = useBuyButtonShow(); useEffect(() => { if (state?.key) { @@ -92,8 +92,7 @@ export default function MyBalance() { appDispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray })); appDispatch(getWalletNameAsync()); appDispatch(getSymbolImagesAsync()); - refreshBuyButton(); - }, [passwordSeed, appDispatch, caAddresses, chainIdArray, caAddressInfos, isMainNet, state?.key, refreshBuyButton]); + }, [passwordSeed, appDispatch, caAddresses, chainIdArray, caAddressInfos, isMainNet, state?.key]); useEffect(() => { getGuardianList({ caHash: walletInfo?.caHash }); diff --git a/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx b/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx index c570b0daed..8700a8b42a 100644 --- a/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx @@ -13,7 +13,6 @@ import { FaucetUrl } from '@portkey-wallet/constants/constants-ca/payment'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import './index.less'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; -import { useEffectOnce } from 'react-use'; export enum TokenTransferStatus { CONFIRMED = 'Confirmed', @@ -25,7 +24,7 @@ function TokenDetail() { const { state: currentToken } = useLocation(); const isMainNet = useIsMainnet(); const { isPrompt } = useCommonState(); - const { isBuyButtonShow, refreshBuyButton } = useBuyButtonShow(); + const { isBuyButtonShow } = useBuyButtonShow(); const isShowBuy = useMemo( () => currentToken.symbol === 'ELF' && currentToken.chainId === 'AELF' && isBuyButtonShow, [currentToken.chainId, currentToken.symbol, isBuyButtonShow], @@ -33,10 +32,6 @@ function TokenDetail() { const amountInUsdShow = useAmountInUsdShow(); useFreshTokenPrice(); - useEffectOnce(() => { - refreshBuyButton(); - }); - const handleBuy = useCallback(() => { if (isMainNet) { navigate('/buy', { state: { tokenInfo: currentToken } }); From a726203ce148839bae96b70ab4502b1f459fed4d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 27 Jun 2023 10:15:31 +0800 Subject: [PATCH 212/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/android/app/build.gradle | 2 +- packages/mobile-app-did/ios/Portkey/Info.plist | 2 +- packages/web-extension-did/app/web/manifest.json | 2 +- packages/web-extension-did/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/android/app/build.gradle b/packages/mobile-app-did/android/app/build.gradle index bdd1f51266..0b20f653f8 100644 --- a/packages/mobile-app-did/android/app/build.gradle +++ b/packages/mobile-app-did/android/app/build.gradle @@ -168,7 +168,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 231 - versionName "1.3.0" + versionName "1.3.1" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/packages/mobile-app-did/ios/Portkey/Info.plist b/packages/mobile-app-did/ios/Portkey/Info.plist index 297fbb59e5..ec19d210e0 100644 --- a/packages/mobile-app-did/ios/Portkey/Info.plist +++ b/packages/mobile-app-did/ios/Portkey/Info.plist @@ -32,7 +32,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.0 + 1.3.1 CFBundleSignature ???? CFBundleVersion diff --git a/packages/web-extension-did/app/web/manifest.json b/packages/web-extension-did/app/web/manifest.json index 19a0287e4f..a1d87858dc 100644 --- a/packages/web-extension-did/app/web/manifest.json +++ b/packages/web-extension-did/app/web/manifest.json @@ -4,7 +4,7 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "name": "Portkey: DID & Crypto & NFT", - "version": "1.3.0", + "version": "1.3.1", "description": "Identity System for Social Recover and Asset Management Tool", "icons": { "16": "assets/images/extension_logo.png", diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index ae2cef0995..4f27080583 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -1,6 +1,6 @@ { "name": "web-extension-did", - "version": "1.3.0", + "version": "1.3.1", "description": "web-extension-did", "private": true, "dependencies": { From b63e7a78ced5b98af2596784ec9272eb8d285838 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 27 Jun 2023 10:17:57 +0800 Subject: [PATCH 213/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20buySoon?= =?UTF-8?q?Text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/Preview/index.tsx | 3 +-- .../web-extension-did/app/web/pages/Buy/const.ts | 1 + .../web-extension-did/app/web/pages/Buy/index.tsx | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index ce10a49d24..3fc3cf583a 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -33,7 +33,7 @@ export default function Preview() { const { setLoading } = useLoading(); const wallet = useCurrentWalletInfo(); const { buyConfig } = useCurrentNetworkInfo(); - const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); + const { refreshBuyButton } = useBuyButtonShow(); const data = useMemo(() => ({ ...initPreviewData, ...state }), [state]); const showRateText = useMemo(() => `1 ${data.crypto} ≈ ${formatAmountShow(rate, 2)} ${data.fiat}`, [data, rate]); @@ -112,7 +112,6 @@ export default function Preview() { const baseUrl = buyConfig?.ach?.baseUrl; if (!appId || !baseUrl) return setLoading(false); try { - setLoading(true); const { network, country, fiat, amount, crypto } = data; let achUrl = `${baseUrl}/?crypto=${crypto}&network=${network}&country=${country}&fiat=${fiat}&appId=${appId}&callbackUrl=${encodeURIComponent( `${apiUrl}${paymentApi.updateAchOrder}`, diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index d8addd8646..9e987bef25 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -3,6 +3,7 @@ import { ChainId } from '@portkey-wallet/types'; import { ICurToken } from './components/TokenInput'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +export const buySoonText = 'On-ramp is currently not supported. It will be launched in the coming weeks.'; export const sellSoonText = 'Off-ramp is currently not supported. It will be launched in the coming weeks.'; export const serviceUnavailableText = 'Sorry, the service you are using is temporarily unavailable.'; diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index d783db9a21..1f59ef068c 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -16,6 +16,7 @@ import { initValueSave, MAX_UPDATE_TIME, PartialFiatType, + buySoonText, sellSoonText, serviceUnavailableText, } from './const'; @@ -273,10 +274,13 @@ export default function Buy() { const side = e.target.value; // Compatible with the situation where the function is turned off when the user is on the page. - if ( - (side === PaymentTypeEnum.BUY && !isBuySectionShow) || - (side === PaymentTypeEnum.SELL && !isSellSectionShow) - ) { + if (side === PaymentTypeEnum.BUY && !isBuySectionShow) { + CustomTipModal({ + content: buySoonText, + }); + return; + } + if (side === PaymentTypeEnum.SELL && !isSellSectionShow) { CustomTipModal({ content: sellSoonText, }); From d41cae7d8e2207952d4fad38bbbab8093975f9a8 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 27 Jun 2023 10:48:43 +0800 Subject: [PATCH 214/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20publish=20messag?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lerna.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lerna.json b/lerna.json index b64532c043..c6bb57eef4 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,12 @@ "$schema": "node_modules/lerna/schemas/lerna-schema.json", "useNx": true, "useWorkspaces": true, - "version": "0.0.0", + "version": "1.3.0", "packages": ["packages/*"], - "npmClient": "yarn" + "npmClient": "yarn", + "command":{ + "publish": { + "message": "chore: 🤖 release %s" + } + } } From 7e26a7a5d674306b7a96b72d6c80472616410d34 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 27 Jun 2023 11:12:58 +0800 Subject: [PATCH 215/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20social=20media?= =?UTF-8?q?=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/My/WalletSecurity/index.tsx | 6 ++++++ .../js/pages/My/components/MenuItem/index.tsx | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx index 098089c0e7..c0b68ae22c 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx @@ -8,6 +8,7 @@ import { useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import navigationService from 'utils/navigationService'; import MenuItem from '../components/MenuItem'; import { useCurrentDappList } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import { pTd } from 'utils/unit'; const WalletSecurity: React.FC = () => { const { deviceAmount } = useDeviceList(); @@ -20,6 +21,7 @@ const WalletSecurity: React.FC = () => { containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> { @@ -27,6 +29,7 @@ const WalletSecurity: React.FC = () => { }} /> { @@ -43,6 +46,9 @@ const pageStyles = StyleSheet.create({ backgroundColor: defaultColors.bg4, ...GStyles.paddingArg(24, 20, 18), }, + menuStyle: { + marginBottom: pTd(24), + }, }); export default WalletSecurity; diff --git a/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx b/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx index 5f5f969585..768a85d1d5 100644 --- a/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx +++ b/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx @@ -58,7 +58,6 @@ const styles = StyleSheet.create({ alignItems: 'center', paddingHorizontal: pTd(16), borderRadius: pTd(6), - marginBottom: pTd(24), }, menuIcon: { borderRadius: pTd(6), From 4ffffaaf67f56dd1c26123044015b8cd9ab34519 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 27 Jun 2023 11:34:32 +0800 Subject: [PATCH 216/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20adjust=20buyHooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Buy/hooks.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mobile-app-did/js/pages/Buy/hooks.tsx b/packages/mobile-app-did/js/pages/Buy/hooks.tsx index f8bf76b0c2..fb2c382f1d 100644 --- a/packages/mobile-app-did/js/pages/Buy/hooks.tsx +++ b/packages/mobile-app-did/js/pages/Buy/hooks.tsx @@ -119,6 +119,7 @@ export const useReceive = ( registerRefreshReceive(); } + if (!isFocusedRef.current) return; if (!isEqual(params, lastParams.current)) return; if ( !rst || From 39d11e3565b32f40f6bb6b254469f9e9c03e8cd2 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 27 Jun 2023 11:57:20 +0800 Subject: [PATCH 217/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lerna.json | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lerna.json b/lerna.json index c6bb57eef4..42832f235e 100644 --- a/lerna.json +++ b/lerna.json @@ -5,8 +5,9 @@ "version": "1.3.0", "packages": ["packages/*"], "npmClient": "yarn", - "command":{ + "command": { "publish": { + "allowBranch": ["release/*"], "message": "chore: 🤖 release %s" } } diff --git a/package.json b/package.json index 9b8c6b5d02..6f2bea3b8d 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,12 @@ ] }, "scripts": { + "prepare": "husky install", "bootstrap": "lerna bootstrap", "build": "lerna run build", "clean": "lerna clean --yes", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", - "prepare": "husky install", + "tag:did": "lerna version --tag-version-prefix=did-v", "reset-modules": "rm -rf node_modules/ yarn.lock packages/*/node_modules", "common-package:extension": "npx lerna add @portkey-wallet/utils --scope=web-extension && npx lerna add @portkey-wallet/hooks --scope=web-extension && npx lerna add @portkey-wallet/store --scope=web-extension && npx lerna add @portkey-wallet/constants --scope=web-extension", "app:did": "yarn workspace mobile-did run", From f4903a5589db4c9a01f9fd60edec5ed87a31fa74 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 27 Jun 2023 12:02:07 +0800 Subject: [PATCH 218/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20remove=20buyPage?= =?UTF-8?q?=20goHome=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Buy/BuyHome/index.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx index 48113efc1d..22db044b48 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx @@ -1,5 +1,5 @@ import { defaultColors } from 'assets/theme'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { pTd } from 'utils/unit'; import PageContainer from 'components/PageContainer'; @@ -13,9 +13,6 @@ import SellForm from './components/SellForm'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import ActionSheet from 'components/ActionSheet'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; -import { useIsFocused } from '@react-navigation/native'; -import navigationService from 'utils/navigationService'; -import CommonToast from 'components/CommonToast'; type TabItemType = { name: string; @@ -39,23 +36,10 @@ const tabList: TabItemType[] = [ export default function BuyHome() { const { t } = useLanguage(); const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); - const isFocused = useIsFocused(); const [selectTab, setSelectTab] = useState( isBuySectionShow ? PaymentTypeEnum.BUY : PaymentTypeEnum.SELL, ); - useEffect(() => { - if (!isFocused) return; - if ( - (selectTab === PaymentTypeEnum.BUY && !isBuySectionShow) || - (selectTab === PaymentTypeEnum.SELL && !isSellSectionShow) - ) { - CommonToast.fail('Sorry, the service you are using is temporarily unavailable.'); - navigationService.navigate('Tab'); - return; - } - }, [isBuySectionShow, isFocused, isSellSectionShow, selectTab]); - const onTabPress = (type: PaymentTypeEnum) => { if (type === PaymentTypeEnum.BUY && !isBuySectionShow) { ActionSheet.alert({ From 1278c66a6b22334d53be26c92ec933539c273097 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 27 Jun 2023 13:35:01 +0800 Subject: [PATCH 219/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20release=20did-v?= =?UTF-8?q?1.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/package.json | 4 ++-- packages/constants/package.json | 4 ++-- packages/contracts/package.json | 6 +++--- packages/graphql/package.json | 4 ++-- packages/hooks/package.json | 4 ++-- packages/i18n/package.json | 2 +- packages/mobile-app-did/package.json | 14 ++++++------- packages/socket/package.json | 2 +- packages/store/package.json | 8 ++++---- packages/types/package.json | 2 +- packages/utils/package.json | 22 ++++++++++----------- packages/web-extension-did/package.json | 22 ++++++++++----------- packages/web-page/package.json | 26 ++++++++++++------------- 13 files changed, 60 insertions(+), 60 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index 52bc45c719..a7beaaca9d 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", @@ -11,6 +11,6 @@ "start": "tsc --watch" }, "dependencies": { - "@portkey-wallet/utils": "^0.0.1" + "@portkey-wallet/utils": "^1.3.0" } } diff --git a/packages/constants/package.json b/packages/constants/package.json index 96ddbdc634..f35f306fb7 100644 --- a/packages/constants/package.json +++ b/packages/constants/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", @@ -12,6 +12,6 @@ "handle-network": "cd ./scripts && node handleNetwork.js" }, "dependencies": { - "@portkey-wallet/types": "^0.0.1" + "@portkey-wallet/types": "^1.3.0" } } diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 1a1e018a36..138b1f7d1e 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", @@ -11,8 +11,8 @@ "start": "tsc --watch" }, "dependencies": { - "@portkey-wallet/constants": "^0.0.1", - "@portkey-wallet/types": "^0.0.1", + "@portkey-wallet/constants": "^1.3.0", + "@portkey-wallet/types": "^1.3.0", "query-string": "^7.1.1" } } diff --git a/packages/graphql/package.json b/packages/graphql/package.json index ede79eb740..e4edc84cd5 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -3,14 +3,14 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "generate": "rm -rf ./*/__generated__ && node gqlg.js && graphql-codegen --config codegen.config.ts && eslint '*/**/*.{js,ts,tsx}' --quiet --fix" }, "dependencies": { "@apollo/client": "^3.7.3", - "@portkey-wallet/constants": "^0.0.1", + "@portkey-wallet/constants": "^1.3.0", "graphql": "^16.6.0", "subscriptions-transport-ws": "^0.11.0" }, diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 9b320fad51..e9a4c457c0 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", @@ -11,7 +11,7 @@ "start": "tsc --watch" }, "devDependencies": { - "@portkey-wallet/utils": "^0.0.1", + "@portkey-wallet/utils": "^1.3.0", "@types/react-native": "^0.69.7" } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 7692e6e03e..0752a8fb13 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index b6447e8866..feda59f352 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -1,6 +1,6 @@ { "name": "mobile-did", - "version": "1.1.5", + "version": "1.3.0", "private": true, "scripts": { "postinstall": "bash scripts/postinstall.sh", @@ -45,12 +45,12 @@ "dependencies": { "@invertase/react-native-apple-authentication": "^2.2.2", "@notifee/react-native": "^5.3.0", - "@portkey-wallet/constants": "^0.0.1", - "@portkey-wallet/contracts": "^0.0.1", - "@portkey-wallet/graphql": "^0.0.1", - "@portkey-wallet/i18n": "^0.0.1", - "@portkey-wallet/store": "^0.0.1", - "@portkey-wallet/utils": "^0.0.1", + "@portkey-wallet/constants": "^1.3.0", + "@portkey-wallet/contracts": "^1.3.0", + "@portkey-wallet/graphql": "^1.3.0", + "@portkey-wallet/i18n": "^1.3.0", + "@portkey-wallet/store": "^1.3.0", + "@portkey-wallet/utils": "^1.3.0", "@portkey/mobile-provider": "0.0.1-alpha.24", "@portkey/provider-types": "0.0.1-alpha.24", "@portkey/provider-utils": "0.0.1-alpha.24", diff --git a/packages/socket/package.json b/packages/socket/package.json index 2f06c0bc68..e530a5248d 100644 --- a/packages/socket/package.json +++ b/packages/socket/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/store/package.json b/packages/store/package.json index ec263b667e..0471dc8b8d 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", @@ -11,9 +11,9 @@ "start": "tsc --watch" }, "dependencies": { - "@portkey-wallet/api": "^0.0.1", - "@portkey-wallet/types": "^0.0.1", - "@portkey-wallet/utils": "^0.0.1", + "@portkey-wallet/api": "^1.3.0", + "@portkey-wallet/types": "^1.3.0", + "@portkey-wallet/utils": "^1.3.0", "aelf-sdk": "^3.2.40" } } diff --git a/packages/types/package.json b/packages/types/package.json index 2f76d29b75..2a8cc126dc 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "dependencies": { "@portkey/provider-types": "0.0.1-alpha.24" diff --git a/packages/utils/package.json b/packages/utils/package.json index efb9c67052..985259acaf 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.0.1", + "version": "1.3.0", "type": "commonjs", "scripts": { "prebuild": "rm -rf dist", @@ -11,20 +11,20 @@ "start": "tsc --watch" }, "dependencies": { - "@portkey-wallet/constants": "^0.0.1", - "@portkey-wallet/types": "^0.0.1", - "query-string": "^7.1.1", - "is-url": "^1.2.4" + "@portkey-wallet/constants": "^1.3.0", + "@portkey-wallet/types": "^1.3.0", + "is-url": "^1.2.4", + "query-string": "^7.1.1" }, "devDependencies": { - "@types/react-native": "^0.69.7", + "@portkey/provider-types": "0.0.1-alpha.24", + "@portkey/provider-utils": "0.0.1-alpha.24", + "@portkey/providers": "0.0.1-alpha.24", + "@react-native-async-storage/async-storage": "^1.17.6", "@types/is-url": "^1.2.30", + "@types/react-native": "^0.69.7", "expo-device": "^4.3.0", "expo-local-authentication": "^12.3.0", - "expo-secure-store": "^11.3.0", - "@react-native-async-storage/async-storage": "^1.17.6", - "@portkey/provider-types": "0.0.1-alpha.24", - "@portkey/provider-utils": "0.0.1-alpha.24", - "@portkey/providers": "0.0.1-alpha.24" + "expo-secure-store": "^11.3.0" } } diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index ae2cef0995..efb6c77e35 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -7,20 +7,19 @@ "@ant-design/icons": "^4.7.0", "@babel/runtime": "^7.19.0", "@metamask/safe-event-emitter": "^2.0.0", - "@portkey-wallet/api": "^0.0.1", - "@portkey-wallet/constants": "^0.0.1", - "@portkey-wallet/contracts": "^0.0.1", - "@portkey-wallet/graphql": "^0.0.1", - "@portkey-wallet/hooks": "^0.0.1", - "@portkey-wallet/i18n": "^0.0.1", - "@portkey-wallet/store": "^0.0.1", - "@portkey-wallet/types": "^0.0.1", - "@portkey-wallet/utils": "^0.0.1", + "@portkey-wallet/api": "^1.3.0", + "@portkey-wallet/constants": "^1.3.0", + "@portkey-wallet/contracts": "^1.3.0", + "@portkey-wallet/graphql": "^1.3.0", + "@portkey-wallet/hooks": "^1.3.0", + "@portkey-wallet/i18n": "^1.3.0", + "@portkey-wallet/store": "^1.3.0", + "@portkey-wallet/types": "^1.3.0", + "@portkey-wallet/utils": "^1.3.0", "@portkey/extension-provider": "0.0.1-alpha.24", - "@portkey/provider-utils": "0.0.1-alpha.24", "@portkey/provider-types": "0.0.1-alpha.24", + "@portkey/provider-utils": "0.0.1-alpha.24", "@portkey/providers": "0.0.1-alpha.24", - "readable-stream": "4.4.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", @@ -51,6 +50,7 @@ "react-router": "^6.4.0", "react-router-dom": "^6.4.0", "react-use": "^17.4.0", + "readable-stream": "4.4.0", "redux": "^4.2.0", "redux-persist": "^6.0.0", "redux-persist-webextension-storage": "^1.0.2", diff --git a/packages/web-page/package.json b/packages/web-page/package.json index 9fe624c040..3beeef4485 100644 --- a/packages/web-page/package.json +++ b/packages/web-page/package.json @@ -1,6 +1,6 @@ { "name": "web-page", - "version": "0.1.0", + "version": "1.3.0", "private": true, "scripts": { "dev": "next dev", @@ -17,16 +17,16 @@ "@ant-design/icons": "^4.7.0", "@matt-block/react-recaptcha-v2": "^1.0.8", "@micro-zoe/micro-app": "^0.8.10", - "@portkey-wallet/api": "^0.0.1", - "@portkey-wallet/constants": "^0.0.1", - "@portkey-wallet/contracts": "^0.0.1", - "@portkey-wallet/graphql": "^0.0.1", - "@portkey-wallet/hooks": "^0.0.1", - "@portkey-wallet/i18n": "^0.0.1", - "@portkey-wallet/store": "^0.0.1", - "@portkey-wallet/types": "^0.0.1", - "@portkey-wallet/utils": "^0.0.1", - "next-transpile-modules": "^10.0.0", + "@portkey-wallet/api": "^1.3.0", + "@portkey-wallet/constants": "^1.3.0", + "@portkey-wallet/contracts": "^1.3.0", + "@portkey-wallet/graphql": "^1.3.0", + "@portkey-wallet/hooks": "^1.3.0", + "@portkey-wallet/i18n": "^1.3.0", + "@portkey-wallet/store": "^1.3.0", + "@portkey-wallet/types": "^1.3.0", + "@portkey-wallet/utils": "^1.3.0", + "@react-oauth/google": "^0.8.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/nextjs": "^7.19.0", "@walletconnect/ethereum-provider": "^1.7.8", @@ -50,15 +50,15 @@ "next-babel-conditional-ssg-ssr": "https://github.com/Anthonyzou/next-babel-conditional-ssg-ssr.git#patch-1", "next-compose-plugins": "^2.2.1", "next-plugin-antd-less": "^1.8.0", + "next-transpile-modules": "^10.0.0", "next-with-less": "^2.0.5", "query-string": "^7.1.1", "react-i18next": "^11.18.0", "react-query": "^3.39.2", "react-redux": "^8.0.5", "react-use": "^17.4.0", - "web3": "^1.7.4", "reactjs-social-login": "^2.6.2", - "@react-oauth/google": "^0.8.0", + "web3": "^1.7.4", "web3-core": "^1.7.4" }, "resolutions": { From 56cc3f282be7749a16b122ac035d29c2b20d5fc8 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 27 Jun 2023 14:58:49 +0800 Subject: [PATCH 220/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20app=20whiteSpace?= =?UTF-8?q?=20and=20http=20toast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/network.ts | 2 +- packages/mobile-app-did/js/hooks/discover.ts | 27 ++++++++ .../pages/Discover/DiscoverSearch/index.tsx | 68 +++++++++++++++---- .../DiscoverCmsListSection/index.tsx | 40 ++++++++++- .../SearchDiscoverSection/index.tsx | 61 +++++++++++++---- packages/store/store-ca/discover/slice.ts | 4 ++ packages/store/store-ca/discover/type.ts | 2 +- packages/utils/dapp/browser.ts | 21 +++--- 8 files changed, 182 insertions(+), 43 deletions(-) diff --git a/packages/constants/constants-ca/network.ts b/packages/constants/constants-ca/network.ts index cedcba6d2e..acc046ba08 100644 --- a/packages/constants/constants-ca/network.ts +++ b/packages/constants/constants-ca/network.ts @@ -1 +1 @@ -export * from './network-mainnet'; +export * from './network-test1'; diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 5f967a9550..14d478b393 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -1,6 +1,7 @@ import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { + addUrlToWhiteList, changeDrawerOpenStatus, setActiveTab, addRecordsItem, @@ -8,6 +9,7 @@ import { initNetworkDiscoverMap, } from '@portkey-wallet/store/store-ca/discover/slice'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { useCallback } from 'react'; export const useIsDrawerOpen = () => useAppCASelector(state => state.discover.isDrawerOpen); @@ -29,3 +31,28 @@ export const useDiscoverJumpWithNetWork = () => { return discoverJump; }; + +// discover whiteList +export const useDiscoverWhiteList = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const { discoverMap } = useAppCASelector(state => state.discover); + + const checkIsInWhiteList = useCallback( + (url: string) => { + console.log('discoverMap', discoverMap && discoverMap[networkType]?.whiteList); + return discoverMap && discoverMap[networkType]?.whiteList.includes(url); + }, + [discoverMap, networkType], + ); + + const upDateWhiteList = useCallback( + (url: string) => { + dispatch(addUrlToWhiteList({ url, networkType })); + }, + [dispatch, networkType], + ); + + return { checkIsInWhiteList, upDateWhiteList }; +}; diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 687dabd3a4..432c44adac 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -14,16 +14,24 @@ import fonts from 'assets/theme/fonts'; import RecordSection from '../components/RecordSection'; import SearchDiscoverSection from '../components/SearchDiscoverSection'; import { isIOS } from '@rneui/base'; -import { checkIsUrl, getHost, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; +import { + checkIsUrl, + getHost, + getProtocolAndHost, + isDangerousLink, + prefixUrlWithProtocol, +} from '@portkey-wallet/utils/dapp/browser'; import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; -import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import { useDiscoverJumpWithNetWork, useDiscoverWhiteList } from 'hooks/discover'; +import ActionSheet from 'components/ActionSheet'; export default function DiscoverSearch() { const { t } = useLanguage(); const discoverGroupList = useDiscoverGroupList(); const jumpToWebview = useDiscoverJumpWithNetWork(); + const { checkIsInWhiteList, upDateWhiteList } = useDiscoverWhiteList(); const timerRef = useRef(null); const iptRef = useRef(); @@ -31,9 +39,8 @@ export default function DiscoverSearch() { const [showRecord, setShowRecord] = useState(true); const [filteredDiscoverList, setFilteredDiscoverList] = useState([]); - const navBack = useCallback(() => { - navigationService.goBack(); - }, []); + const clearText = useCallback(() => setValue(''), []); + const navBack = useCallback(() => navigationService.goBack(), []); const flatList = useMemo((): DiscoverItem[] => { const list = [] as DiscoverItem[]; @@ -46,31 +53,62 @@ export default function DiscoverSearch() { return list; }, [discoverGroupList]); - const clearText = useCallback(() => setValue(''), []); - useEffect(() => { if (!value) setShowRecord(true); }, [value]); - const onSearch = useCallback(() => { - const newValue = value.replace(/\s+/g, ''); - if (!newValue) return; - - if (checkIsUrl(newValue)) { + const onDiscoverJump = useCallback( + (name: string, url: string) => { jumpToWebview({ item: { id: Date.now(), - name: getHost(prefixUrlWithProtocol(newValue)), - url: prefixUrlWithProtocol(newValue), + name, + url, }, }); + }, + [jumpToWebview], + ); + + const onSearch = useCallback(() => { + const newValue = value.replace(/\s+/g, '').toLocaleLowerCase(); + if (!newValue) return; + + if (checkIsUrl(newValue)) { + const protocolAndHost = getProtocolAndHost(newValue); + console.log(protocolAndHost, checkIsInWhiteList(protocolAndHost), !isDangerousLink(protocolAndHost)); + if (checkIsInWhiteList(protocolAndHost) || !isDangerousLink(protocolAndHost)) { + onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); + } else { + ActionSheet.alert({ + title: 'title', + message: 'message', + buttons: [ + { + title: 'Get it', + type: 'solid', + onPress: () => { + onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); + }, + }, + { + title: 'Disable notification', + type: 'solid', + onPress: () => { + onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); + upDateWhiteList(protocolAndHost); + }, + }, + ], + }); + } } else { // else search in Discover list const filterList = flatList.filter(item => item.title.replace(/\s+/g, '').includes(newValue)); setFilteredDiscoverList(filterList); setShowRecord(false); } - }, [flatList, jumpToWebview, value]); + }, [checkIsInWhiteList, flatList, onDiscoverJump, upDateWhiteList, value]); useFocusEffect( useCallback(() => { diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 4866b209df..215eb7cf29 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -5,17 +5,20 @@ import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { TextL, TextM, TextS } from 'components/CommonText'; -import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import { useDiscoverJumpWithNetWork, useDiscoverWhiteList } from 'hooks/discover'; import React, { useCallback } from 'react'; import { ScrollView, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; +import ActionSheet from 'components/ActionSheet'; +import { isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; export function DiscoverCmsListSection() { const GroupList = useDiscoverGroupList(); const { s3Url } = useCurrentNetworkInfo(); const discoverJump = useDiscoverJumpWithNetWork(); + const { checkIsInWhiteList, upDateWhiteList } = useDiscoverWhiteList(); - const onJump = useCallback( + const onDiscoverJump = useCallback( (i: DiscoverItem) => { discoverJump({ item: { @@ -28,6 +31,37 @@ export function DiscoverCmsListSection() { [discoverJump], ); + const onClickJump = useCallback( + (i: DiscoverItem) => { + if (checkIsInWhiteList(i.url) || !isDangerousLink(i.url)) { + onDiscoverJump(i); + } else { + ActionSheet.alert({ + title: 'title', + message: 'message', + buttons: [ + { + title: 'Get it', + type: 'solid', + onPress: () => { + onDiscoverJump(i); + }, + }, + { + title: 'Disable notification', + type: 'solid', + onPress: () => { + onDiscoverJump(i); + upDateWhiteList(i.url); + }, + }, + ], + }); + } + }, + [checkIsInWhiteList, onDiscoverJump, upDateWhiteList], + ); + return ( {GroupList.map((group, index) => ( @@ -35,7 +69,7 @@ export function DiscoverCmsListSection() { {group.title} {group.items.map((item, i) => ( - onJump(item)}> + onClickJump(item)}> diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx index 88b0a50f6f..f73d5d8bb9 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { StyleSheet, View, ScrollView, TouchableOpacity } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { useLanguage } from 'i18n/hooks'; @@ -11,7 +11,9 @@ import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { defaultColors } from 'assets/theme'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; -import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import { useDiscoverJumpWithNetWork, useDiscoverWhiteList } from 'hooks/discover'; +import { isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; +import ActionSheet from 'components/ActionSheet'; interface ISearchDiscoverSectionProps { searchedDiscoverList: DiscoverItem[]; } @@ -21,23 +23,58 @@ export default function SearchDiscoverSection(props: ISearchDiscoverSectionProps const { searchedDiscoverList } = props; const { s3Url } = useCurrentNetworkInfo(); const jumpToWebview = useDiscoverJumpWithNetWork(); + const { checkIsInWhiteList, upDateWhiteList } = useDiscoverWhiteList(); - const onJump = (i: DiscoverItem) => { - jumpToWebview({ - item: { - id: Date.now(), - name: i.title, - url: i?.url ?? i?.description, - }, - }); - }; + const onDiscoverJump = useCallback( + (i: DiscoverItem) => { + jumpToWebview({ + item: { + id: Date.now(), + name: i.title, + url: i?.url ?? i?.description, + }, + }); + }, + [jumpToWebview], + ); + + const onClickJump = useCallback( + (i: DiscoverItem) => { + if (checkIsInWhiteList(i.url) || !isDangerousLink(i.url)) { + onDiscoverJump(i); + } else { + ActionSheet.alert({ + title: 'title', + message: 'message', + buttons: [ + { + title: 'Get it', + type: 'solid', + onPress: () => { + onDiscoverJump(i); + }, + }, + { + title: 'Disable notification', + type: 'solid', + onPress: () => { + onDiscoverJump(i); + upDateWhiteList(i.url); + }, + }, + ], + }); + } + }, + [checkIsInWhiteList, onDiscoverJump, upDateWhiteList], + ); if (searchedDiscoverList.length === 0) return ; return ( {searchedDiscoverList?.map((item, index) => ( - onJump(item)}> + onClickJump(item)}> diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 6da391fae0..fd879c44cc 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -29,6 +29,9 @@ export const discoverSlice = createSlice({ [payload]: JSON.parse(JSON.stringify(initNetworkData)), }; }, + addUrlToWhiteList: (state, { payload }: { payload: { url: string; networkType: NetworkType } }) => { + state.discoverMap?.[payload.networkType]?.whiteList.push(payload.url); + }, addRecordsItem: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { const { networkType, url } = payload; if (!state.discoverMap) return; @@ -121,6 +124,7 @@ export const discoverSlice = createSlice({ export const { initNetworkDiscoverMap, + addUrlToWhiteList, addRecordsItem, upDateRecordsItem, clearRecordsList, diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index 161d573787..a146e27307 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -9,7 +9,7 @@ export interface ITabItem { export interface IDiscoverNetworkStateType { recordsList: ITabItem[]; - whiteList: any[]; + whiteList: string[]; tabs: ITabItem[]; } diff --git a/packages/utils/dapp/browser.ts b/packages/utils/dapp/browser.ts index 50b3b9c10c..5ab739932d 100644 --- a/packages/utils/dapp/browser.ts +++ b/packages/utils/dapp/browser.ts @@ -12,22 +12,12 @@ export function getUrlObj(url: string) { return new Url(url); } -/** - * check if url is ip - * @param url - * @returns - */ -export const isIp = (url: string): boolean => { - return /((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))/.test(url); -}; - /** * check if url is ip * @param url * @returns */ export const isDangerousLink = (url: string): boolean => { - if (isIp(url)) return true; return /^(?:http:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!&',;=.+]+$/g.test(url); }; @@ -39,11 +29,11 @@ export const isDangerousLink = (url: string): boolean => { * @returns - String corresponding to sanitized input depending if it's a search or url */ export const prefixUrlWithProtocol = (url: string, defaultProtocol = 'https://') => { - if (isIp(url)) defaultProtocol = 'http://'; const hasProtocol = /^[a-z]*:\/\//.test(url); const sanitizedURL = hasProtocol ? url : `${defaultProtocol}${url}`; return sanitizedURL; }; + /** * Return host from url string * @@ -62,6 +52,15 @@ export function getHost(url: string, defaultProtocol = 'https://') { return result; } +/** + * + * @param url + * @returns + */ +export function getProtocolAndHost(url: string) { + const { protocol, hostname } = getUrlObj(url); + return `${protocol}//${hostname}`; +} /** * getFaviconUrl From 68b47c706cb5f3a4bcaf85143ebbcab59065f742 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 27 Jun 2023 15:27:18 +0800 Subject: [PATCH 221/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20checkVerif?= =?UTF-8?q?icationCode=20verifierCodeOperationType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Guardian/VerifierDetails/index.tsx | 33 ++++++++++++++++++- .../components/GuardianItem/index.tsx | 21 ++++++++++-- packages/types/verifier.ts | 16 ++++++++- .../app/web/pages/VerifierAccount/index.tsx | 26 +++++++++++++-- .../pages/components/VerifierPage/index.tsx | 7 ++-- 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index dc1dfd0418..b117e98f7c 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -11,6 +11,7 @@ import { ApprovalType, RecaptchaType, VerificationType, + VerifierCodeOperationType, VerifierInfo, VerifyStatus, } from '@portkey-wallet/types/verifier'; @@ -110,6 +111,32 @@ export default function VerifierDetails() { const isRequestResult = pin && verificationType === VerificationType.register && managerAddress; Loading.show(isRequestResult ? { text: 'Creating address on the chain...' } : undefined); try { + let verifierCodeOperationType: VerifierCodeOperationType; + switch (verificationType) { + case VerificationType.register: + verifierCodeOperationType = VerifierCodeOperationType.register; + break; + case VerificationType.communityRecovery: + verifierCodeOperationType = VerifierCodeOperationType.communityRecovery; + break; + case VerificationType.addGuardian: + case VerificationType.addGuardianByApprove: + verifierCodeOperationType = VerifierCodeOperationType.addGuardian; + break; + case VerificationType.deleteGuardian: + verifierCodeOperationType = VerifierCodeOperationType.removeGuardian; + break; + case VerificationType.editGuardian: + verifierCodeOperationType = VerifierCodeOperationType.editGuardian; + break; + case VerificationType.removeOtherManager: + verifierCodeOperationType = VerifierCodeOperationType.removeOtherManager; + break; + default: + verifierCodeOperationType = VerifierCodeOperationType.unknown; + break; + } + const rst = await verification.checkVerificationCode({ params: { type: LoginType[guardianItem?.guardianType as LoginType], @@ -118,6 +145,7 @@ export default function VerifierDetails() { ...requestCodeResult, verifierId: guardianItem?.verifier?.id, chainId: originChainId, + verifierCodeOperationType, }, }); !isRequestResult && CommonToast.success('Verified Successfully'); @@ -129,7 +157,10 @@ export default function VerifierDetails() { switch (verificationType) { case VerificationType.communityRecovery: - case VerificationType.optGuardianApproval: + case VerificationType.addGuardianByApprove: + case VerificationType.editGuardian: + case VerificationType.deleteGuardian: + case VerificationType.removeOtherManager: setGuardianStatus({ requestCodeResult: requestCodeResult, status: VerifyStatus.Verified, diff --git a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx index 6c1b81c5c0..8069f223bf 100644 --- a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx @@ -64,9 +64,24 @@ function GuardianItemButton({ const { status, requestCodeResult } = itemStatus || {}; const verifyToken = useVerifyToken(); const guardianInfo = useMemo(() => { - let _verificationType = VerificationType.optGuardianApproval; - if (approvalType === ApprovalType.communityRecovery) { - _verificationType = VerificationType.communityRecovery; + let _verificationType: VerificationType; + switch (approvalType) { + case ApprovalType.addGuardian: + _verificationType = VerificationType.addGuardianByApprove; + break; + case ApprovalType.editGuardian: + _verificationType = VerificationType.editGuardian; + break; + case ApprovalType.deleteGuardian: + _verificationType = VerificationType.deleteGuardian; + break; + case ApprovalType.removeOtherManager: + _verificationType = VerificationType.removeOtherManager; + break; + case ApprovalType.communityRecovery: + default: + _verificationType = VerificationType.communityRecovery; + break; } return { guardianItem, diff --git a/packages/types/verifier.ts b/packages/types/verifier.ts index 0cb7b9ce56..444093bc18 100644 --- a/packages/types/verifier.ts +++ b/packages/types/verifier.ts @@ -19,7 +19,10 @@ export enum VerificationType { addGuardian, setLoginAccount, addManager, - optGuardianApproval, + editGuardian, + removeOtherManager, + addGuardianByApprove, + deleteGuardian, } export enum ApprovalType { @@ -36,6 +39,17 @@ export enum RecaptchaType { optGuardian = 2, } +// Indicates the type of operation to generate a signature file +export enum VerifierCodeOperationType { + unknown = 0, + register = 1, + communityRecovery = 2, + addGuardian = 3, + removeGuardian = 4, + editGuardian = 5, + removeOtherManager = 6, +} + export interface VerifierInfo { verifierId: string; verificationDoc: string; diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 364a861bdf..33650bca45 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -4,7 +4,7 @@ import { useAppDispatch, useLoginInfo, useGuardiansInfo, useUserInfo, useLoading import { useCallback, useMemo } from 'react'; import { message } from 'antd'; import { setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; -import { RecaptchaType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifierCodeOperationType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import useLocationState from 'hooks/useLocationState'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { handleGuardian } from 'utils/sandboxUtil/handleGuardian'; @@ -198,6 +198,27 @@ export default function VerifierAccount() { return RecaptchaType.optGuardian; }, [state]); + const verifierCodeOperationType = useMemo(() => { + switch (state) { + case 'register': + return VerifierCodeOperationType.register; + case 'login': + return VerifierCodeOperationType.communityRecovery; + case 'guardians/add': + return VerifierCodeOperationType.addGuardian; + case 'guardians/edit': + return VerifierCodeOperationType.editGuardian; + case 'guardians/del': + return VerifierCodeOperationType.removeGuardian; + default: + if (state?.indexOf('removeManage') !== -1) { + return VerifierCodeOperationType.removeOtherManager; + } else { + return VerifierCodeOperationType.unknown; + } + } + }, [state]); + const renderContent = useMemo( () => (
    @@ -208,10 +229,11 @@ export default function VerifierAccount() { guardianType={loginAccount?.loginType} onSuccess={onSuccess} recaptchaType={recaptchaType} + verifierCodeOperationType={verifierCodeOperationType} />
    ), - [currentGuardian, isInitStatus, loginAccount, onSuccess, recaptchaType], + [currentGuardian, isInitStatus, loginAccount, onSuccess, recaptchaType, verifierCodeOperationType], ); const props = useMemo( diff --git a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx index c68d85239d..4d1e9af314 100644 --- a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx @@ -17,7 +17,7 @@ import { verification } from 'utils/api'; import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCommonState } from 'store/Provider/hooks'; import { useLocation } from 'react-router'; -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; const MAX_TIMER = 60; @@ -28,6 +28,7 @@ enum VerificationError { interface VerifierPageProps { recaptchaType: RecaptchaType; + verifierCodeOperationType: VerifierCodeOperationType; loginAccount?: LoginInfo; currentGuardian?: UserGuardianItem; guardianType?: LoginType; @@ -37,6 +38,7 @@ interface VerifierPageProps { export default function VerifierPage({ recaptchaType, + verifierCodeOperationType, currentGuardian, guardianType, isInitStatus, @@ -79,6 +81,7 @@ export default function VerifierPage({ verificationCode: code, verifierId: currentGuardian.verifier?.id || '', chainId: originChainId, + verifierCodeOperationType, }, }); @@ -100,7 +103,7 @@ export default function VerifierPage({ message.error(_error); } }, - [guardianType, originChainId, currentGuardian, setLoading, onSuccess, t], + [guardianType, originChainId, currentGuardian, setLoading, onSuccess, t, verifierCodeOperationType], ); const resendCode = useCallback(async () => { From 2a5559f100b6a453e3a05a029a63ac413315fdc5 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 27 Jun 2023 16:57:04 +0800 Subject: [PATCH 222/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20scan=20lot?= =?UTF-8?q?tie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/hooks/authentication.ts | 2 +- .../js/pages/Login/components/QRCode.tsx | 22 ++++++++++++++----- .../pages/Login/components/scanLoading.json | 1 + .../mobile-app-did/js/pages/Login/styles.ts | 7 ++++++ packages/utils/balance.ts | 2 +- 5 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Login/components/scanLoading.json diff --git a/packages/mobile-app-did/js/hooks/authentication.ts b/packages/mobile-app-did/js/hooks/authentication.ts index f82e3d9624..5163e871fa 100644 --- a/packages/mobile-app-did/js/hooks/authentication.ts +++ b/packages/mobile-app-did/js/hooks/authentication.ts @@ -146,7 +146,7 @@ export function useAppleAuthentication() { const iosPromptAsync = useCallback(async () => { setResponse(undefined); try { - const appleInfo = await await appleAuth.performRequest({ + const appleInfo = await appleAuth.performRequest({ requestedOperation: appleAuth.Operation.LOGIN, requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME], }); diff --git a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx index be2c1a1cfa..1732d1aa99 100644 --- a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx @@ -10,7 +10,7 @@ import navigationService from 'utils/navigationService'; import styles from '../styles'; import Touchable from 'components/Touchable'; import GStyles from 'assets/theme/GStyles'; -import { TextS, TextXXXL } from 'components/CommonText'; +import { TextM, TextS, TextXXXL } from 'components/CommonText'; import { PageLoginType } from '../types'; import { useCurrentWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { WalletInfoType } from '@portkey-wallet/types/wallet'; @@ -25,6 +25,8 @@ import { useGetDeviceInfo } from 'hooks/device'; import { DEVICE_INFO_VERSION } from '@portkey-wallet/constants/constants-ca/device'; import CommonQRCodeStyled from 'components/CommonQRCodeStyled'; import { useCheckManager } from 'hooks/useLogOut'; +import Lottie from 'lottie-react-native'; +import { pTd } from 'utils/unit'; export default function QRCode({ setLoginType }: { setLoginType: (type: PageLoginType) => void }) { const { walletInfo, currentNetwork } = useCurrentWallet(); @@ -108,14 +110,22 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi }, [currentNetwork, getDeviceInfo, newWallet]); return ( - + setLoginType(PageLoginType.referral)}> - Scan code to log in - Please use the Portkey DApp to scan the QR code - - + + Scan code to log in + + Please use the Portkey DApp to scan the QR code + + + + + + Waiting for authorization... + + ); diff --git a/packages/mobile-app-did/js/pages/Login/components/scanLoading.json b/packages/mobile-app-did/js/pages/Login/components/scanLoading.json new file mode 100644 index 0000000000..22e76a6712 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Login/components/scanLoading.json @@ -0,0 +1 @@ +{"v":"5.6.9","fr":29.9700012207031,"ip":0,"op":62.0000025253118,"w":280,"h":280,"nm":"Comp 3","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"voice Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[140,140,0],"ix":2},"a":{"a":0,"k":[270,270,0],"ix":1},"s":{"a":0,"k":[94,94,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.022,0],[0,0],[0,0.023],[0,0],[-0.021,0.002],[0,0],[0,7.956],[0,0],[0.023,0],[0,0],[0,-0.023],[0,0],[7.542,-0.351],[0,7.94],[0,0],[0.022,0],[0,0],[0,-0.023],[0,0],[-7.895,-0.994],[0,0],[0,-0.021],[0,0],[0.023,0],[0,0],[0,-0.023],[0,0],[-0.023,0],[0,0],[0,0.022],[0,0]],"o":[[0,0],[-0.023,0],[0,0],[0,-0.021],[0,0],[7.893,-0.994],[0,0],[0,-0.023],[0,0],[-0.023,0],[0,0],[0,7.55],[-8.022,0.374],[0,0],[0,-0.023],[0,0],[-0.023,0],[0,0],[0,7.958],[0,0],[0.021,0.002],[0,0],[0,0.023],[0,0],[-0.023,0],[0,0],[0,0.022],[0,0],[0.022,0],[0,0],[0,-0.023]],"v":[[10.752,11.56],[0.917,11.56],[0.876,11.518],[0.876,6.398],[0.912,6.358],[1.936,6.228],[15.75,-9.435],[15.75,-13.268],[15.708,-13.31],[14.042,-13.31],[14,-13.268],[14,-9.841],[0.668,4.585],[-14,-9.398],[-14,-13.268],[-14.041,-13.31],[-15.708,-13.31],[-15.75,-13.268],[-15.75,-9.438],[-1.933,6.228],[-0.912,6.358],[-0.875,6.398],[-0.875,11.518],[-0.917,11.56],[-10.749,11.56],[-10.791,11.603],[-10.791,13.27],[-10.749,13.31],[10.752,13.31],[10.793,13.27],[10.793,11.603]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.301384151683,0.525550034467,0.973881740196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270,284.189],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.442,0],[0,5.439],[0,0],[5.439,0],[0,-5.439],[0,0]],"o":[[5.439,0],[0,0],[0,-5.439],[-5.442,0],[0,0],[0,5.439]],"v":[[0.001,20.946],[9.866,11.081],[9.866,-11.08],[0.001,-20.946],[-9.866,-11.08],[-9.866,11.081]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.301384151683,0.525550034467,0.973881740196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270,263.447],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-39.488],[39.488,0],[0,39.488],[-39.488,0]],"o":[[0,39.488],[-39.488,0],[0,-39.488],[39.488,0]],"v":[[71.5,0],[0,71.5],[-71.5,0],[0,-71.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.301384151683,0.525550034467,0.973881740196,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[270,270],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Base Layer 4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[49]},{"t":45.0000018328876,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[139.029,140.234,0],"ix":2},"a":{"a":0,"k":[9.953,-2.402,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":0,"s":[31.532,31.532,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":20,"s":[69.368,69.368,100]},{"t":45.0000018328876,"s":[84.882,84.882,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[331.855,331.855],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.356862745098,0.556862745098,0.956862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.356862745098,0.556862745098,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11.178,-2.697],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[97.195,97.195],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":62.0000025253118,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Base Layer 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":34,"s":[27]},{"t":59.0000024031193,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[140.971,139.766,0],"ix":2},"a":{"a":0,"k":[12.553,-3.029,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":14,"s":[31.532,31.532,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":34,"s":[69.368,69.368,100]},{"t":59.0000024031193,"s":[84.882,84.882,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[331.855,331.855],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.356862745098,0.556862745098,0.956862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.356862745098,0.556862745098,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11.178,-2.697],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[97.195,97.195],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":62.0000025253118,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Comp 1","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[140,140,0],"ix":2},"a":{"a":0,"k":[140,140,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":280,"h":280,"ip":0,"op":62.0000025253118,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/packages/mobile-app-did/js/pages/Login/styles.ts b/packages/mobile-app-did/js/pages/Login/styles.ts index 3cda67d5f4..5618300899 100644 --- a/packages/mobile-app-did/js/pages/Login/styles.ts +++ b/packages/mobile-app-did/js/pages/Login/styles.ts @@ -25,6 +25,9 @@ const styles = StyleSheet.create({ paddingVertical: 24, minHeight: Math.min(screenHeight * 0.58, 494), }, + qrCodeCard: { + paddingBottom: 0, + }, inputContainerStyle: { marginTop: 8, flex: 1, @@ -82,5 +85,9 @@ const styles = StyleSheet.create({ borderRadius: pTd(10), paddingVertical: 1, }, + scanLoading: { + height: pTd(18), + marginRight: pTd(8), + }, }); export default styles; diff --git a/packages/utils/balance.ts b/packages/utils/balance.ts index 03ecff687e..59e04c5185 100644 --- a/packages/utils/balance.ts +++ b/packages/utils/balance.ts @@ -8,7 +8,7 @@ const wallet1 = AElf.wallet.getWalletByPrivateKey(privateKey1); export const getELFChainBalance = async (tokenContract: any, symbol: string, owner: string) => { let balance; if (tokenContract instanceof ContractBasic) { - const req = await await tokenContract.callViewMethod('GetBalance', { + const req = await tokenContract.callViewMethod('GetBalance', { symbol, owner, }); From 8589720cf75e6230260cc690cba8280d6aeb0bc2 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 27 Jun 2023 17:02:37 +0800 Subject: [PATCH 223/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20buy=20f?= =?UTF-8?q?iat=20countryName?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/payment/actions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/store/store-ca/payment/actions.ts b/packages/store/store-ca/payment/actions.ts index 2267c745dc..eb420dc29a 100644 --- a/packages/store/store-ca/payment/actions.ts +++ b/packages/store/store-ca/payment/actions.ts @@ -21,7 +21,6 @@ export const fetchBuyFiatListAsync = createAsyncThunk('payment/fetch return Object.values(fiatMap).map(item => ({ ...item, - countryName: countryCodeMap[item.country]?.country, icon: countryCodeMap[item.country]?.icon, })); }); @@ -44,7 +43,6 @@ export const fetchSellFiatListAsync = createAsyncThunk('payment/fetc return Object.values(fiatMap).map(item => ({ ...item, - countryName: countryCodeMap[item.country]?.country, icon: countryCodeMap[item.country]?.icon, })); }); From ac0add58983912304810795c0a327eaf2ee37483 Mon Sep 17 00:00:00 2001 From: Portkey-David <120542595+Portkey-David@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:41:14 +0800 Subject: [PATCH 224/893] Update versionShow --- .../mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx index 789fc02524..45aeb81401 100644 --- a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx @@ -24,7 +24,7 @@ const AboutUs = () => { Portkey - V {Application.nativeApplicationVersion} + {`V${Application.nativeApplicationVersion} beta`} Date: Wed, 28 Jun 2023 14:36:28 +0800 Subject: [PATCH 225/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20check=20register?= =?UTF-8?q?=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/.eslintrc.json | 3 +- .../web/controllers/PermissionController.ts | 56 ++++++----- .../web/hooks/useCheckRegisterOnNetwork.ts | 27 ------ .../app/web/hooks/useFetchDidWallet.ts | 10 +- .../app/web/hooks/useUpdateContact.ts | 14 --- .../app/web/pages/SetWalletPin/index.tsx | 18 ++-- .../components/PermissionCheck/index.tsx | 92 ++++++++++--------- .../serviceWorker/ServiceWorkerInstantiate.ts | 48 +++------- .../app/web/serviceWorker/index.ts | 8 +- .../serviceWorker/serviceWorkerListener.ts | 6 +- .../web-extension-did/app/web/types/SW.ts | 5 +- .../app/web/types/index.d.ts | 2 - .../app/web/utils/lib/SWGetReduxStore.ts | 6 ++ .../app/web/utils/lib/serviceWorkerAction.ts | 10 +- .../app/web/utils/storage/storage.ts | 1 - 15 files changed, 111 insertions(+), 195 deletions(-) delete mode 100644 packages/web-extension-did/app/web/hooks/useCheckRegisterOnNetwork.ts delete mode 100644 packages/web-extension-did/app/web/hooks/useUpdateContact.ts diff --git a/packages/web-extension-did/.eslintrc.json b/packages/web-extension-did/.eslintrc.json index 8063f716f1..d13c4da6c8 100644 --- a/packages/web-extension-did/.eslintrc.json +++ b/packages/web-extension-did/.eslintrc.json @@ -32,7 +32,8 @@ "@typescript-eslint/explicit-module-boundary-types": 0, "@typescript-eslint/ban-types": 0, "@typescript-eslint/no-explicit-any": 0, - "no-inline-styles/no-inline-styles": 2 + "no-inline-styles/no-inline-styles": 2, + "@typescript-eslint/no-non-null-assertion": 0 }, "env": { "browser": true, diff --git a/packages/web-extension-did/app/web/controllers/PermissionController.ts b/packages/web-extension-did/app/web/controllers/PermissionController.ts index 1eb1b9f7ce..67b9886528 100644 --- a/packages/web-extension-did/app/web/controllers/PermissionController.ts +++ b/packages/web-extension-did/app/web/controllers/PermissionController.ts @@ -3,16 +3,15 @@ import { PromptRouteTypes } from 'messages/InternalMessageTypes'; import NotificationService from 'service/NotificationService'; import { CreatePromptType } from 'types'; import type { PortKeyResultType } from 'utils/errorHandler'; -import { getLocalStorage } from 'utils/storage/chromeStorage'; -// import pendingTaskService from 'controllers/pendingController'; +import { getCurrentNetworkWallet } from 'utils/lib/SWGetReduxStore'; export default class PermissionController { notificationService: NotificationService; - whitelist?: string[]; + whitelist: string[]; getPassword?: () => string | null; constructor({ notificationService, - whitelist, + whitelist = [], getPassword, }: { notificationService: NotificationService; @@ -64,40 +63,39 @@ export default class PermissionController { } } - getRegisterStatus() { - return getLocalStorage('registerStatus'); + checkAllowMethod(methodName: string) { + return this.whitelist.includes(methodName) || isNotificationEvents(methodName); } - async checkIsRegister() { - const registerStatus = await getLocalStorage('registerStatus'); - if (registerStatus === 'Registered') { - return true; - } else { - return false; - } + async checkCurrentNetworkIsRegister() { + const currentNetworkWallet = await getCurrentNetworkWallet(); + const originChainId = currentNetworkWallet?.originChainId; + return Boolean(originChainId && currentNetworkWallet?.[originChainId]?.caHash); } - async checkIsRegisterOtherwiseRegister(method: string): Promise { - if (this.whitelist?.includes(method) || isNotificationEvents(method)) + async registerCurrentNetworkWallet(): Promise { + if (await this.checkCurrentNetworkIsRegister()) return { error: 0, - message: 'no check', + message: 'The current network has completed login', }; - const registerStatus = await this.getRegisterStatus(); - if (registerStatus !== 'Registered') { - // if (!search) search = { from: 'sw' }; - return await this.notificationService.openPrompt( - { - method: PromptRouteTypes[registerStatus === 'registeredNotGetCaAddress' ? 'BLANK_PAGE' : 'REGISTER_WALLET'], - // search: JSON.stringify(search), - }, - 'tabs', - ); - } else { + // Not yet registered or logged in + let routerType: keyof typeof PromptRouteTypes = 'REGISTER_WALLET'; + const currentNetworkWallet = await getCurrentNetworkWallet(); + if (currentNetworkWallet?.managerInfo) routerType = 'BLANK_PAGE'; + return await this.notificationService.openPrompt( + { + method: PromptRouteTypes[routerType], + }, + 'tabs', + ); + } + async checkCurrentNetworkOtherwiseRegister(methodName: string): Promise { + if (this.checkAllowMethod(methodName)) return { error: 0, - message: 'Registered', + message: 'no check', }; - } + return await this.registerCurrentNetworkWallet(); } } diff --git a/packages/web-extension-did/app/web/hooks/useCheckRegisterOnNetwork.ts b/packages/web-extension-did/app/web/hooks/useCheckRegisterOnNetwork.ts deleted file mode 100644 index f6a0a2def5..0000000000 --- a/packages/web-extension-did/app/web/hooks/useCheckRegisterOnNetwork.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useEffect, useMemo } from 'react'; -import { useLocation, useNavigate } from 'react-router'; -import { RegisterStatus } from 'types'; -import { useStorage } from './useStorage'; - -export default function useCheckRegisterOnNetwork() { - const navigate = useNavigate(); - const registerStatus = useStorage('registerStatus'); - const location = useLocation(); - const wallet = useStorage('reduxStorageWallet'); - const { currentNetwork, walletInfo } = useMemo(() => { - const _wallet = JSON.parse(wallet || null); - return { - currentNetwork: _wallet?.currentNetwork ? JSON.parse(_wallet?.currentNetwork) : undefined, - walletInfo: _wallet?.walletInfo ? JSON.parse(_wallet?.walletInfo || {}) : undefined, - }; - }, [wallet]); - - useEffect(() => { - const isRegisterSuccessBeforePage = - location.pathname.startsWith('/register') || location.pathname.startsWith('/login'); - - if (registerStatus === 'Registered' && isRegisterSuccessBeforePage && walletInfo?.caInfo?.[currentNetwork]) { - navigate('/'); - } - }, [currentNetwork, location.pathname, navigate, registerStatus, walletInfo?.caInfo]); -} diff --git a/packages/web-extension-did/app/web/hooks/useFetchDidWallet.ts b/packages/web-extension-did/app/web/hooks/useFetchDidWallet.ts index 411b657862..d7ce60b80a 100644 --- a/packages/web-extension-did/app/web/hooks/useFetchDidWallet.ts +++ b/packages/web-extension-did/app/web/hooks/useFetchDidWallet.ts @@ -8,7 +8,6 @@ import { useCallback } from 'react'; import { useNavigate } from 'react-router'; import { useAppDispatch } from 'store/Provider/hooks'; import { getHolderInfo } from 'utils/sandboxUtil/getHolderInfo'; -import { setLocalStorage } from 'utils/storage/chromeStorage'; import { contractErrorHandler } from 'utils/tryErrorHandler'; export default function useFetchDidWallet(isExistWallet = false) { @@ -53,9 +52,6 @@ export default function useFetchDidWallet(isExistWallet = false) { if (walletResult.status !== 'pass') { const errorString = walletResult?.message || walletResult.status; if (!isExistWallet) { - await setLocalStorage({ - registerStatus: null, - }); dispatch(resetWallet()); } throw (errorString as string) || 'Something error'; @@ -83,11 +79,7 @@ export default function useFetchDidWallet(isExistWallet = false) { chainId: originChainId, }), ); - if (!isExistWallet) { - await setLocalStorage({ - registerStatus: 'Registered', - }); - } + const path = VerificationType.register === verificationType ? 'register' : 'login'; navigate(`/success-page/${path}`); } catch (error: any) { diff --git a/packages/web-extension-did/app/web/hooks/useUpdateContact.ts b/packages/web-extension-did/app/web/hooks/useUpdateContact.ts deleted file mode 100644 index 79d1741452..0000000000 --- a/packages/web-extension-did/app/web/hooks/useUpdateContact.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { fetchContactListAsync } from '@portkey-wallet/store/store-ca/contact/actions'; -import { useEffect } from 'react'; -import { useAppDispatch } from 'store/Provider/hooks'; -import { RegisterStatus } from 'types'; -import { getLocalStorage } from 'utils/storage/chromeStorage'; - -export default function useUpdateContact() { - const appDispatch = useAppDispatch(); - useEffect(() => { - getLocalStorage('registerStatus').then((state) => { - state === 'Registered' && appDispatch(fetchContactListAsync()); - }); - }, [appDispatch]); -} diff --git a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx index 69b03b45c8..d90b8a75d4 100644 --- a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx +++ b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx @@ -7,7 +7,6 @@ import { useNavigate, useParams } from 'react-router'; import { useAppDispatch, useGuardiansInfo, useLoading, useLoginInfo } from 'store/Provider/hooks'; import { setPinAction } from 'utils/lib/serviceWorkerAction'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { setLocalStorage } from 'utils/storage/chromeStorage'; import { createWallet, setManagerInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useTranslation } from 'react-i18next'; import { recoveryDIDWallet, registerDIDWallet } from '@portkey-wallet/api/api-did/utils/wallet'; @@ -131,12 +130,11 @@ export default function SetWalletPin() { caInfo: scanCaWalletInfo, }), ); - await setLocalStorage({ - registerStatus: 'Registered', - }); + + setPinAction(pin); + dispatch(setPasswordSeed(pin)); scanWallet?.address && sendScanLoginSuccess({ targetClientId: scanWallet.address }); - await setPinAction(pin); navigate(`/success-page/${state}`); }, [dispatch, navigate, scanCaWalletInfo, scanWalletInfo, state], @@ -174,8 +172,9 @@ export default function SetWalletPin() { type: loginAccount.loginType, verificationType: state === 'login' ? VerificationType.communityRecovery : VerificationType.register, }; - console.log(managerInfo, 'managerInfo====1'); + dispatch(setPasswordSeed(pin)); + !walletInfo.address ? dispatch( createWallet({ @@ -191,11 +190,8 @@ export default function SetWalletPin() { managerInfo, }), ); - console.log(managerInfo, 'managerInfo===='); - await setLocalStorage({ - registerStatus: 'registeredNotGetCaAddress', - }); - await setPinAction(pin); + + setPinAction(pin); // TODO Step 14 Only get Main Chain caAddress diff --git a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx index 3d6f4762f7..6620d71b88 100644 --- a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx @@ -7,11 +7,15 @@ import { useLocation, useNavigate } from 'react-router'; import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; import { setIsPrompt } from 'store/reducers/common/slice'; import { useStorage } from 'hooks/useStorage'; -import { getLocalStorage } from 'utils/storage/chromeStorage'; -import { useEffectOnce } from 'react-use'; import { sleep } from '@portkey-wallet/utils'; import { useIsNotLessThan768 } from 'hooks/useScreen'; +const timeout = async () => { + // TODO This is a bug + await sleep(2000); + return 'Chrome serviceworker is not working'; +}; + export default function PermissionCheck({ children, pageType = 'Popup', @@ -22,6 +26,7 @@ export default function PermissionCheck({ const dispatch = useDispatch(); const navigate = useNavigate(); const { walletInfo, currentNetwork } = useWalletInfo(); + // const networkList = useNetworkList(); const location = useLocation(); const appDispatch = useAppDispatch(); @@ -33,7 +38,6 @@ export default function PermissionCheck({ useIsNotLessThan768(); // Check register on current network, if registered and current page is register page, redirect to home page - // useCheckRegisterOnNetwork(); const noCheckRegister = useMemo( () => @@ -52,69 +56,73 @@ export default function PermissionCheck({ [location.pathname, pageType], ); - const locked = useStorage('locked'); - - const _sleep = useCallback(async () => { - // TODO This is a bug - await sleep(2000); - return 'Extension error'; - }, []); + const locked = useStorage('locked'); const getPassword = useCallback(async () => { try { const res = await Promise.race([ InternalMessage.payload(PortkeyMessageTypes.CHECK_WALLET_STATUS).send(), - _sleep(), + timeout(), ]); console.log(res, 'CHECK_WALLET_STATUS'); - if (typeof res === 'string') return chrome.runtime.reload(); // navigate('/unlock'); + if (typeof res === 'string') return chrome.runtime.reload(); const detail = (res as any)?.data; - if (detail?.registerStatus === 'Registered') { + if (detail?.registerStatus) { detail?.privateKey && dispatch(setPasswordSeed(detail.privateKey)); !detail?.privateKey && navigate('/unlock'); - } else if (detail?.registerStatus === 'registeredNotGetCaAddress') { - navigate('/query-page'); } else { - navigate('/register/start'); + InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); } } catch (error) { console.error(error, 'CHECK_WALLET_STATUS==error'); } - }, [_sleep, dispatch, navigate]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const checkCurrentNetworkRegisterHandler = useCallback(async () => { + const caInfo = walletInfo?.caInfo?.[currentNetwork]; + const caHash = caInfo?.[caInfo?.originChainId || 'AELF']?.caHash; - const goQueryPage = useCallback(async () => { - const registerStatus = await getLocalStorage('registerStatus'); - if (!location.pathname.includes('/query-page') && registerStatus === 'registeredNotGetCaAddress') - return navigate('/query-page'); - }, [location.pathname, navigate]); + console.log(caInfo, 'caInfo==='); + const res = await Promise.race([ + InternalMessage.payload(PortkeyMessageTypes.CHECK_WALLET_STATUS).send(), + timeout(), + ]); + if (typeof res === 'string') return chrome.runtime.reload(); - useEffectOnce(() => { - goQueryPage(); - }); + if (caHash) return getPassword(); - const checkRegisterHandler = useCallback(async () => { - const registerStatus = await getLocalStorage('registerStatus'); - if (registerStatus !== 'Registered' && pageType === 'Popup') { + if (pageType == 'Popup') { + await sleep(500); return InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); + } else { + // Already request not get register result + console.log(caInfo?.managerInfo, 'checkCurrentNetworkRegisterHandler=='); + // const otherNetwork = networkList.filter((item) => item.networkType !== currentNetwork); + // const otherNetworkType = otherNetwork.length ? otherNetwork[0].networkType : undefined; + // const ortherCaInfo = otherNetworkType ? walletInfo?.caInfo?.[otherNetworkType] : undefined; + // const otherNetworkCaHash = ortherCaInfo + // ? ortherCaInfo?.[ortherCaInfo.originChainId || 'AELF']?.caHash + // : undefined; + + if (caInfo?.managerInfo) return navigate('/query-page'); + const isRegisterPage = + location.pathname.includes('/login') || + location.pathname.includes('/register') || + location.pathname.includes('/success-page') || + location.pathname === '/query-page'; + if (isRegisterPage) return; + return navigate('/register'); } - if (noCheckRegister) return; - if (isRegisterPage) return InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); - if (!walletInfo?.caInfo?.[currentNetwork]) { - if (pageType === 'Popup') { - await sleep(500); - return InternalMessage.payload(PortkeyMessageTypes.LOGIN_WALLET).send(); - } else { - return navigate('/register'); - } - } - getPassword(); - }, [pageType, noCheckRegister, isRegisterPage, walletInfo, currentNetwork, getPassword, navigate]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentNetwork, getPassword, pageType, walletInfo?.caInfo]); useEffect(() => { if (location.pathname.includes('/test')) return; if (locked && !noCheckRegister && !isRegisterPage) return navigate('/unlock'); - checkRegisterHandler(); - }, [checkRegisterHandler, isRegisterPage, location.pathname, locked, navigate, noCheckRegister]); + checkCurrentNetworkRegisterHandler(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [checkCurrentNetworkRegisterHandler, locked]); return <>{children}; } diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index 6a2236aed4..e6e86bc8b7 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -1,11 +1,9 @@ -import { getAllStorageLocalData, getLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; -import storage from 'utils/storage/storage'; +import { getLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; import { AutoLockDataKey, AutoLockDataType, DefaultLock } from 'constants/lock'; import SWEventController, { DappEventPack } from 'controllers/SWEventController'; import PermissionController from 'controllers/PermissionController'; import NotificationService, { CloseParams } from 'service/NotificationService'; import ApprovalController from 'controllers/approval/ApprovalController'; -import { getStoreState } from 'store/utils/getStore'; import { InternalMessageData, IPageState } from 'types/SW'; import AELFMethodController from 'controllers/methodController/AELFMethodController'; import InternalMessageTypes, { @@ -20,22 +18,16 @@ import { apis } from 'utils/BrowserApis'; import SocialLoginController from 'controllers/socialLoginController'; import { LocalStream } from 'utils/extensionStreams'; import { MethodsWallet, MethodsBase } from '@portkey/provider-types'; -import { getWalletState } from 'utils/lib/SWGetReduxStore'; import OpenNewTabController from 'controllers/openNewTabController'; const notificationService = new NotificationService(); const socialLoginService = new SocialLoginController(); OpenNewTabController.onOpenNewTab(); -// Get default data in redux -const store = getStoreState(); - let seed: string | null = null; let pageState: IPageState = { lockTime: AutoLockDataType[DefaultLock], // minutes - registerStatus: undefined, - wallet: store.wallet, }; const permissionWhitelist = [ @@ -64,14 +56,10 @@ const permissionWhitelist = [ ]; const initPageState = async () => { - const allStorage = await getAllStorageLocalData(); - const wallet = await getWalletState(); + const lockTime = await getLocalStorage('lockTime'); pageState = { - registerStatus: allStorage[storage.registerStatus], - lockTime: AutoLockDataType[allStorage[storage.lockTime] as AutoLockDataKey] ?? AutoLockDataType[DefaultLock], - wallet: wallet as IPageState['wallet'], + lockTime: AutoLockDataType[lockTime as AutoLockDataKey] ?? AutoLockDataType[DefaultLock], }; - console.log(pageState, 'pageState==='); }; // This is the script that runs in the extension's serviceWorker ( singleton ) @@ -111,8 +99,9 @@ export default class ServiceWorkerInstantiate { if (!message.type) return; // reset lockout timer console.log(message, 'LocalStream.watch message'); + if (message.type === InternalMessageTypes.ACTIVE_LOCK_STATUS) return sendResponse(errorHandler(0)); - const registerRes = await this.permissionController.checkIsRegisterOtherwiseRegister(message.type); + const registerRes = await this.permissionController.checkCurrentNetworkOtherwiseRegister(message.type); if (registerRes.error !== 0) return sendResponse(registerRes); // process events if (SWEventController.check(message.type, message.payload?.data)) { @@ -124,7 +113,6 @@ export default class ServiceWorkerInstantiate { return; } await ServiceWorkerInstantiate.checkTimingLock(); - if (message.type === InternalMessageTypes.ACTIVE_LOCK_STATUS) return sendResponse(errorHandler(0)); const isLocked = await this.permissionController.checkIsLockOtherwiseUnlock(message.type); if (isLocked.error !== 0) return sendResponse(isLocked); this.dispenseMessage(sendResponse, message); @@ -159,7 +147,7 @@ export default class ServiceWorkerInstantiate { this.notificationServiceClose(sendResponse, message.payload); break; case PortkeyMessageTypes.REGISTER_WALLET: - ServiceWorkerInstantiate.checkRegisterStatus(); + this.checkRegisterStatus(sendResponse); break; case PortkeyMessageTypes.REGISTER_START_WALLET: ServiceWorkerInstantiate.registerStartWallet(); @@ -378,12 +366,11 @@ export default class ServiceWorkerInstantiate { } async checkWalletStatus(sendResponse: SendResponseFun) { - const registerStatus = await this.permissionController.getRegisterStatus(); - console.log(registerStatus, 'registerStatus==='); + const isRegisterOnCurrentNetwork = await this.permissionController.checkCurrentNetworkIsRegister(); return sendResponse({ ...errorHandler(0), data: { - registerStatus, + registerStatus: isRegisterOnCurrentNetwork, privateKey: seed, }, }); @@ -452,19 +439,8 @@ export default class ServiceWorkerInstantiate { }); } - static async checkRegisterStatus() { - const registerStatus = await getLocalStorage('registerStatus'); - if (registerStatus !== 'Registered') { - return await notificationService.open({ - sendResponse: (response) => { - console.log(response); - }, - message: { - method: PromptRouteTypes[registerStatus === 'registeredNotGetCaAddress' ? 'BLANK_PAGE' : 'REGISTER_WALLET'], - }, - promptType: 'tabs', - }); - } - return true; - } + checkRegisterStatus = async (sendResponse?: SendResponseFun) => { + const registerStatus = await this.permissionController.registerCurrentNetworkWallet(); + sendResponse?.(registerStatus); + }; } diff --git a/packages/web-extension-did/app/web/serviceWorker/index.ts b/packages/web-extension-did/app/web/serviceWorker/index.ts index 10ad9cd29a..12c1fe27a6 100644 --- a/packages/web-extension-did/app/web/serviceWorker/index.ts +++ b/packages/web-extension-did/app/web/serviceWorker/index.ts @@ -2,9 +2,8 @@ import ServiceWorkerInstantiate from './ServiceWorkerInstantiate'; import serviceWorkerListener from './serviceWorkerListener'; /** - * Initializes the MetaMask controller, and sets up all platform configuration. + * Initializes the Portkey controller, and sets up all platform configuration. * - * @param {string} remotePort - remote application port connecting to extension. */ // async function initialize(remotePort: any) { // console.log(remotePort, 'remotePort==='); @@ -24,10 +23,9 @@ import serviceWorkerListener from './serviceWorkerListener'; // }; // apis.runtime.onConnect.addListener(initApp); -new ServiceWorkerInstantiate(); - +const service = new ServiceWorkerInstantiate(); serviceWorkerListener({ pageStateChange: ServiceWorkerInstantiate.setPageState, - checkRegisterStatus: ServiceWorkerInstantiate.checkRegisterStatus, + checkRegisterStatus: service.checkRegisterStatus, checkTimingLock: ServiceWorkerInstantiate.checkTimingLock, }); diff --git a/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts b/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts index f46cab4d5e..bc8a2916de 100644 --- a/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts +++ b/packages/web-extension-did/app/web/serviceWorker/serviceWorkerListener.ts @@ -23,11 +23,7 @@ const serviceWorkerListener = ({ pageStateChange, checkRegisterStatus, checkTimi // eslint-disable-next-line @typescript-eslint/no-unused-vars apis.storage.onChanged.addListener((changes) => { console.log('storage.onChanged', changes); - if (storage.registerStatus in changes) { - pageStateChange({ - registerStatus: changes[storage.registerStatus].newValue, - }); - } else if (storage.lockTime in changes) { + if (storage.lockTime in changes) { checkTimingLock(); pageStateChange({ diff --git a/packages/web-extension-did/app/web/types/SW.ts b/packages/web-extension-did/app/web/types/SW.ts index 195d29ba7c..81c12ad4e3 100644 --- a/packages/web-extension-did/app/web/types/SW.ts +++ b/packages/web-extension-did/app/web/types/SW.ts @@ -1,11 +1,8 @@ -import { WalletState } from '@portkey-wallet/store/store-ca/wallet/type'; import { AutoLockDataType } from 'constants/lock'; -import { RegisterStatus, SendResponseFun } from 'types'; +import { SendResponseFun } from 'types'; export interface IPageState { lockTime: AutoLockDataType; - registerStatus: RegisterStatus; - wallet?: WalletState; } export interface BaseInternalMessagePayload { diff --git a/packages/web-extension-did/app/web/types/index.d.ts b/packages/web-extension-did/app/web/types/index.d.ts index d30669b5a5..ce83023a12 100644 --- a/packages/web-extension-did/app/web/types/index.d.ts +++ b/packages/web-extension-did/app/web/types/index.d.ts @@ -8,8 +8,6 @@ export interface CustomEventType extends Event { detail?: string; } -export type RegisterStatus = undefined | null | 'notRegistered' | 'registeredNotGetCaAddress' | 'Registered'; - export type ReCaptchaResponseParams = { response?: string; error?: number; diff --git a/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts b/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts index 564a83872b..c48f45d45d 100644 --- a/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts +++ b/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts @@ -22,3 +22,9 @@ export const getDappState = async () => { if (!dapp) dapp = getDefaultState().dapp; return dapp as IDappStoreState; }; + +export const getCurrentNetworkWallet = async () => { + const wallet = await getWalletState(); + const currentNetwork = wallet.currentNetwork; + return wallet.walletInfo?.caInfo?.[currentNetwork]; +}; diff --git a/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts b/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts index 6266a4b9dc..227f471539 100644 --- a/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts +++ b/packages/web-extension-did/app/web/utils/lib/serviceWorkerAction.ts @@ -8,14 +8,8 @@ import { useCallback } from 'react'; import { CloseParams } from 'service/NotificationService'; import { CreatePromptType, ReCaptchaResponseParams, SendResponseParams } from 'types'; import { getPortkeyFinanceUrl } from 'utils'; -import { setLocalStorage } from 'utils/storage/chromeStorage'; import { getWalletState } from './SWGetReduxStore'; -export const completeRegistration = async () => { - await setLocalStorage({ registerStatus: 'Registered' }); - await InternalMessage.payload(PortkeyMessageTypes.CLOSE_PROMPT, { isClose: false }).send(); -}; - export const closePrompt = async (closeParams?: CloseParams, promptType?: CreatePromptType) => { // await DappMiddle.middle await sleep(100); @@ -46,9 +40,7 @@ export const useActiveLockStatusAction = () => { }, []); }; -export const setPinAction = async (pin: string) => { - await InternalMessage.payload(PortkeyMessageTypes.SET_SEED, pin).send(); -}; +export const setPinAction = (pin: string) => InternalMessage.payload(PortkeyMessageTypes.SET_SEED, pin).send(); export const socialLoginAction = async (type: ISocialLogin, network: NetworkType): Promise => { const { JOIN_AUTH_URL } = getPortkeyFinanceUrl(network); diff --git a/packages/web-extension-did/app/web/utils/storage/storage.ts b/packages/web-extension-did/app/web/utils/storage/storage.ts index 4d077a5400..d1b196fadb 100644 --- a/packages/web-extension-did/app/web/utils/storage/storage.ts +++ b/packages/web-extension-did/app/web/utils/storage/storage.ts @@ -3,7 +3,6 @@ import { reduxStorageName, reduxStorageToken, reduxStorageWallet } from 'constan const storage = { // in background.js aelfCrossMeta: 'BG_AELF_CROSSMETA', - registerStatus: 'BG_REGISTER', lockTime: 'BG_LOCK_TIME', locked: 'BG_LOCKED', connections: 'BG_CONNECTIONS', From 5d25a016bebf228b3e2b8b05a0870450ceb03015 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 29 Jun 2023 11:49:18 +0800 Subject: [PATCH 226/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20BuyTab?= =?UTF-8?q?=20press?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Buy/BuyHome/index.tsx | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx index 22db044b48..e1b246af5b 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx @@ -1,5 +1,5 @@ import { defaultColors } from 'assets/theme'; -import React, { useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { pTd } from 'utils/unit'; import PageContainer from 'components/PageContainer'; @@ -40,33 +40,36 @@ export default function BuyHome() { isBuySectionShow ? PaymentTypeEnum.BUY : PaymentTypeEnum.SELL, ); - const onTabPress = (type: PaymentTypeEnum) => { - if (type === PaymentTypeEnum.BUY && !isBuySectionShow) { - ActionSheet.alert({ - title2: ( - - On-ramp is currently not supported. It will be launched in the coming weeks. - - ), - buttons: [{ title: 'OK' }], - }); - refreshBuyButton(); - return; - } - if (type === PaymentTypeEnum.SELL && !isSellSectionShow) { - ActionSheet.alert({ - title2: ( - - Off-ramp is currently not supported. It will be launched in the coming weeks. - - ), - buttons: [{ title: 'OK' }], - }); - refreshBuyButton(); - return; - } - setSelectTab(type); - }; + const onTabPress = useCallback( + (type: PaymentTypeEnum) => { + if (type === PaymentTypeEnum.BUY && !isBuySectionShow) { + ActionSheet.alert({ + title2: ( + + On-ramp is currently not supported. It will be launched in the coming weeks. + + ), + buttons: [{ title: 'OK' }], + }); + refreshBuyButton(); + return; + } + if (type === PaymentTypeEnum.SELL && !isSellSectionShow) { + ActionSheet.alert({ + title2: ( + + Off-ramp is currently not supported. It will be launched in the coming weeks. + + ), + buttons: [{ title: 'OK' }], + }); + refreshBuyButton(); + return; + } + setSelectTab(type); + }, + [isBuySectionShow, isSellSectionShow, refreshBuyButton], + ); return ( Date: Thu, 29 Jun 2023 15:15:34 +0800 Subject: [PATCH 227/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20scan=20wai?= =?UTF-8?q?ting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/message/index.ts | 1 + packages/hooks/hooks-ca/misc.ts | 66 +++++++++++++++++- .../components/CommonQRCodeStyled/index.tsx | 2 +- .../js/pages/Login/ScanLogin/index.tsx | 22 ++++-- .../js/pages/Login/components/QRCode.tsx | 67 ++++++++++++------- packages/socket/socket-did/index.ts | 11 +++ packages/socket/socket-did/types.ts | 4 ++ packages/types/types-ca/qrcode.ts | 1 + 8 files changed, 144 insertions(+), 30 deletions(-) diff --git a/packages/api/api-did/message/index.ts b/packages/api/api-did/message/index.ts index 82722526dd..ae7a189d22 100644 --- a/packages/api/api-did/message/index.ts +++ b/packages/api/api-did/message/index.ts @@ -1,3 +1,4 @@ export default { sendScanLoginSuccess: '/api/app/message/scanLoginSuccess', + sendScanLogin: '/api/app/message/scanLogin', }; diff --git a/packages/hooks/hooks-ca/misc.ts b/packages/hooks/hooks-ca/misc.ts index 584fad4400..d092104784 100644 --- a/packages/hooks/hooks-ca/misc.ts +++ b/packages/hooks/hooks-ca/misc.ts @@ -1,10 +1,13 @@ import { useAppCASelector } from '.'; import { getPhoneCountryCode, setLocalPhoneCountryCodeAction } from '@portkey-wallet/store/store-ca/misc/actions'; -import { useEffect, useMemo, useCallback } from 'react'; +import { useEffect, useMemo, useCallback, useState, useRef } from 'react'; import { useCurrentNetworkInfo, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; import { useAppCommonDispatch } from '../index'; import { DefaultCountry, getCountryCodeIndex } from '@portkey-wallet/constants/constants-ca/country'; import { CountryItem } from '@portkey-wallet/types/types-ca/country'; +import signalrDid from '@portkey-wallet/socket/socket-did'; +import { request } from '@portkey-wallet/api/api-did'; +import { onScanLoginDataType } from '@portkey-wallet/socket/socket-did/types'; export const useMisc = () => useAppCASelector(state => state.misc); @@ -66,3 +69,64 @@ export function usePhoneCountryCode(isInit = false) { return { phoneCountryCodeList, phoneCountryCodeIndex, localPhoneCountryCode, setLocalPhoneCountryCode }; } + +export const useIsScanQRCode = (clientId: string | undefined) => { + const [isScanQRCode, setIsScanQRCode] = useState(false); + const signalrDidRemoveRef = useRef<() => void>(); + + const isActiveRef = useRef(true); + useEffect(() => { + isActiveRef.current = true; + return () => { + isActiveRef.current = false; + }; + }, []); + + const cleanSignalr = useCallback(() => { + try { + signalrDidRemoveRef.current?.(); + signalrDidRemoveRef.current = undefined; + signalrDid.stop(); + signalrDid.signalr = null; + } catch (error) { + console.log(error); + } + }, []); + const cleanSignalrRef = useRef(cleanSignalr); + + const registerSignalr = useCallback(async (clientId: string) => { + try { + await signalrDid.doOpen({ + url: `${request.defaultConfig.baseURL}/ca`, + clientId, + }); + if (!isActiveRef.current) { + throw new Error('isActiveRef.current is false'); + } + + const signalrSellResult = await new Promise(resolve => { + const { remove } = signalrDid.onScanLogin(data => { + resolve(data); + }); + signalrDidRemoveRef.current = remove; + }); + + if (signalrSellResult !== null) { + setIsScanQRCode(true); + } + } catch (error) { + console.log('registerSignalr: error', error); + } finally { + cleanSignalrRef.current(); + } + }, []); + const registerSignalrRef = useRef(registerSignalr); + + useEffect(() => { + if (!clientId) return; + registerSignalrRef.current(clientId); + return cleanSignalrRef.current; + }, [clientId]); + + return isScanQRCode; +}; diff --git a/packages/mobile-app-did/js/components/CommonQRCodeStyled/index.tsx b/packages/mobile-app-did/js/components/CommonQRCodeStyled/index.tsx index b1cee35928..c5f948c4b8 100644 --- a/packages/mobile-app-did/js/components/CommonQRCodeStyled/index.tsx +++ b/packages/mobile-app-did/js/components/CommonQRCodeStyled/index.tsx @@ -34,7 +34,7 @@ export default function CommonQRCodeStyled(props: CommonQRCodeStyledPropsType) { (); - const { address: managerAddress, extraData: qrExtraData, deviceType } = data || {}; + const { address: managerAddress, extraData: qrExtraData, deviceType, time } = data || {}; const { caHash, address } = useCurrentWalletInfo(); const [loading, setLoading] = useState(); const getCurrentCAContract = useGetCurrentCAContract(); + useEffectOnce(() => { + const timeData = time || Math.floor(Date.now() / 1000); + try { + request.message.sendScanLogin({ + params: { + targetClientId: `${managerAddress}_${timeData}`, + }, + }); + } catch (error) { + console.log('sendScanLogin: error', error); + } + }); + const onLogin = useCallback(async () => { if (!caHash || loading || !managerAddress) return; try { setLoading(true); const deviceInfo = getDeviceInfoFromQR(qrExtraData, deviceType); - console.log('qrExtraData', qrExtraData, deviceType); - console.log('deviceInfo', deviceInfo); const contract = await getCurrentCAContract(); const extraData = await extraDataEncode(deviceInfo || {}, true); - console.log('extraData===', extraData); - const req = await addManager({ contract, caHash, address, managerAddress, extraData }); if (req?.error) throw req?.error; socket.doOpen({ diff --git a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx index 1732d1aa99..1793e0ca46 100644 --- a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx @@ -26,7 +26,22 @@ import { DEVICE_INFO_VERSION } from '@portkey-wallet/constants/constants-ca/devi import CommonQRCodeStyled from 'components/CommonQRCodeStyled'; import { useCheckManager } from 'hooks/useLogOut'; import Lottie from 'lottie-react-native'; -import { pTd } from 'utils/unit'; +import { useIsScanQRCode } from '@portkey-wallet/hooks/hooks-ca/misc'; + +// When wallet does not exist, DEFAULT_WALLET is populated as the default data +const DEFAULT_WALLET: LoginQRData = { + chainType: 'aelf', + type: 'login', + address: '2Aj8aTMsmgp1YyrVeCvB2dp9DbrLz5zgmAVmKNXsLnxhqzA69L', + netWorkType: 'TESTNET', + extraData: { + deviceInfo: { + deviceType: 2, + deviceName: 'iOS', + }, + version: '1.0.0', + }, +}; export default function QRCode({ setLoginType }: { setLoginType: (type: PageLoginType) => void }) { const { walletInfo, currentNetwork } = useCurrentWallet(); @@ -90,24 +105,28 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi listener.remove(); }; }); - const qrData = useMemo(() => { - if (!newWallet) - return '{"chainType":"aelf","type":"login","address":"2Aj8aTMsmgp1YyrVeCvB2dp9DbrLz5zgmAVmKNXsLnxhqzA69L","netWorkType":"TESTNET","extraData":{"deviceInfo":{"deviceType":2,"deviceName":"iOS"},"version":"1.0.0"}}'; - - const data: LoginQRData = { - // TODO: ethereum - chainType: 'aelf', - type: 'login', - address: newWallet.address, - netWorkType: currentNetwork, - extraData: { - deviceInfo: getDeviceInfo(), - version: DEVICE_INFO_VERSION, - }, - }; - return JSON.stringify(data); - }, [currentNetwork, getDeviceInfo, newWallet]); + const qrData: LoginQRData = useMemo( + () => + newWallet + ? { + // TODO: ethereum + chainType: 'aelf', + type: 'login', + address: newWallet.address, + netWorkType: currentNetwork, + time: Math.floor(Date.now() / 1000), + extraData: { + deviceInfo: getDeviceInfo(), + version: DEVICE_INFO_VERSION, + }, + } + : DEFAULT_WALLET, + [currentNetwork, getDeviceInfo, newWallet], + ); + const qrDataStr = useMemo(() => JSON.stringify(qrData), [qrData]); + const clientId = useMemo(() => (qrData.time ? `${qrData.address}_${qrData.time}` : undefined), [qrData]); + const isScanQRCode = useIsScanQRCode(clientId); return ( @@ -120,11 +139,13 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi Please use the Portkey DApp to scan the QR code - - - - Waiting for authorization... - + + {isScanQRCode && ( + + + Waiting for authorization... + + )} diff --git a/packages/socket/socket-did/index.ts b/packages/socket/socket-did/index.ts index 40b6ba0362..d1e4029448 100644 --- a/packages/socket/socket-did/index.ts +++ b/packages/socket/socket-did/index.ts @@ -1,6 +1,7 @@ import Signalr from '../index'; import { CaAccountRecoverResult, CaAccountRegisterResult } from '@portkey-wallet/types/types-ca/wallet'; import { listenList } from '@portkey-wallet/constants/constants-ca/socket'; +import { onScanLoginDataType } from './types'; export class SignalrDid extends Signalr { public Ack(clientId: string, requestId: string) { @@ -42,6 +43,16 @@ export class SignalrDid extends Signalr { } }); } + + public onScanLogin(callback: (data: onScanLoginDataType | null) => void) { + return this.listen('onScanLogin', (data: { body: string }) => { + if (typeof data?.body === 'string') { + callback(data); + } else { + callback(null); + } + }); + } } const signalrDid = new SignalrDid({ diff --git a/packages/socket/socket-did/types.ts b/packages/socket/socket-did/types.ts index 8322e589ce..6bd18bd723 100644 --- a/packages/socket/socket-did/types.ts +++ b/packages/socket/socket-did/types.ts @@ -1 +1,5 @@ export type RegisterStatus = 'pass' | 'pending' | 'fail' | null; + +export type onScanLoginDataType = { + body: string; +}; diff --git a/packages/types/types-ca/qrcode.ts b/packages/types/types-ca/qrcode.ts index b98cb1a63a..1c05af07b1 100644 --- a/packages/types/types-ca/qrcode.ts +++ b/packages/types/types-ca/qrcode.ts @@ -13,6 +13,7 @@ export interface LoginQRData extends QRData { type: 'login'; extraData?: QRExtraDataType; deviceType?: DeviceType; // 0.0.1 + time?: number; } export interface SendTokenQRDataType extends QRData { From f29c059c1d226ce248eaf88e32dcaa7a049158e9 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Thu, 29 Jun 2023 16:29:00 +0800 Subject: [PATCH 228/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20wallet=20soc?= =?UTF-8?q?ket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/socket.ts | 1 - packages/socket/index.ts | 93 ----------------------- packages/socket/package.json | 2 +- packages/socket/socket-did/index.ts | 52 +------------ packages/socket/socket-did/types.ts | 1 - packages/socket/socket-sell/index.ts | 6 +- packages/socket/types.ts | 8 -- yarn.lock | 15 ++++ 8 files changed, 22 insertions(+), 156 deletions(-) delete mode 100644 packages/socket/index.ts delete mode 100644 packages/socket/socket-did/types.ts delete mode 100644 packages/socket/types.ts diff --git a/packages/constants/constants-ca/socket.ts b/packages/constants/constants-ca/socket.ts index 754827e657..f4ead49bc7 100644 --- a/packages/constants/constants-ca/socket.ts +++ b/packages/constants/constants-ca/socket.ts @@ -1,7 +1,6 @@ import { MINUTE } from '../index'; export const SocketUrl = 'http://192.168.66.38:5577/ca'; -export const listenList = ['caAccountRegister', 'caAccountRecover', 'onScanLoginSuccess'] as const; export const queryExpirationTime = 5 * MINUTE; export const sellListenList = ['onAchTxAddressReceived'] as const; diff --git a/packages/socket/index.ts b/packages/socket/index.ts deleted file mode 100644 index be9d4b6029..0000000000 --- a/packages/socket/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr'; -import { randomId } from '@portkey-wallet/utils'; -import { Receive, SocketError } from './types'; - -export interface ISignalrOption { - listenList: ListenList; -} - -interface IMessageMap { - [eventName: string]: { - [key: string]: (data?: any) => void; - }; -} - -export default class Signalr { - url?: string; - signalr: HubConnection | null; - connectionId: string; - private messageMap: IMessageMap; - private listenList: ListenList; - constructor({ listenList }: ISignalrOption) { - this.connectionId = ''; - this.messageMap = {}; - this.signalr = null; - this.listenList = listenList; - } - - doOpen = async ({ url, clientId }: { url: string; clientId: string }) => { - const signalr = new HubConnectionBuilder().withUrl(url).withAutomaticReconnect().build(); - this._listener(signalr); - if (this.signalr) await this.signalr.stop(); - await signalr.start(); - await signalr.invoke('Connect', clientId); - this.connectionId = signalr.connectionId ?? ''; - this.signalr = signalr; - this.url = url; - return signalr; - }; - - public listen = (name: ListenList[keyof ListenList], handler: (data?: any) => void) => { - const key = randomId(); - let _name = name as string; - if (!this.messageMap[_name]) this.messageMap[_name] = {}; - this.messageMap[_name][key] = handler; - return { - remove: () => { - delete this.messageMap[_name][key]; - }, - }; - }; - - public on: HubConnection['on'] = (...args) => { - return this._checkSignalr().on(...args); - }; - - public invoke: HubConnection['invoke'] = (...args) => { - return this._checkSignalr().invoke(...args); - }; - - public onClose: HubConnection['onclose'] = (...args) => { - return this._checkSignalr().onclose(...args); - }; - - public stop: HubConnection['stop'] = (...args) => { - return this._checkSignalr().stop(...args); - }; - - public destroy: HubConnection['stop'] = () => { - this.messageMap = {}; - return this._checkSignalr().stop(); - }; - - private _listener(signalr: HubConnection) { - (this.listenList as string[]).forEach(listenType => { - signalr.on(listenType, (data: any) => { - this._onReceiver({ Event: listenType, Data: data }); - }); - }); - } - - private _onReceiver(data: Receive): void { - const callback = this.messageMap[data.Event]; - callback && - Object.values(callback).forEach(handler => { - handler(data.Data); - }); - } - - private _checkSignalr() { - if (!this.signalr) throw SocketError.notConnect; - return this.signalr; - } -} diff --git a/packages/socket/package.json b/packages/socket/package.json index e530a5248d..8084259c47 100644 --- a/packages/socket/package.json +++ b/packages/socket/package.json @@ -11,6 +11,6 @@ "start": "tsc --watch" }, "dependencies": { - "@abp/signalr": "^7.0.0" + "@portkey/socket": "1.0.0-alpha.2" } } diff --git a/packages/socket/socket-did/index.ts b/packages/socket/socket-did/index.ts index 40b6ba0362..b94f7a74cb 100644 --- a/packages/socket/socket-did/index.ts +++ b/packages/socket/socket-did/index.ts @@ -1,51 +1,5 @@ -import Signalr from '../index'; -import { CaAccountRecoverResult, CaAccountRegisterResult } from '@portkey-wallet/types/types-ca/wallet'; -import { listenList } from '@portkey-wallet/constants/constants-ca/socket'; +import { DIDSignalr } from '@portkey/socket'; -export class SignalrDid extends Signalr { - public Ack(clientId: string, requestId: string) { - this.invoke('Ack', clientId, requestId); - } +const didSignalr = new DIDSignalr(); - public onCaAccountRegister( - { clientId, requestId }: { clientId: string; requestId: string }, - callback: (data: CaAccountRegisterResult) => void, - ) { - return this.listen('caAccountRegister', (data: CaAccountRegisterResult) => { - if (data.requestId === requestId) { - if (data.body.registerStatus !== 'pending') { - this.Ack(clientId, requestId); - } - callback(data); - } - }); - } - - public onCaAccountRecover( - { clientId, requestId }: { clientId: string; requestId: string }, - callback: (data: CaAccountRecoverResult) => void, - ) { - return this.listen('caAccountRecover', (data: CaAccountRecoverResult) => { - if (data.requestId === requestId) { - if (data.body.recoveryStatus !== 'pending') { - this.Ack(clientId, requestId); - } - callback(data); - } - }); - } - public onScanLoginSuccess(callback: (data: { body: string }) => void) { - return this.listen('onScanLoginSuccess', (data: { body: string }) => { - if (typeof data?.body === 'string') { - callback(data); - this.stop(); - } - }); - } -} - -const signalrDid = new SignalrDid({ - listenList, -}) as Signalr & SignalrDid; - -export default signalrDid; +export default didSignalr; diff --git a/packages/socket/socket-did/types.ts b/packages/socket/socket-did/types.ts deleted file mode 100644 index 8322e589ce..0000000000 --- a/packages/socket/socket-did/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type RegisterStatus = 'pass' | 'pending' | 'fail' | null; diff --git a/packages/socket/socket-sell/index.ts b/packages/socket/socket-sell/index.ts index cb482eaad5..4a19da4e40 100644 --- a/packages/socket/socket-sell/index.ts +++ b/packages/socket/socket-sell/index.ts @@ -1,8 +1,8 @@ -import Signalr from '../index'; import { sellListenList } from '@portkey-wallet/constants/constants-ca/socket'; import { AchTxAddressReceivedType } from '@portkey-wallet/types/types-ca/payment'; +import { BaseSignalr } from '@portkey/socket'; -export class SignalrSell extends Signalr { +export class SignalrSell extends BaseSignalr { public requestAchTxAddress(clientId: string, orderId: string) { console.log('invoke RequestAchTxAddress', clientId, orderId); this.invoke('RequestAchTxAddress', { @@ -27,6 +27,6 @@ export class SignalrSell extends Signalr { const signalrSell = new SignalrSell({ listenList: sellListenList, -}) as Signalr & SignalrSell; +}) as BaseSignalr & SignalrSell; export default signalrSell; diff --git a/packages/socket/types.ts b/packages/socket/types.ts deleted file mode 100644 index d3aae7bce9..0000000000 --- a/packages/socket/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface Receive { - Event: string; - Data?: any; -} - -export enum SocketError { - notConnect = 'Signalr is null, please doOpen', -} diff --git a/yarn.lock b/yarn.lock index d13190f088..7493540b45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5233,6 +5233,21 @@ lodash "^4.17.21" readable-stream "^4.4.0" +"@portkey/socket@1.0.0-alpha.2": + version "1.0.0-alpha.2" + resolved "https://registry.npmjs.org/@portkey/socket/-/socket-1.0.0-alpha.2.tgz#fc243b61c03d2d5c0856190478af566841c00c0e" + integrity sha512-B2qr5wIYMS1A0d7UtnfhRJtOPJ+S51tf8Vyp3UdND5BT4d9b63XdeSjSnaBmzLuxXwT9zVP4kRJNkhLPaMpGQA== + dependencies: + "@abp/signalr" "^7.0.0" + "@portkey/utils" "^1.0.0-alpha.2" + +"@portkey/utils@^1.0.0-alpha.2": + version "1.0.0-alpha.2" + resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.2.tgz#719184830e520976c35db44ed131bcd95923df0b" + integrity sha512-Q+6yQuOPqXk/I4QzJD8AgEP/FhBd1NjnPkoEn1rjWAskONaVlBDKWL3lB2ihtGoSK+kn/c5YJ5aNLZJ4KLbkog== + dependencies: + aelf-sdk "^3.2.44" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From 8613110e4cdff360b0debc225bcf7bcd25f69dc2 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 29 Jun 2023 17:12:27 +0800 Subject: [PATCH 229/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20ThirdPart?= =?UTF-8?q?=20verifierCodeOperation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/hooks/authentication.ts | 2 ++ .../pages/Guardian/SelectVerifier/index.tsx | 9 ++++- .../pages/Guardian/VerifierDetails/index.tsx | 3 +- .../components/GuardianItem/index.tsx | 25 ++++++++++++++ .../pages/My/Guardian/GuardianEdit/index.tsx | 9 ++++- packages/types/types-ca/authentication.ts | 2 ++ packages/types/verifier.ts | 2 +- .../components/GuardianItems/index.tsx | 33 +++++++++++++++++-- .../pages/Guardians/GuardiansAdd/index.tsx | 3 +- .../pages/Guardians/GuardiansView/index.tsx | 3 +- .../app/web/pages/SelectVerifier/index.tsx | 3 +- .../app/web/pages/VerifierAccount/index.tsx | 3 +- 12 files changed, 87 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/hooks/authentication.ts b/packages/mobile-app-did/js/hooks/authentication.ts index f82e3d9624..e9b52df492 100644 --- a/packages/mobile-app-did/js/hooks/authentication.ts +++ b/packages/mobile-app-did/js/hooks/authentication.ts @@ -16,6 +16,7 @@ import { changeCanLock } from 'utils/LockManager'; import { AppState } from 'react-native'; import appleAuth, { appleAuthAndroid } from '@invertase/react-native-apple-authentication'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import { VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; if (!isIos) { GoogleSignin.configure({ @@ -245,6 +246,7 @@ export type VerifyTokenParams = { verifierId?: string; chainId: ChainId; id: string; + verifierCodeOperation: VerifierCodeOperationType; }; export function useVerifyGoogleToken() { diff --git a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx index 8f28e01bfb..122fbc3df4 100644 --- a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx @@ -23,7 +23,13 @@ import { VerifierImage } from '../components/VerifierImage'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import myEvents from 'utils/deviceEvent'; import { verification } from 'utils/api'; -import { AuthenticationInfo, RecaptchaType, VerificationType, VerifierItem } from '@portkey-wallet/types/verifier'; +import { + AuthenticationInfo, + RecaptchaType, + VerificationType, + VerifierCodeOperationType, + VerifierItem, +} from '@portkey-wallet/types/verifier'; import { useVerifyToken } from 'hooks/authentication'; import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useOnRequestOrSetPin } from 'hooks/login'; @@ -58,6 +64,7 @@ export default function SelectVerifier() { id: loginAccount, verifierId: selectedVerifier?.id, chainId: originChainId, + verifierCodeOperation: VerifierCodeOperationType.register, }); onRequestOrSetPin({ showLoading: !isRequestResult, diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index b117e98f7c..92c466896e 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -124,7 +124,7 @@ export default function VerifierDetails() { verifierCodeOperationType = VerifierCodeOperationType.addGuardian; break; case VerificationType.deleteGuardian: - verifierCodeOperationType = VerifierCodeOperationType.removeGuardian; + verifierCodeOperationType = VerifierCodeOperationType.deleteGuardian; break; case VerificationType.editGuardian: verifierCodeOperationType = VerifierCodeOperationType.editGuardian; @@ -132,6 +132,7 @@ export default function VerifierDetails() { case VerificationType.removeOtherManager: verifierCodeOperationType = VerifierCodeOperationType.removeOtherManager; break; + case VerificationType.setLoginAccount: default: verifierCodeOperationType = VerifierCodeOperationType.unknown; break; diff --git a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx index 8069f223bf..98de19950d 100644 --- a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx @@ -17,6 +17,7 @@ import { AuthenticationInfo, RecaptchaType, VerificationType, + VerifierCodeOperationType, VerifierInfo, VerifyStatus, } from '@portkey-wallet/types/verifier'; @@ -136,11 +137,34 @@ function GuardianItemButton({ const onVerifierAuth = useCallback(async () => { try { Loading.show(); + let verifierCodeOperation: VerifierCodeOperationType; + switch (approvalType) { + case ApprovalType.addGuardian: + verifierCodeOperation = VerifierCodeOperationType.addGuardian; + break; + case ApprovalType.editGuardian: + verifierCodeOperation = VerifierCodeOperationType.editGuardian; + break; + case ApprovalType.deleteGuardian: + verifierCodeOperation = VerifierCodeOperationType.deleteGuardian; + break; + case ApprovalType.removeOtherManager: + verifierCodeOperation = VerifierCodeOperationType.removeOtherManager; + break; + case ApprovalType.communityRecovery: + verifierCodeOperation = VerifierCodeOperationType.communityRecovery; + break; + default: + verifierCodeOperation = VerifierCodeOperationType.unknown; + break; + } + const rst = await verifyToken(guardianItem.guardianType, { accessToken: authenticationInfo?.[guardianItem.guardianAccount], id: guardianItem.guardianAccount, verifierId: guardianItem.verifier?.id, chainId: originChainId, + verifierCodeOperation, }); if (rst.accessToken) { @@ -160,6 +184,7 @@ function GuardianItemButton({ } Loading.hide(); }, [ + approvalType, authenticationInfo, guardianItem.guardianAccount, guardianItem.guardianType, diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx index 6262073cfb..39e058d2b3 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx @@ -15,7 +15,13 @@ import { checkEmail } from '@portkey-wallet/utils/check'; import { useGuardiansInfo } from 'hooks/store'; import { LOGIN_TYPE_LIST } from '@portkey-wallet/constants/verifier'; import { PRIVATE_GUARDIAN_ACCOUNT } from '@portkey-wallet/constants/constants-ca/guardian'; -import { ApprovalType, RecaptchaType, VerificationType, VerifierItem } from '@portkey-wallet/types/verifier'; +import { + ApprovalType, + RecaptchaType, + VerificationType, + VerifierCodeOperationType, + VerifierItem, +} from '@portkey-wallet/types/verifier'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import GuardianTypeSelectOverlay from '../components/GuardianTypeSelectOverlay'; import VerifierSelectOverlay from '../components/VerifierSelectOverlay'; @@ -165,6 +171,7 @@ const GuardianEdit: React.FC = () => { id: thirdPartyInfo.id, verifierId: verifierInfo.id, chainId: originChainId, + verifierCodeOperation: VerifierCodeOperationType.addGuardian, }); Loading.hide(); diff --git a/packages/types/types-ca/authentication.ts b/packages/types/types-ca/authentication.ts index df0ff42e05..ef27df7b8c 100644 --- a/packages/types/types-ca/authentication.ts +++ b/packages/types/types-ca/authentication.ts @@ -1,8 +1,10 @@ import { ChainId } from '@portkey-wallet/types'; +import { VerifierCodeOperationType } from '../verifier'; export type VerifyTokenParams = { accessToken?: string; verifierId?: string; chainId: ChainId; id: string; + verifierCodeOperation: VerifierCodeOperationType; }; diff --git a/packages/types/verifier.ts b/packages/types/verifier.ts index 444093bc18..c3f7d8c483 100644 --- a/packages/types/verifier.ts +++ b/packages/types/verifier.ts @@ -45,7 +45,7 @@ export enum VerifierCodeOperationType { register = 1, communityRecovery = 2, addGuardian = 3, - removeGuardian = 4, + deleteGuardian = 4, editGuardian = 5, removeOtherManager = 6, } diff --git a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx index ca979b9547..2caab26380 100644 --- a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx +++ b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx @@ -1,6 +1,12 @@ import { setCurrentGuardianAction, setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; import { UserGuardianItem, UserGuardianStatus } from '@portkey-wallet/store/store-ca/guardians/type'; -import { ApprovalType, RecaptchaType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { + ApprovalType, + RecaptchaType, + VerifierCodeOperationType, + VerifierInfo, + VerifyStatus, +} from '@portkey-wallet/types/verifier'; import { Button, message } from 'antd'; import clsx from 'clsx'; import VerifierPair from 'components/VerifierPair'; @@ -165,11 +171,34 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount async (item: UserGuardianItem) => { try { setLoading(true); + let verifierCodeOperation: VerifierCodeOperationType; + switch (query) { + case 'login': + verifierCodeOperation = VerifierCodeOperationType.communityRecovery; + break; + case 'guardians/add': + verifierCodeOperation = VerifierCodeOperationType.addGuardian; + break; + case 'guardians/edit': + verifierCodeOperation = VerifierCodeOperationType.editGuardian; + break; + case 'guardians/del': + verifierCodeOperation = VerifierCodeOperationType.deleteGuardian; + break; + default: + if (query?.indexOf('removeManage') !== -1) { + verifierCodeOperation = VerifierCodeOperationType.removeOtherManager; + } else { + verifierCodeOperation = VerifierCodeOperationType.unknown; + } + break; + } const result = await verifyToken(item.guardianType, { accessToken: loginAccount?.authenticationInfo?.[item.guardianAccount], id: item.guardianAccount, verifierId: item.verifier?.id, chainId: originChainId, + verifierCodeOperation, }); const verifierInfo: VerifierInfo = { ...result, verifierId: item?.verifier?.id }; const { guardianIdentifier } = handleVerificationDoc(verifierInfo.verificationDoc); @@ -189,7 +218,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount setLoading(false); } }, - [dispatch, loginAccount, originChainId, setLoading, verifyToken], + [dispatch, loginAccount, originChainId, setLoading, verifyToken, query], ); const verifyingHandler = useCallback( diff --git a/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx b/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx index 0b78f78afc..a56775fe75 100644 --- a/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx +++ b/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx @@ -29,7 +29,7 @@ import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { request } from '@portkey-wallet/api/api-did'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { handleVerificationDoc } from '@portkey-wallet/utils/guardian'; -import { RecaptchaType, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifierCodeOperationType, VerifyStatus } from '@portkey-wallet/types/verifier'; import verificationApiConfig from '@portkey-wallet/api/api-did/verification'; import GuardianAddPrompt from './Prompt'; import GuardianAddPopup from './Popup'; @@ -376,6 +376,7 @@ export default function AddGuardian() { verifierId: verifierVal, chainId: currentChain?.chainId || originChainId, accessToken: socialValue?.accessToken, + verifierCodeOperation: VerifierCodeOperationType.addGuardian, }; let res; if (guardianType === LoginType.Apple) { diff --git a/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx b/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx index 7c1847eb50..17d1de2975 100644 --- a/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx +++ b/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx @@ -34,7 +34,7 @@ import { useCommonState } from 'store/Provider/hooks'; import AccountShow from '../components/AccountShow'; import { guardianIconMap } from '../utils'; import './index.less'; -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; export default function GuardiansView() { const { t } = useTranslation(); @@ -76,6 +76,7 @@ export default function GuardiansView() { verifierId: opGuardian?.verifier?.id, chainId: currentChain?.chainId || originChainId, accessToken: data?.access_token, + verifierCodeOperation: VerifierCodeOperationType.unknown, }; if (v === 'Google') { await getGoogleUserInfo(data?.access_token); diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx index 86b7480044..cedb439f09 100644 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx +++ b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx @@ -19,7 +19,7 @@ import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQ import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import './index.less'; -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { RecaptchaType, VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; export default function SelectVerifier() { const { verifierMap } = useGuardiansInfo(); @@ -109,6 +109,7 @@ export default function SelectVerifier() { id: loginAccount.guardianAccount, verifierId: selectItem?.id, chainId: originChainId, + verifierCodeOperation: VerifierCodeOperationType.register, }); dispatch( setRegisterVerifierAction({ diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 33650bca45..2bfc645a56 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -209,7 +209,8 @@ export default function VerifierAccount() { case 'guardians/edit': return VerifierCodeOperationType.editGuardian; case 'guardians/del': - return VerifierCodeOperationType.removeGuardian; + return VerifierCodeOperationType.deleteGuardian; + case 'guardians/setLoginAccount': default: if (state?.indexOf('removeManage') !== -1) { return VerifierCodeOperationType.removeOtherManager; From 28b6f2893a0e639c1061c08207a4e950d7727e62 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 29 Jun 2023 17:46:29 +0800 Subject: [PATCH 230/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20login=20lo?= =?UTF-8?q?ading=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/wallet.ts | 4 ++++ .../web/hooks/useOnManagerAddressAndQueryResult.ts | 12 +++++++++++- .../web-extension-did/app/web/i18n/languages/en.json | 4 +++- .../app/web/pages/SetWalletPin/index.tsx | 9 ++++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/constants/constants-ca/wallet.ts b/packages/constants/constants-ca/wallet.ts index 78c3fd7883..43990bfc0f 100644 --- a/packages/constants/constants-ca/wallet.ts +++ b/packages/constants/constants-ca/wallet.ts @@ -1,2 +1,6 @@ export const CROSS_FEE = '0.35'; export const DEFAULT_FEE = '0.39'; + +export const CreateAddressLoading = 'Creating address on the chain...'; + +export const InitLoginLoading = 'Initiating social recovery'; diff --git a/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts b/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts index 8741b1eeb7..5d33fc3228 100644 --- a/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts +++ b/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts @@ -16,6 +16,8 @@ import useFetchDidWallet from './useFetchDidWallet'; import { isWalletError } from '@portkey-wallet/store/wallet/utils'; import { message } from 'antd'; import ModalTip from 'pages/components/ModalTip'; +import { CreateAddressLoading, InitLoginLoading } from '@portkey-wallet/constants/constants-ca/wallet'; +import { useTranslation } from 'react-i18next'; export function useOnManagerAddressAndQueryResult(state: string | undefined) { const { setLoading } = useLoading(); @@ -25,6 +27,7 @@ export function useOnManagerAddressAndQueryResult(state: string | undefined) { const getWalletCAAddressResult = useFetchDidWallet(true); const { loginAccount, registerVerifier } = useLoginInfo(); const network = useCurrentNetworkInfo(); + const { t } = useTranslation(); const originChainId = useOriginChainId(); @@ -109,7 +112,13 @@ export function useOnManagerAddressAndQueryResult(state: string | undefined) { try { if (!loginAccount?.guardianAccount || !LoginType[loginAccount.loginType]) return message.error('Missing account!!! Please login/register again'); - setLoading(true, 'Creating address on the chain...'); + + if (loginAccount.createType === 'register') { + setLoading(true, t(CreateAddressLoading)); + } else { + setLoading(true, t(InitLoginLoading)); + } + const _walletInfo = walletInfo.address ? walletInfo : AElf.wallet.createNewWallet(); console.log(walletInfo.address, 'onCreate=='); @@ -176,6 +185,7 @@ export function useOnManagerAddressAndQueryResult(state: string | undefined) { requestRegisterDIDWallet, setLoading, state, + t, walletInfo, ], ); diff --git a/packages/web-extension-did/app/web/i18n/languages/en.json b/packages/web-extension-did/app/web/i18n/languages/en.json index 916dc54b99..a1d59d9d33 100644 --- a/packages/web-extension-did/app/web/i18n/languages/en.json +++ b/packages/web-extension-did/app/web/i18n/languages/en.json @@ -417,5 +417,7 @@ "verificationCodeTip2": "to verify your {{type}} address.", "returnTip": "Are you sure you want to leave this page? All changes will not be saved.", "This guardian is the only login account and cannot be removed": "This guardian is the only login account and cannot be removed", - "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian": "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian" + "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian": "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian", + "Creating address on the chain...": "Creating address on the chain...", + "Initiating social recovery...": "Initiating social recovery..." } diff --git a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx index d90b8a75d4..7ac53db219 100644 --- a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx +++ b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx @@ -27,6 +27,7 @@ import { getDeviceInfo } from 'utils/device'; import { sendScanLoginSuccess } from '@portkey-wallet/api/api-did/message/utils'; import ModalTip from 'pages/components/ModalTip'; import './index.less'; +import { CreateAddressLoading, InitLoginLoading } from '@portkey-wallet/constants/constants-ca/wallet'; export default function SetWalletPin() { const [form] = Form.useForm(); @@ -149,7 +150,12 @@ export default function SetWalletPin() { if (state === 'scan') return createByScan(pin); if (!loginAccount?.guardianAccount || !LoginType[loginAccount.loginType]) return message.error('Missing account!!! Please login/register again'); - setLoading(true, 'Creating address on the chain...'); + + if (loginAccount.createType === 'register') { + setLoading(true, t(CreateAddressLoading)); + } else { + setLoading(true, t(InitLoginLoading)); + } const _walletInfo = walletInfo.address ? walletInfo : AElf.wallet.createNewWallet(); console.log(pin, walletInfo.address, 'onCreate=='); @@ -230,6 +236,7 @@ export default function SetWalletPin() { getWalletCAAddressResult, requestRegisterDIDWallet, requestRecoveryDIDWallet, + t, ], ); From a7399fd953336bce4c0bdbdb581e2160bd404ec1 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 29 Jun 2023 17:50:55 +0800 Subject: [PATCH 231/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20sort=20rule=20up?= =?UTF-8?q?date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/tokenManagement/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/store-ca/tokenManagement/api.ts b/packages/store/store-ca/tokenManagement/api.ts index 0bff138339..13cd7fc09f 100644 --- a/packages/store/store-ca/tokenManagement/api.ts +++ b/packages/store/store-ca/tokenManagement/api.ts @@ -17,7 +17,7 @@ export function fetchAllTokenList({ filter: `${filterKeywords} AND (${chainIdSearchLanguage})`, // filter: `${filterKeywords}`, - sort: 'sortWeight desc,token.symbol acs,token.chainId acs', + sort: 'sortWeight desc,isDisplay desc,token.symbol acs,token.chainId acs', skipCount: 0, maxResultCount: 1000, }, From 0d67db2c17c4fcd4e05d82a0a517265b2084e625 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 29 Jun 2023 19:03:48 +0800 Subject: [PATCH 232/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=201.3.2=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/token/index.ts | 8 + .../app/web/Popup/routes/index.tsx | 5 + .../app/web/Prompt/routes/index.tsx | 5 + .../app/web/assets/svgIcon/DappLock.svg | 5 + .../app/web/assets/svgIcon/DappWarn.svg | 5 + .../app/web/assets/svgIcon/ErrorIcon.svg | 23 +++ .../app/web/assets/svgIcon/Group.svg | 8 + .../web-extension-did/app/web/assets/svgs.ts | 8 + .../web/components/CircleLoading/data.json | 1 + .../web/components/CircleLoading/index.tsx | 31 ++++ .../web/components/ErrorBoundary/index.less | 51 ++++++ .../web/components/ErrorBoundary/index.tsx | 33 ++-- .../web-extension-did/app/web/manifest.json | 2 +- .../components/AmountInput/TokenInput.tsx | 21 ++- .../Send/components/AmountInput/index.tsx | 3 + .../app/web/pages/Send/index.less | 3 + .../app/web/pages/Send/index.tsx | 56 +++--- .../app/web/pages/SendTransactions/index.less | 8 + .../app/web/pages/SendTransactions/index.tsx | 83 +++++++-- .../app/web/pages/Token/Custom/index.less | 80 +++++++++ .../app/web/pages/Token/Custom/index.tsx | 168 ++++++++++++++++++ .../app/web/pages/Token/Manage/index.less | 102 +++++++++-- .../app/web/pages/Token/Manage/index.tsx | 154 ++++++++++++---- .../components/ConnectedSiteList/index.less | 7 + .../components/ConnectedSiteList/index.tsx | 11 +- .../app/web/store/Provider/index.tsx | 2 +- packages/web-extension-did/package.json | 2 +- 27 files changed, 769 insertions(+), 116 deletions(-) create mode 100644 packages/web-extension-did/app/web/assets/svgIcon/DappLock.svg create mode 100644 packages/web-extension-did/app/web/assets/svgIcon/DappWarn.svg create mode 100644 packages/web-extension-did/app/web/assets/svgIcon/ErrorIcon.svg create mode 100644 packages/web-extension-did/app/web/assets/svgIcon/Group.svg create mode 100644 packages/web-extension-did/app/web/components/CircleLoading/data.json create mode 100644 packages/web-extension-did/app/web/components/CircleLoading/index.tsx create mode 100644 packages/web-extension-did/app/web/components/ErrorBoundary/index.less create mode 100644 packages/web-extension-did/app/web/pages/Token/Custom/index.less create mode 100644 packages/web-extension-did/app/web/pages/Token/Custom/index.tsx diff --git a/packages/api/api-did/token/index.ts b/packages/api/api-did/token/index.ts index de7b11f55d..87fe8d2f85 100644 --- a/packages/api/api-did/token/index.ts +++ b/packages/api/api-did/token/index.ts @@ -7,4 +7,12 @@ export default { target: '/api/app/userTokens', config: { method: 'PUT' }, }, + fetchTokenListBySearch: { + target: 'api/app/tokens/list', + config: { method: 'GET' }, + }, + fetchTokenItemBySearch: { + target: 'api/app/tokens/token', + config: { method: 'GET' }, + }, } as const; diff --git a/packages/web-extension-did/app/web/Popup/routes/index.tsx b/packages/web-extension-did/app/web/Popup/routes/index.tsx index 4c46dc64ca..6727014842 100644 --- a/packages/web-extension-did/app/web/Popup/routes/index.tsx +++ b/packages/web-extension-did/app/web/Popup/routes/index.tsx @@ -4,6 +4,7 @@ import Wallet from 'pages/Wallet'; import Contacts from 'pages/Contacts'; import GuardianApproval from 'pages/GuardianApproval'; import AddToken from 'pages/Token/Manage'; +import CustomToken from 'pages/Token/Custom'; import Receive from 'pages/Receive'; import TokenDetail from 'pages/Token/Detail'; import AccountSetting from 'pages/AccountSetting'; @@ -90,6 +91,10 @@ export const PageRouter = () => path: '/add-token', element: , }, + { + path: '/custom-token', + element: , + }, { path: '/transaction', element: , diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index e155028373..8263b3b1b3 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -19,6 +19,7 @@ import AddGuardian from 'pages/Guardians/GuardiansAdd'; import GuardiansEdit from 'pages/Guardians/GuardiansEdit'; import GuardiansView from 'pages/Guardians/GuardiansView'; import AddToken from 'pages/Token/Manage'; +import CustomToken from 'pages/Token/Custom'; import Transaction from 'pages/Transaction'; import TokenDetail from 'pages/Token/Detail'; import Send from 'pages/Send'; @@ -97,6 +98,10 @@ export const PageRouter = () => { path: '/add-token', element: , }, + { + path: '/custom-token', + element: , + }, { path: '/transaction', element: , diff --git a/packages/web-extension-did/app/web/assets/svgIcon/DappLock.svg b/packages/web-extension-did/app/web/assets/svgIcon/DappLock.svg new file mode 100644 index 0000000000..22c332b844 --- /dev/null +++ b/packages/web-extension-did/app/web/assets/svgIcon/DappLock.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/web-extension-did/app/web/assets/svgIcon/DappWarn.svg b/packages/web-extension-did/app/web/assets/svgIcon/DappWarn.svg new file mode 100644 index 0000000000..43f39bf1b2 --- /dev/null +++ b/packages/web-extension-did/app/web/assets/svgIcon/DappWarn.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/web-extension-did/app/web/assets/svgIcon/ErrorIcon.svg b/packages/web-extension-did/app/web/assets/svgIcon/ErrorIcon.svg new file mode 100644 index 0000000000..7098e67aee --- /dev/null +++ b/packages/web-extension-did/app/web/assets/svgIcon/ErrorIcon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web-extension-did/app/web/assets/svgIcon/Group.svg b/packages/web-extension-did/app/web/assets/svgIcon/Group.svg new file mode 100644 index 0000000000..40deb7db09 --- /dev/null +++ b/packages/web-extension-did/app/web/assets/svgIcon/Group.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/web-extension-did/app/web/assets/svgs.ts b/packages/web-extension-did/app/web/assets/svgs.ts index bcacbf0e97..b14443006b 100644 --- a/packages/web-extension-did/app/web/assets/svgs.ts +++ b/packages/web-extension-did/app/web/assets/svgs.ts @@ -40,6 +40,10 @@ export default { '\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n', DappDefault: '\n\n\n\n\n', + DappLock: + '\n\n\n\n\n', + DappWarn: + '\n\n\n\n\n', Delete: '\n\n Delete\n \n \n \n \n \n \n \n \n', Discord: @@ -52,6 +56,8 @@ export default { Edit: '\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n', EmptyBox: '\n\n Empty\n \n \n \n \n \n \n \n \n', + ErrorIcon: + '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', Expand: '\n\n Expand\n \n \n \n \n \n \n \n \n', ExpandBlue: @@ -66,6 +72,8 @@ export default { '\n\n czxhfqteof\n \n \n \n \n \n \n \n \n \n \n', Google: '\n\n\n\n\n\n', + Group: + '\n\n\n\n\n\n\n\n', GuardianApple: '\n\n\n\n\n\n', GuardianGoogle: diff --git a/packages/web-extension-did/app/web/components/CircleLoading/data.json b/packages/web-extension-did/app/web/components/CircleLoading/data.json new file mode 100644 index 0000000000..562a1fa5c3 --- /dev/null +++ b/packages/web-extension-did/app/web/components/CircleLoading/data.json @@ -0,0 +1 @@ +{"v":"5.6.9","fr":25,"ip":0,"op":50,"w":500,"h":500,"nm":"turn","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"turn","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[-16,-36,0],"ix":1},"s":{"a":0,"k":[92.843,92.843,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[447.14,447.14],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"elliptical path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.356862745098,0.556862745098,0.956862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":76,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"outline 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-16,-36],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"conversion"}],"nm":"ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[50]},{"t":50,"s":[0]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[410]},{"t":50,"s":[720]}],"ix":3},"m":1,"ix":2,"nm":"pruning path 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":51,"st":0,"bm":0}],"markers":[]} diff --git a/packages/web-extension-did/app/web/components/CircleLoading/index.tsx b/packages/web-extension-did/app/web/components/CircleLoading/index.tsx new file mode 100644 index 0000000000..523895ecfc --- /dev/null +++ b/packages/web-extension-did/app/web/components/CircleLoading/index.tsx @@ -0,0 +1,31 @@ +import { useRef, useEffect } from 'react'; +import lottie, { AnimationItem } from 'lottie-web'; +import animationData from './data.json'; + +const CircleLoading = () => { + const containerRef = useRef(null); + const animation = useRef(null); + + useEffect(() => { + if (!animation.current) { + animation.current = lottie.loadAnimation({ + container: containerRef.current!, + renderer: 'svg', + loop: true, + autoplay: true, + animationData: animationData, + }); + } + return () => { + if (animation.current) { + animation.current.stop(); + animation.current.destroy(); + animation.current = null; + } + }; + }, []); + + return
    ; +}; + +export default CircleLoading; diff --git a/packages/web-extension-did/app/web/components/ErrorBoundary/index.less b/packages/web-extension-did/app/web/components/ErrorBoundary/index.less new file mode 100644 index 0000000000..25ae6be1b4 --- /dev/null +++ b/packages/web-extension-did/app/web/components/ErrorBoundary/index.less @@ -0,0 +1,51 @@ +@import '../../assets/theme/color.less'; + +.error-body { + width: 328px; + height: 480px; + margin: 120px auto 0; + flex-direction: column; + font-size: 16px; + line-height: 22px; + font-weight: 400; + color: @font-17; + + .erroricon-icon { + width: 160px; + height: 140px; + margin-bottom: 24px; + } + .tip { + margin-bottom: 72px; + text-align: center; + } + + .btn-wrap { + margin-bottom: 0; + width: 100%; + font-weight: 500; + color: @font-5; + button { + width: 100%; + height: 48px; + border-radius: 24px; + border:1px solid @border-3; + background-color: @bg-7; + } + } +} + +.error-body-popup { + height: 600px; + width: 100%; + padding-top: 120px; + margin: 0; + justify-content: space-between; + .tip { + margin: 0 24px; + } + .btn-wrap { + padding: 16px; + border-top: 1px solid @border-2; + } +} diff --git a/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx b/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx index 179424fc5f..dcdeb06cb6 100644 --- a/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx +++ b/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx @@ -1,32 +1,43 @@ -import { ReactNode, useCallback } from 'react'; +import { ReactNode, useCallback, useMemo } from 'react'; import ReactErrorBoundary, { ErrorBoundaryTrue, handleReportError } from '@portkey-wallet/utils/errorBoundary'; +import CustomSvg from 'components/CustomSvg'; +import { Button } from 'antd'; +import clsx from 'clsx'; +import * as Sentry from '@sentry/react'; +import './index.less'; export type ErrorBoundaryProps = { children: ReactNode; view: string; + pageType: string; }; -export default function ErrorBoundary({ children, view }: ErrorBoundaryProps) { +export default function ErrorBoundary({ children, view, pageType }: ErrorBoundaryProps) { + const isPrompt = useMemo(() => pageType === 'Prompt', [pageType]); const onError = useCallback( ({ error, componentStack }: Omit) => { const sendError = handleReportError({ error, componentStack, view }); console.log(sendError, '====sendError'); - // TODO: reportError + Sentry.captureException({ error, componentStack, view }); }, [view], ); return ( onError({ error, componentStack })} - fallback={({ error, componentStack, resetError }) => { + fallback={({ resetError }) => { return ( - <> -

    Something went wrong.

    - {error.toString()} -
    - {componentStack} - - +
    +
    + +
    + {"Oops! Looks like something went wrong. But don't worry, your wallet and funds are safe and sound."} +
    +
    +
    + +
    +
    ); }}> {children} diff --git a/packages/web-extension-did/app/web/manifest.json b/packages/web-extension-did/app/web/manifest.json index 19a0287e4f..46e22ec2f7 100644 --- a/packages/web-extension-did/app/web/manifest.json +++ b/packages/web-extension-did/app/web/manifest.json @@ -4,7 +4,7 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "name": "Portkey: DID & Crypto & NFT", - "version": "1.3.0", + "version": "1.3.2", "description": "Identity System for Social Recover and Asset Management Tool", "icons": { "16": "assets/images/extension_logo.png", diff --git a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx index f8ea9c28cf..721b7f3b20 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx @@ -14,6 +14,7 @@ import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import CustomSvg from 'components/CustomSvg'; import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; +import { useCheckManagerSyncState } from 'hooks/wallet'; export default function TokenInput({ fromAccount, @@ -22,6 +23,7 @@ export default function TokenInput({ errorMsg, onChange, getTranslationInfo, + setErrorMsg, }: { fromAccount: { address: string; AESEncryptPrivateKey: string }; toAccount: { address: string }; @@ -30,6 +32,7 @@ export default function TokenInput({ errorMsg: string; onChange: (params: { amount: string; balance: string }) => void; getTranslationInfo: (num: string) => any; + setErrorMsg: (v: string) => void; }) { const currentNetwork = useCurrentNetworkInfo(); const currentChain = useCurrentChain(token.chainId as ChainId); @@ -40,6 +43,7 @@ export default function TokenInput({ const [maxAmount, setMaxAmount] = useState(''); const [, getTokenPrice] = useGetCurrentAccountTokenPrice(); const amountInUsdShow = useAmountInUsdShow(); + const checkManagerSyncState = useCheckManagerSyncState(); const amountInUsd = useMemo( () => amountInUsdShow(value || amount, 0, token.symbol), @@ -77,6 +81,8 @@ export default function TokenInput({ setMaxAmount(divDecimals(balance, token.decimals).toString()); return; } + const _isManagerSynced = await checkManagerSyncState(token.chainId); + if (!_isManagerSynced) return; const fee = await getTranslationInfo(divDecimals(balance, token.decimals).toString()); if (fee) { setMaxAmount(divDecimals(balance, token.decimals).toString()); @@ -86,7 +92,7 @@ export default function TokenInput({ } else { setMaxAmount(divDecimals(balance, token.decimals).toString()); } - }, [balance, getTranslationInfo, token]); + }, [balance, checkManagerSyncState, getTranslationInfo, token.chainId, token.decimals, token.symbol]); useEffect(() => { getTokenBalance(); @@ -111,10 +117,15 @@ export default function TokenInput({ onChange({ amount, balance }); }, [amount, balance, onChange]); - const handleMax = useCallback(() => { - setAmount(maxAmount); - onChange({ amount: maxAmount, balance }); - }, [balance, maxAmount, onChange]); + const handleMax = useCallback(async () => { + const _isManagerSynced = await checkManagerSyncState(token.chainId); + if (_isManagerSynced) { + setAmount(maxAmount); + onChange({ amount: maxAmount, balance }); + } else { + setErrorMsg('Synchronizing on-chain account information...'); + } + }, [balance, checkManagerSyncState, maxAmount, onChange, setErrorMsg, token.chainId]); return (
    diff --git a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/index.tsx b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/index.tsx index 74c1e565e5..cf0ffe6cc4 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/index.tsx @@ -11,6 +11,7 @@ export default function AmountInput({ errorMsg, onChange, getTranslationInfo, + setErrorMsg, }: { fromAccount: { address: string; AESEncryptPrivateKey: string }; type: 'token' | 'nft'; @@ -20,6 +21,7 @@ export default function AmountInput({ errorMsg: string; onChange: (params: { amount: string; balance: string }) => void; getTranslationInfo: (v: string) => void; + setErrorMsg: (v: string) => void; }) { return type === 'token' ? ( ) : ( diff --git a/packages/web-extension-did/app/web/pages/Send/index.less b/packages/web-extension-did/app/web/pages/Send/index.less index 91151549d9..fe308114ab 100644 --- a/packages/web-extension-did/app/web/pages/Send/index.less +++ b/packages/web-extension-did/app/web/pages/Send/index.less @@ -120,6 +120,9 @@ font-size: 12px; margin: -6px 0 9px 78px; } + .error-msg.error-warning { + color: @font-8; + } } .address-wrap { .item { diff --git a/packages/web-extension-did/app/web/pages/Send/index.tsx b/packages/web-extension-did/app/web/pages/Send/index.tsx index 935918c7c9..03aabce558 100644 --- a/packages/web-extension-did/app/web/pages/Send/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/index.tsx @@ -33,6 +33,7 @@ import clsx from 'clsx'; import { AddressCheckError } from '@portkey-wallet/store/store-ca/assets/type'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { ChainId } from '@portkey-wallet/types'; +import { useCheckManagerSyncState } from 'hooks/wallet'; import './index.less'; export type Account = { address: string; name?: string }; @@ -68,7 +69,8 @@ export default function Send() { const [amount, setAmount] = useState(''); const [balance, setBalance] = useState(''); const isValidSuffix = useIsValidSuffix(); - + const checkManagerSyncState = useCheckManagerSyncState(); + const [isManagerSynced, setIsManagerSynced] = useState(true); const [txFee, setTxFee] = useState(); const currentChain = useCurrentChain(state.chainId); @@ -211,6 +213,11 @@ export default function Send() { } else { return 'input error'; } + const _isManagerSynced = await checkManagerSyncState(state.chainId); + setIsManagerSynced(_isManagerSynced); + if (!_isManagerSynced) { + return 'Synchronizing on-chain account information...'; + } const fee = await getTranslationInfo(); console.log('---getTranslationInfo', fee); if (fee) { @@ -229,6 +236,8 @@ export default function Send() { setLoading, amount, type, + checkManagerSyncState, + state.chainId, getTranslationInfo, tokenInfo.decimals, balance, @@ -341,7 +350,6 @@ export default function Send() { address: `ELF_${account.address}_${account?.addressChainId || account?.chainId}`, }; setToAccount(value); - // validateToAddress(value); }} chainId={tokenInfo.chainId} /> @@ -350,7 +358,6 @@ export default function Send() { 1: { btnText: 'Preview', handler: async () => { - // if (!validateToAddress(toAccount)) return; const res = await handleCheckPreview(); console.log('handleCheckPreview res', res); if (!res) { @@ -383,6 +390,7 @@ export default function Send() { setBalance(balance); }} getTranslationInfo={getTranslationInfo} + setErrorMsg={setErrorMsg} /> ), }, @@ -455,12 +463,7 @@ export default function Send() {
    {t('To_with_colon')}
    - setToAccount(v)} - focus={stage !== Stage.Amount} - // onBlur={() => validateToAddress(toAccount)} - /> + setToAccount(v)} focus={stage !== Stage.Amount} /> {stage === Stage.Amount && (
    - {errorMsg && {errorMsg}} + {errorMsg && {errorMsg}}
    )}
    {StageObj[stage].element}
    - {stage === Stage.Preview ? ( -
    - -
    - ) : ( -

    - -

    - )} +
    + +
    {isPrompt ? : null}
    ); - }, [StageObj, btnDisabled, errorMsg, isPrompt, navigate, stage, symbol, t, toAccount, type, walletName]); + }, [ + StageObj, + btnDisabled, + errorMsg, + isManagerSynced, + isPrompt, + navigate, + stage, + symbol, + t, + toAccount, + type, + walletName, + ]); return <>{isPrompt ? : mainContent()}; } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.less b/packages/web-extension-did/app/web/pages/SendTransactions/index.less index 97cc983444..d055f6e84a 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.less +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.less @@ -7,6 +7,11 @@ font-size: 12px; line-height: 16px; color: @font-13; + .circle-loading { + display: inline-block; + width: 12px; + margin-right: 8px; + } .value { font-size: 14px; line-height: 20px; @@ -123,6 +128,9 @@ margin-top: 16px; color: @font-14; } + .error-message.error-warning { + color: @font-8; + } .btn { width: 100%; margin-top: 40px; diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 1489a524d3..ff490487bf 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -19,6 +19,10 @@ import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wal import getTransferFee from './utils/getTransferFee'; import { ResponseCode } from '@portkey/provider-types'; import { getLocalStorage } from 'utils/storage/chromeStorage'; +import { useCheckManagerSyncState } from 'hooks/wallet'; +import CircleLoading from 'components/CircleLoading'; +import { request } from '@portkey-wallet/api/api-did'; +import clsx from 'clsx'; import './index.less'; export default function SendTransactions() { @@ -43,12 +47,16 @@ export default function SendTransactions() { const [_, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); const [fee, setFee] = useState(''); const [errMsg, setErrMsg] = useState(''); + const [loading, setLoading] = useState(true); + const [tokenDecimals, setTokenDecimals] = useState(0); const isCAContract = useMemo(() => chainInfo?.caContractAddress === payload?.contractAddress, [chainInfo, payload]); const privateKey = useMemo( () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), [passwordSeed, wallet.AESEncryptPrivateKey], ); const [txParams, setTxParams] = useState({}); + const checkManagerSyncState = useCheckManagerSyncState(); + const [isManagerSynced, setIsManagerSynced] = useState(true); const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { @@ -88,11 +96,28 @@ export default function SendTransactions() { setErrMsg('Failed to estimate transaction fee'); } else { setFee(fee); + setErrMsg(''); } }, [chainInfo, isCAContract, payload, privateKey, wallet], ); + const getTokenDecimals = useCallback(async (token: string, chainId: ChainId) => { + try { + const tokenDetail = await request.token.fetchTokenItemBySearch({ + params: { + token, + chainId, + }, + }); + setTokenDecimals(tokenDetail?.decimals ?? 0); + } catch (err) { + console.log('get token decimals error', err); + } finally { + setLoading(false); + } + }, []); + const getTxPayload = useCallback(async () => { const txPayload = await getLocalStorage<{ [x: string]: any }>('txPayload'); @@ -104,9 +129,16 @@ export default function SendTransactions() { return; } const params = JSON.parse(txPayload[transactionInfoId]); + getTokenDecimals(params.symbol, params.chainId); setTxParams(params); - getFee(params); - }, [getFee, transactionInfoId]); + const _isManagerSynced = await checkManagerSyncState(params?.chainId); + setIsManagerSynced(_isManagerSynced); + if (_isManagerSynced) { + getFee(params); + } else { + setErrMsg('the manager has not been synchronized yet'); // TODO + } + }, [checkManagerSyncState, getFee, getTokenDecimals, transactionInfoId]); useEffect(() => { getTxPayload(); @@ -138,7 +170,7 @@ export default function SendTransactions() { const renderTransfer = useMemo(() => { const { symbol, amount } = txParams.paramsOption || {}; - const decimals = symbol === 'ELF' ? 8 : 0; + const decimals = symbol === 'ELF' ? 8 : tokenDecimals; return (
    @@ -146,14 +178,20 @@ export default function SendTransactions() {
    Amount
    -
    {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`}
    +
    + {loading ? : `${formatAmountShow(divDecimals(amount, decimals), 8)}`} + {symbol} +
    {isMainnet &&
    {formatAmountInUsdShow(amount, decimals, symbol)}
    }
    Transaction Fee
    -
    {`${formatAmountShow(fee, 8)} ELF`}
    +
    + {loading ? : `${formatAmountShow(fee, 8)}`} + ELF +
    {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    @@ -161,10 +199,16 @@ export default function SendTransactions() {
    Total (Amount + Transaction Fee)
    {symbol === 'ELF' ? (
    -
    {`${formatAmountShow( - ZERO.plus(divDecimals(amount, decimals)).plus(fee), - 8, - )} ${symbol}`}
    +
    + + {loading ? ( + + ) : ( + `${formatAmountShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee), 8)}` + )} + + {symbol} +
    {isMainnet && (
    {formatAmountInUsdShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee).toNumber(), 0, symbol)} @@ -174,11 +218,17 @@ export default function SendTransactions() { ) : ( <>
    -
    {`${formatAmountShow(fee, 8)} ELF`}
    +
    + {loading ? : `${formatAmountShow(fee, 8)}`} + ELF +
    {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    -
    {`${formatAmountShow(amount)} ${symbol}`}
    +
    + {loading ? : `${formatAmountShow(amount)}`} + {symbol} +
    {isMainnet &&
    {formatAmountInUsdShow(amount, 0, symbol)}
    }
    @@ -186,7 +236,7 @@ export default function SendTransactions() {
    ); - }, [txParams.paramsOption, isMainnet, formatAmountInUsdShow, fee]); + }, [txParams.paramsOption, tokenDecimals, loading, isMainnet, formatAmountInUsdShow, fee]); const renderMessage = useMemo(() => { const params = txParams.paramsOption || {}; @@ -208,13 +258,16 @@ export default function SendTransactions() {
    Transaction Fee
    -
    {`${formatAmountShow(fee)} ELF`}
    +
    + {loading ? : `${formatAmountShow(fee)}`} + ELF +
    {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    ); - }, [txParams.paramsOption, fee, isMainnet, formatAmountInUsdShow]); + }, [txParams.paramsOption, loading, fee, isMainnet, formatAmountInUsdShow]); const sendHandler = useCallback(async () => { try { @@ -271,7 +324,7 @@ export default function SendTransactions() {
    {payload?.method}
    {payload?.method.toLowerCase() === 'transfer' ? renderTransfer : renderMessage} - {errMsg &&
    {errMsg}
    } + {errMsg &&
    {errMsg}
    }
    +
    + {isPrompt ? : null} +
    + ); + }, [ + chainOptions, + curChainId, + curToken, + errorMsg, + handleAdd, + handleBack, + handleChangeChainId, + isPrompt, + searchDebounce, + t, + value, + ]); + + return <>{isPrompt ? : mainContent()}; +} diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.less b/packages/web-extension-did/app/web/pages/Token/Manage/index.less index 372a1c019a..9254f8e2de 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.less +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.less @@ -5,6 +5,7 @@ height: 100%; display: flex; flex-direction: column; + background-color: #fff; .add-token-top { margin-bottom: 1px; @@ -24,6 +25,15 @@ text-align: left; } + .title-right-col { + .@{app-prefix}-btn.custom-token-add-btn { + width: 120px; + height: 44px; + border-radius: 22px; + margin-right: 10px; + } + } + .header-title { display: flex; justify-content: flex-start; @@ -43,6 +53,14 @@ flex: 1; background-color: #fff; overflow: auto; + font-size: 16px; + line-height: 22px; + color: @font-11; + + .token-title { + margin: 16px 16px 8px; + font-weight: 500; + } .token-item { display: flex; @@ -85,10 +103,7 @@ flex-direction: column; .token-item-symbol { width: 232px; - font-size: 16px; - color: @font-11; height: 22px; - line-height: 22px; } .token-item-net { line-height: 16px; @@ -97,30 +112,81 @@ } } } + + .token-item-action { + .add-token-btn-icon { + display: flex; + width: 48px; + justify-content: center; + height: 16px; + } + + .add-token-btn { + min-width: 48px; + padding: 2px 8px; + font-size: 12px; + border-color: @border-3; + color: @font-9; + border-radius: 11px; + + & > span { + display: block; + height: 16px; + line-height: 16px; + } + } + } } } - .add-token-btn { - min-width: 48px; - padding: 2px 8px; - font-size: 12px; - border-color: @border-3; + .add-button { + width: fit-content; + background-color: transparent; color: @font-9; - border-radius: 11px; + border: none; + box-shadow: none; + text-shadow: none; + justify-content: space-between; + .plus-icon { + width: 12px; + height: 12px; + margin-right: 8px; + /* stylelint-disable-next-line no-descending-specificity */ + & > svg { + display: block; + } + } + } - & > span { - display: block; - height: 16px; - line-height: 16px; + .no-result { + flex: 1; + background-color: #fff; + overflow: auto; + font-size: 14px; + line-height: 20px; + font-weight: 400; + .no-token-svg { + width: 64px; + height: 64px; + margin-top: 56px; + } + .desc { + margin: 24px 0 40px; + color: @font-13; } } - .add-token-btn-icon { - display: flex; - width: 48px; - justify-content: center; - height: 16px; + .search-result-tip { + margin: 32px auto; + font-size: 14px; + line-height: 20px; + font-weight: 400; + .desc { + margin-bottom: 16px; + color: @font-13; + } } + } .left-icon { diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx index 833590bddb..5ab3459036 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx @@ -3,59 +3,106 @@ import { useNavigate } from 'react-router'; import { Button, message } from 'antd'; import SettingHeader from 'pages/components/SettingHeader'; import CustomSvg from 'components/CustomSvg'; -import { useToken } from '@portkey-wallet/hooks/hooks-ca/useToken'; import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; import DropdownSearch from 'components/DropdownSearch'; import { useTranslation } from 'react-i18next'; import { useAppDispatch, useCommonState, useTokenInfo, useUserInfo } from 'store/Provider/hooks'; import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import { useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import './index.less'; import { transNetworkText } from '@portkey-wallet/utils/activity'; -import { useIsTestnet } from 'hooks/useNetwork'; import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import PromptFrame from 'pages/components/PromptFrame'; import clsx from 'clsx'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; +import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import { request } from '@portkey-wallet/api/api-did'; +import { useDebounceCallback } from '@portkey-wallet/hooks'; +import './index.less'; export default function AddToken() { const { t } = useTranslation(); const navigate = useNavigate(); - const [, tokenActions] = useToken(); const { tokenDataShowInMarket } = useTokenInfo(); - const { displayUserToken } = tokenActions; const [filterWord, setFilterWord] = useState(''); - const [openDrop, setOpenDrop] = useState(false); const { passwordSeed } = useUserInfo(); const appDispatch = useAppDispatch(); const chainIdArray = useChainIdList(); - const isTestNet = useIsTestnet(); + const isMainnet = useIsMainnet(); + const [tokenShowList, setTokenShowList] = useState(tokenDataShowInMarket); useEffect(() => { - passwordSeed && appDispatch(fetchAllTokenListAsync({ keyword: filterWord, chainIdArray })); - }, [passwordSeed, filterWord, appDispatch, chainIdArray]); + if (!filterWord) { + setTokenShowList(tokenDataShowInMarket); + } + }, [filterWord, tokenDataShowInMarket]); useEffect(() => { - tokenDataShowInMarket.length ? setOpenDrop(false) : setOpenDrop(true); - if (filterWord && !tokenDataShowInMarket.length) setOpenDrop(true); - }, [filterWord, tokenDataShowInMarket]); + passwordSeed && appDispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray })); + }, [passwordSeed, appDispatch, chainIdArray]); + + const handleAddCustomToken = useCallback(() => { + setFilterWord(''); + navigate('/custom-token'); + }, [navigate]); + + const handleSearch = useCallback( + async (keyword: string) => { + try { + if (!keyword) return; + const res = await request.token.fetchTokenListBySearch({ + params: { + symbol: keyword, + chainIds: chainIdArray, + }, + }); + setTokenShowList(res?.data || []); + } catch (error) { + console.log('filter search error', error); + } + }, + [chainIdArray], + ); - const rightElement = useMemo(() => navigate(-1)} />, [navigate]); + const searchDebounce = useDebounceCallback(handleSearch, [filterWord], 500); + + const rightElement = useMemo( + () => ( +
    + + navigate('/')} /> +
    + ), + [handleAddCustomToken, navigate, t], + ); const handleUserTokenDisplay = useCallback( async (item: TokenItemShowType) => { try { - await displayUserToken({ tokenItem: item, keyword: filterWord, chainIdArray }); + await request.token.displayUserToken({ + resourceUrl: `${item.userTokenId}/display`, + params: { + isDisplay: !item.isAdded, + }, + }); + setTimeout(() => { + if (!filterWord) { + appDispatch(fetchAllTokenListAsync({ chainIdArray })); + } else { + handleSearch(filterWord); + } + }, 1000); message.success('success'); } catch (error: any) { message.error(error?.message || 'handle display error'); console.log('=== userToken display', error); } }, - [chainIdArray, displayUserToken, filterWord], + [appDispatch, chainIdArray, filterWord, handleSearch], ); - const renderTokenItem = useCallback( + const renderTokenItemBtn = useCallback( (item: TokenItemShowType) => { const { isDefault = false, isAdded = true } = item; if (isDefault) { @@ -79,7 +126,7 @@ export default function AddToken() { [handleUserTokenDisplay, t], ); - const renderList = useCallback( + const renderTokenItem = useCallback( (item: TokenItemShowType) => (
    @@ -90,13 +137,58 @@ export default function AddToken() { )}

    {item.symbol} - {transNetworkText(item.chainId, isTestNet)} + {transNetworkText(item.chainId, !isMainnet)}

    -
    {renderTokenItem(item)}
    +
    {renderTokenItemBtn(item)}
    +
    + ), + [isMainnet, renderTokenItemBtn], + ); + + const renderNoSearchResult = useMemo( + () => ( +
    + +

    {t('There is no search Result.')}

    +
    + +
    +
    + ), + [handleAddCustomToken, t], + ); + + const renderSearchResultTip = useMemo( + () => ( +
    +

    {t("Can't find your token? Please try below.")}

    +
    + +
    ), - [isTestNet, renderTokenItem], + [handleAddCustomToken, t], + ); + + const renderTokenList = useMemo( + () => + tokenShowList.length ? ( +
    +
    +
    {t('Popular Assets')}
    + {tokenShowList.map((item) => renderTokenItem(item))} +
    + {filterWord && renderSearchResultTip} +
    + ) : ( + <>{filterWord ? renderNoSearchResult : ''} + ), + [filterWord, renderNoSearchResult, renderSearchResultTip, renderTokenItem, t, tokenShowList], ); const { isPrompt } = useCommonState(); @@ -104,37 +196,25 @@ export default function AddToken() { return (
    - navigate(-1)} rightElement={rightElement} /> + navigate('/')} rightElement={rightElement} /> {t('There is no search result')}
    } + overlay={<>} value={filterWord} inputProps={{ - // onBlur: () => setOpenDrop(false), - onFocus: () => { - if (filterWord && !tokenDataShowInMarket.length) setOpenDrop(true); - }, onChange: (e) => { const _value = e.target.value.replaceAll(' ', ''); - if (!_value) setOpenDrop(false); - setFilterWord(_value); + searchDebounce(_value); }, placeholder: 'Search token', }} />
    - {!!tokenDataShowInMarket.length && ( -
    {tokenDataShowInMarket.map((item) => renderList(item))}
    - )} + {renderTokenList} {isPrompt ? : null} - {/* {filterWord && !tokenDataShowInMarket.length && ( -
    {t('There is no search result.')}
    - )} */}
    ); - }, [filterWord, isPrompt, navigate, openDrop, renderList, rightElement, t, tokenDataShowInMarket]); + }, [filterWord, isPrompt, navigate, renderTokenList, rightElement, searchDebounce, t]); return <>{isPrompt ? : mainContent()}; } diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less index 217fb7dda9..d01d16dec0 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less @@ -34,9 +34,16 @@ .text-overflow(1); } .name { + display: flex; font-size: 16px; line-height: 22px; color: @font-11; + .dapp-name { + .text-overflow(1); + } + .custom-svg { + margin-left: 4px; + } } .origin { margin-top: 2px; diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx index 7b1b3d3aaf..2da73f30d3 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx @@ -1,8 +1,9 @@ import { useTranslation } from 'react-i18next'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { Button } from 'antd'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import ImageDisplay from 'pages/components/ImageDisplay'; +import CustomSvg from 'components/CustomSvg'; import './index.less'; export interface IConnectedSiteListProps { @@ -12,6 +13,7 @@ export interface IConnectedSiteListProps { export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSiteListProps) { const { t } = useTranslation(); + const isSafeOrigin = useCallback((origin: string) => origin.startsWith('https://'), []); const renderList = useMemo( () => ( @@ -21,7 +23,10 @@ export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSite
    -
    {item.name}
    +
    + {item.name} + +
    {item.origin}
    @@ -34,7 +39,7 @@ export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSite ))}
    ), - [list, onDisconnect, t], + [isSafeOrigin, list, onDisconnect, t], ); return list.length === 0 ?
    {t('No Connected Sites')}
    : renderList; diff --git a/packages/web-extension-did/app/web/store/Provider/index.tsx b/packages/web-extension-did/app/web/store/Provider/index.tsx index 991ea5e1f8..0197382359 100644 --- a/packages/web-extension-did/app/web/store/Provider/index.tsx +++ b/packages/web-extension-did/app/web/store/Provider/index.tsx @@ -53,7 +53,7 @@ export default function ContextProviders({ }, [language]); return ( - + diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 58245d4364..fcee9db71b 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -1,6 +1,6 @@ { "name": "web-extension-did", - "version": "1.3.0", + "version": "1.3.2", "description": "web-extension-did", "private": true, "dependencies": { From 207ae2d7980f2c1ddfea09f2901d39fe66e805ef Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 30 Jun 2023 13:47:34 +0800 Subject: [PATCH 233/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20useIsSc?= =?UTF-8?q?anQRCode=20socket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/misc.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/hooks/hooks-ca/misc.ts b/packages/hooks/hooks-ca/misc.ts index d092104784..69c7e3b8ba 100644 --- a/packages/hooks/hooks-ca/misc.ts +++ b/packages/hooks/hooks-ca/misc.ts @@ -7,7 +7,6 @@ import { DefaultCountry, getCountryCodeIndex } from '@portkey-wallet/constants/c import { CountryItem } from '@portkey-wallet/types/types-ca/country'; import signalrDid from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; -import { onScanLoginDataType } from '@portkey-wallet/socket/socket-did/types'; export const useMisc = () => useAppCASelector(state => state.misc); @@ -103,21 +102,13 @@ export const useIsScanQRCode = (clientId: string | undefined) => { if (!isActiveRef.current) { throw new Error('isActiveRef.current is false'); } - - const signalrSellResult = await new Promise(resolve => { - const { remove } = signalrDid.onScanLogin(data => { - resolve(data); - }); - signalrDidRemoveRef.current = remove; - }); - - if (signalrSellResult !== null) { + const { remove } = signalrDid.onScanLogin(() => { setIsScanQRCode(true); - } + cleanSignalrRef.current(); + }); + signalrDidRemoveRef.current = remove; } catch (error) { console.log('registerSignalr: error', error); - } finally { - cleanSignalrRef.current(); } }, []); const registerSignalrRef = useRef(registerSignalr); From e11ea929d78cdf2f7c8227d6dc82e4e3557f8e96 Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Fri, 30 Jun 2023 14:04:03 +0800 Subject: [PATCH 234/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20check=20CurrentNe?= =?UTF-8?q?twork=20Register?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/PermissionCheck/index.tsx | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx index 6620d71b88..30d1850c0e 100644 --- a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx @@ -9,6 +9,7 @@ import { setIsPrompt } from 'store/reducers/common/slice'; import { useStorage } from 'hooks/useStorage'; import { sleep } from '@portkey-wallet/utils'; import { useIsNotLessThan768 } from 'hooks/useScreen'; +import { useEffectOnce } from 'react-use'; const timeout = async () => { // TODO This is a bug @@ -93,18 +94,8 @@ export default function PermissionCheck({ if (caHash) return getPassword(); if (pageType == 'Popup') { - await sleep(500); return InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); } else { - // Already request not get register result - console.log(caInfo?.managerInfo, 'checkCurrentNetworkRegisterHandler=='); - // const otherNetwork = networkList.filter((item) => item.networkType !== currentNetwork); - // const otherNetworkType = otherNetwork.length ? otherNetwork[0].networkType : undefined; - // const ortherCaInfo = otherNetworkType ? walletInfo?.caInfo?.[otherNetworkType] : undefined; - // const otherNetworkCaHash = ortherCaInfo - // ? ortherCaInfo?.[ortherCaInfo.originChainId || 'AELF']?.caHash - // : undefined; - if (caInfo?.managerInfo) return navigate('/query-page'); const isRegisterPage = location.pathname.includes('/login') || @@ -118,11 +109,13 @@ export default function PermissionCheck({ }, [currentNetwork, getPassword, pageType, walletInfo?.caInfo]); useEffect(() => { - if (location.pathname.includes('/test')) return; if (locked && !noCheckRegister && !isRegisterPage) return navigate('/unlock'); + }, [isRegisterPage, locked, navigate, noCheckRegister]); + + useEffectOnce(() => { + if (location.pathname.includes('/test')) return; checkCurrentNetworkRegisterHandler(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [checkCurrentNetworkRegisterHandler, locked]); + }); return <>{children}; } From 76c049212b33c9dd73b112dd7b29fe281161f40e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 14:40:53 +0800 Subject: [PATCH 235/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20auth=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/AuthLoginOverlay/index.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx index d894fecfe5..f7128cf202 100644 --- a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx @@ -6,7 +6,6 @@ import { pTd } from 'utils/unit'; import { FontStyles } from 'assets/theme/styles'; import { TextM, TextXXL } from 'components/CommonText'; import GStyles from 'assets/theme/GStyles'; -import CommonButton from 'components/CommonButton'; import { LoginQRData } from '@portkey-wallet/types/types-ca/qrcode'; import { useCurrentWalletInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import CommonToast from 'components/CommonToast'; @@ -18,7 +17,6 @@ import { useLanguage } from 'i18n/hooks'; import { defaultColors } from 'assets/theme'; import ActionSheet from 'components/ActionSheet'; import minimizer from 'rn-minimizer'; -import { windowHeight } from '@portkey-wallet/utils/mobile/device'; import { AUTH_LOGIN_MAP } from 'constants/scheme'; import { getFaviconUrlFromDomain } from 'utils'; @@ -100,7 +98,11 @@ function AuthLogin({ loginData, domain, extraData: websiteInfo }: AuthLoginOverl ]); return ( - + - - - - ); } @@ -137,9 +135,6 @@ export default { }; const styles = StyleSheet.create({ - modal: { - height: windowHeight * 0.6, - }, itemRow: { height: 72, paddingLeft: pTd(20), From f489465e82b19d0663645774f77a4188cc0a78cd Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 14:40:53 +0800 Subject: [PATCH 236/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20auth=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/AuthLoginOverlay/index.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx index d894fecfe5..f7128cf202 100644 --- a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx @@ -6,7 +6,6 @@ import { pTd } from 'utils/unit'; import { FontStyles } from 'assets/theme/styles'; import { TextM, TextXXL } from 'components/CommonText'; import GStyles from 'assets/theme/GStyles'; -import CommonButton from 'components/CommonButton'; import { LoginQRData } from '@portkey-wallet/types/types-ca/qrcode'; import { useCurrentWalletInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import CommonToast from 'components/CommonToast'; @@ -18,7 +17,6 @@ import { useLanguage } from 'i18n/hooks'; import { defaultColors } from 'assets/theme'; import ActionSheet from 'components/ActionSheet'; import minimizer from 'rn-minimizer'; -import { windowHeight } from '@portkey-wallet/utils/mobile/device'; import { AUTH_LOGIN_MAP } from 'constants/scheme'; import { getFaviconUrlFromDomain } from 'utils'; @@ -100,7 +98,11 @@ function AuthLogin({ loginData, domain, extraData: websiteInfo }: AuthLoginOverl ]); return ( - + - - - - ); } @@ -137,9 +135,6 @@ export default { }; const styles = StyleSheet.create({ - modal: { - height: windowHeight * 0.6, - }, itemRow: { height: 72, paddingLeft: pTd(20), From c0937e1510007b654a840c37adcccdcb676f4375 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 14:49:53 +0800 Subject: [PATCH 237/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20useless?= =?UTF-8?q?=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/AuthLoginOverlay/index.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx index f7128cf202..ffdae59921 100644 --- a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx @@ -180,17 +180,6 @@ const styles = StyleSheet.create({ tips: { marginTop: pTd(16), }, - loginTypeIcon: { - width: pTd(64), - }, - bottomBox: { - marginTop: pTd(80), - marginHorizontal: pTd(16), - }, - cancelButtonStyle: { - marginTop: pTd(8), - backgroundColor: 'transparent', - }, baseImage: { width: pTd(64), height: pTd(64), @@ -204,7 +193,4 @@ const styles = StyleSheet.create({ height: pTd(64), marginRight: -pTd(12), }, - iconWrap: { - marginLeft: pTd(21), - }, }); From 11a4a696333ad58225501b6c7653aea3d8424aa0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 14:53:28 +0800 Subject: [PATCH 238/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20useless?= =?UTF-8?q?=20title?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/components/AuthLoginOverlay/index.tsx | 1 - packages/mobile-app-did/js/components/ModalBody/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx index ffdae59921..944a9020cb 100644 --- a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx @@ -100,7 +100,6 @@ function AuthLogin({ loginData, domain, extraData: websiteInfo }: AuthLoginOverl return ( diff --git a/packages/mobile-app-did/js/components/ModalBody/index.tsx b/packages/mobile-app-did/js/components/ModalBody/index.tsx index b87351d23a..4d01a2b539 100644 --- a/packages/mobile-app-did/js/components/ModalBody/index.tsx +++ b/packages/mobile-app-did/js/components/ModalBody/index.tsx @@ -28,7 +28,7 @@ export interface ModalBodyProps extends ViewProps { } export const ModalBody: React.FC = props => { - const { modalBodyType, title, children, style = {}, onClose, bottomButtonGroup } = props; + const { modalBodyType, title = '', children, style = {}, onClose, bottomButtonGroup } = props; const gStyles = useGStyles(); From 11a6e6b38baaa9d4b2e3b3b78cf7cca2d144a8e4 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 14:55:43 +0800 Subject: [PATCH 239/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20delete=20uselese?= =?UTF-8?q?=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/ModalBody/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/ModalBody/index.tsx b/packages/mobile-app-did/js/components/ModalBody/index.tsx index 4d01a2b539..a4cf1a6be1 100644 --- a/packages/mobile-app-did/js/components/ModalBody/index.tsx +++ b/packages/mobile-app-did/js/components/ModalBody/index.tsx @@ -110,7 +110,6 @@ export const styles = StyleSheet.create({ backgroundColor: defaultColors.bg1, position: 'absolute', bottom: 0, - paddingRight: pTd(20), ...GStyles.paddingArg(10, 20, 16, 20), }, buttonStyle: { From 856286ddff1970d0e3bdef6733c09cbb1b139a38 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 15:05:23 +0800 Subject: [PATCH 240/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20discover=20http?= =?UTF-8?q?=20dangours=20show?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/assets/theme/index.ts | 2 + .../BrowserTab/components/HttpModal/index.tsx | 103 ++++++++++++++++++ .../js/components/BrowserTab/index.tsx | 5 +- .../pages/Discover/DiscoverSearch/index.tsx | 51 ++------- .../DiscoverCmsListSection/index.tsx | 38 +------ .../SearchDiscoverSection/index.tsx | 38 +------ 6 files changed, 121 insertions(+), 116 deletions(-) create mode 100644 packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx diff --git a/packages/mobile-app-did/js/assets/theme/index.ts b/packages/mobile-app-did/js/assets/theme/index.ts index 2440a57cb9..a645480039 100644 --- a/packages/mobile-app-did/js/assets/theme/index.ts +++ b/packages/mobile-app-did/js/assets/theme/index.ts @@ -23,6 +23,7 @@ export const defaultColors = { bg14: '#CEDDFC', bg15: 'rgba(104,170,253,.2)', bg16: '#C5CBD5', + bg17: '#EA4F45', font1: '#464B53', font2: 'white', @@ -36,6 +37,7 @@ export const defaultColors = { font10: '#34C759', font11: '#ffffff', font12: '#B34B4B', + font13: '#EA4F45', icon1: '#515A62', icon2: '#ffffff', diff --git a/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx new file mode 100644 index 0000000000..b4d6701fba --- /dev/null +++ b/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx @@ -0,0 +1,103 @@ +import React, { useCallback, useState } from 'react'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { bottomBarHeight } from '@portkey-wallet/utils/mobile/device'; +import { TextM, TextL } from 'components/CommonText'; +import { pTd } from 'utils/unit'; +import GStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; +import { useLanguage } from 'i18n/hooks'; +import useEffectOnce from 'hooks/useEffectOnce'; +import { useDiscoverWhiteList } from 'hooks/discover'; +import { getProtocolAndHost, isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; + +type HttpModalPropsType = { + uri: string; +}; + +export default function HttpModal(props: HttpModalPropsType) { + const { uri } = props; + + const { t } = useLanguage(); + const [isShowHttpModal, setIsShowHttpModal] = useState(false); + const { checkIsInWhiteList, upDateWhiteList } = useDiscoverWhiteList(); + + const disableHttp = useCallback(() => { + upDateWhiteList(getProtocolAndHost(uri)); + setIsShowHttpModal(false); + }, [upDateWhiteList, uri]); + + useEffectOnce(() => { + const protocolAndHost = getProtocolAndHost(uri); + if (!checkIsInWhiteList(protocolAndHost) && isDangerousLink(protocolAndHost)) return setIsShowHttpModal(true); + }); + + return ( + + + You are accessing an insecure http connection. Please be mindful of protecting your personal information and + account security. + + + + {t('Disable notification')} + + setIsShowHttpModal(false)}> + {t('Get it')} + + + + ); +} + +export const styles = StyleSheet.create({ + wrap: { + left: 0, + right: 0, + backgroundColor: defaultColors.bg17, + bottom: -bottomBarHeight, + position: 'absolute', + zIndex: 999, + borderTopLeftRadius: pTd(8), + borderTopRightRadius: pTd(8), + ...GStyles.paddingArg(24, 20), + }, + hidden: { + display: 'none', + }, + tips: { + color: defaultColors.font2, + marginBottom: pTd(24), + }, + buttonGroupWrap: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + paddingTop: pTd(10), + paddingBottom: pTd(16), + }, + buttonBaseStyle: { + width: pTd(160), + height: pTd(44), + backgroundColor: 'white', + borderRadius: pTd(6), + textAlign: 'center', + textAlignVertical: 'center', + lineHeight: pTd(44), + overflow: 'hidden', + borderWidth: StyleSheet.hairlineWidth, + borderColor: 'yellow', + }, +}); + +export const buttonStyles = StyleSheet.create({ + type1Button: { + borderColor: defaultColors.bg1, + backgroundColor: defaultColors.bg17, + color: defaultColors.font2, + }, + type2Button: { + borderWidth: 0, + backgroundColor: defaultColors.bg1, + color: defaultColors.font13, + }, +}); diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 13104e6c4b..9370633671 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,10 +1,10 @@ -import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; +import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { StyleSheet, View } from 'react-native'; -import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import ProviderWebview, { IWebView } from 'components/ProviderWebview'; import { captureRef } from 'react-native-view-shot'; import { IBrowserTab, useBrowser } from 'components/TabsDrawer/context'; import Progressbar, { IProgressbar } from 'components/Progressbar'; +import HttpModal from './components/HttpModal'; type BrowserTabProps = { isHidden: boolean; @@ -44,6 +44,7 @@ const BrowserTab = forwardRef(function BrowserTab( source={{ uri }} onLoadProgress={({ nativeEvent }) => progressbarRef.current?.changeInnerBarWidth(nativeEvent.progress)} /> + ); }); diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 432c44adac..ad766c437d 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -14,24 +14,16 @@ import fonts from 'assets/theme/fonts'; import RecordSection from '../components/RecordSection'; import SearchDiscoverSection from '../components/SearchDiscoverSection'; import { isIOS } from '@rneui/base'; -import { - checkIsUrl, - getHost, - getProtocolAndHost, - isDangerousLink, - prefixUrlWithProtocol, -} from '@portkey-wallet/utils/dapp/browser'; +import { checkIsUrl, getHost, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; -import { useDiscoverJumpWithNetWork, useDiscoverWhiteList } from 'hooks/discover'; -import ActionSheet from 'components/ActionSheet'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; export default function DiscoverSearch() { const { t } = useLanguage(); const discoverGroupList = useDiscoverGroupList(); const jumpToWebview = useDiscoverJumpWithNetWork(); - const { checkIsInWhiteList, upDateWhiteList } = useDiscoverWhiteList(); const timerRef = useRef(null); const iptRef = useRef(); @@ -75,40 +67,14 @@ export default function DiscoverSearch() { if (!newValue) return; if (checkIsUrl(newValue)) { - const protocolAndHost = getProtocolAndHost(newValue); - console.log(protocolAndHost, checkIsInWhiteList(protocolAndHost), !isDangerousLink(protocolAndHost)); - if (checkIsInWhiteList(protocolAndHost) || !isDangerousLink(protocolAndHost)) { - onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); - } else { - ActionSheet.alert({ - title: 'title', - message: 'message', - buttons: [ - { - title: 'Get it', - type: 'solid', - onPress: () => { - onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); - }, - }, - { - title: 'Disable notification', - type: 'solid', - onPress: () => { - onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); - upDateWhiteList(protocolAndHost); - }, - }, - ], - }); - } + onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); } else { // else search in Discover list const filterList = flatList.filter(item => item.title.replace(/\s+/g, '').includes(newValue)); setFilteredDiscoverList(filterList); setShowRecord(false); } - }, [checkIsInWhiteList, flatList, onDiscoverJump, upDateWhiteList, value]); + }, [flatList, onDiscoverJump, value]); useFocusEffect( useCallback(() => { @@ -120,11 +86,12 @@ export default function DiscoverSearch() { }, []), ); - useEffect(() => { - return () => { + useEffect( + () => () => { if (timerRef.current) clearTimeout(timerRef.current); - }; - }, []); + }, + [], + ); return ( { discoverJump({ item: { @@ -31,37 +28,6 @@ export function DiscoverCmsListSection() { [discoverJump], ); - const onClickJump = useCallback( - (i: DiscoverItem) => { - if (checkIsInWhiteList(i.url) || !isDangerousLink(i.url)) { - onDiscoverJump(i); - } else { - ActionSheet.alert({ - title: 'title', - message: 'message', - buttons: [ - { - title: 'Get it', - type: 'solid', - onPress: () => { - onDiscoverJump(i); - }, - }, - { - title: 'Disable notification', - type: 'solid', - onPress: () => { - onDiscoverJump(i); - upDateWhiteList(i.url); - }, - }, - ], - }); - } - }, - [checkIsInWhiteList, onDiscoverJump, upDateWhiteList], - ); - return ( {GroupList.map((group, index) => ( diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx index f73d5d8bb9..e46144c8d0 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -11,9 +11,7 @@ import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { defaultColors } from 'assets/theme'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; -import { useDiscoverJumpWithNetWork, useDiscoverWhiteList } from 'hooks/discover'; -import { isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; -import ActionSheet from 'components/ActionSheet'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; interface ISearchDiscoverSectionProps { searchedDiscoverList: DiscoverItem[]; } @@ -23,9 +21,8 @@ export default function SearchDiscoverSection(props: ISearchDiscoverSectionProps const { searchedDiscoverList } = props; const { s3Url } = useCurrentNetworkInfo(); const jumpToWebview = useDiscoverJumpWithNetWork(); - const { checkIsInWhiteList, upDateWhiteList } = useDiscoverWhiteList(); - const onDiscoverJump = useCallback( + const onClickJump = useCallback( (i: DiscoverItem) => { jumpToWebview({ item: { @@ -38,37 +35,6 @@ export default function SearchDiscoverSection(props: ISearchDiscoverSectionProps [jumpToWebview], ); - const onClickJump = useCallback( - (i: DiscoverItem) => { - if (checkIsInWhiteList(i.url) || !isDangerousLink(i.url)) { - onDiscoverJump(i); - } else { - ActionSheet.alert({ - title: 'title', - message: 'message', - buttons: [ - { - title: 'Get it', - type: 'solid', - onPress: () => { - onDiscoverJump(i); - }, - }, - { - title: 'Disable notification', - type: 'solid', - onPress: () => { - onDiscoverJump(i); - upDateWhiteList(i.url); - }, - }, - ], - }); - } - }, - [checkIsInWhiteList, onDiscoverJump, upDateWhiteList], - ); - if (searchedDiscoverList.length === 0) return ; return ( From e0085e4417912e0e2a9147039475080848fcc554 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 30 Jun 2023 15:11:59 +0800 Subject: [PATCH 241/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20select=20chain?= =?UTF-8?q?=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ChainOverlay/index.tsx | 33 ++------ .../components/ChainOverlay/styles.ts | 27 ++----- .../js/components/SelectChain/index.tsx | 80 +++++++++++++++++++ .../pages/My/Contacts/ContactEdit/index.tsx | 2 +- .../store/store-ca/tokenManagement/action.ts | 4 - 5 files changed, 96 insertions(+), 50 deletions(-) rename packages/mobile-app-did/js/{pages/My/Contacts/ContactEdit => }/components/ChainOverlay/index.tsx (70%) rename packages/mobile-app-did/js/{pages/My/Contacts/ContactEdit => }/components/ChainOverlay/styles.ts (67%) create mode 100644 packages/mobile-app-did/js/components/SelectChain/index.tsx diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ChainOverlay/index.tsx b/packages/mobile-app-did/js/components/ChainOverlay/index.tsx similarity index 70% rename from packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ChainOverlay/index.tsx rename to packages/mobile-app-did/js/components/ChainOverlay/index.tsx index 2261906988..2ab24f66bc 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ChainOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/ChainOverlay/index.tsx @@ -1,16 +1,15 @@ -import React, { useMemo, useState } from 'react'; +import React from 'react'; import OverlayModal from 'components/OverlayModal'; import { Keyboard, ScrollView, View } from 'react-native'; import Touchable from 'components/Touchable'; import styles from './styles'; import Svg from 'components/Svg'; -import { TextL, TextXL } from 'components/CommonText'; +import { TextL } from 'components/CommonText'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; -import CommonInput from 'components/CommonInput'; -import { MAIN_CHAIN_ID } from '@portkey-wallet/constants/constants-ca/activity'; import { useGStyles } from 'assets/theme/useGStyles'; import { ModalBody } from 'components/ModalBody'; +import { useCurrentNetwork } from '@portkey-wallet/hooks/hooks-ca/network'; type ValueType = string | number; type DefaultValueType = string; @@ -35,29 +34,13 @@ const SelectList = , ItemValueType }: SelectListProps) => { const { t } = useLanguage(); const gStyle = useGStyles(); - const [keyWord, setKeyWord] = useState(''); - - const _list = useMemo(() => { - const _keyWord = keyWord?.trim(); - return _keyWord === '' ? list : list.filter(item => item[labelAttrName] === _keyWord); - }, [keyWord, labelAttrName, list]); + const networkType = useCurrentNetwork(); return ( - - - - {_list.length ? ( - - {_list.map(item => { + {list.length ? ( + + {list.map(item => { return ( , ItemValueType callBack(item); }}> - {item.chainId === MAIN_CHAIN_ID ? ( + {networkType === 'MAIN' ? ( ) : ( diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ChainOverlay/styles.ts b/packages/mobile-app-did/js/components/ChainOverlay/styles.ts similarity index 67% rename from packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ChainOverlay/styles.ts rename to packages/mobile-app-did/js/components/ChainOverlay/styles.ts index ee098ddcff..c4daf2051b 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ChainOverlay/styles.ts +++ b/packages/mobile-app-did/js/components/ChainOverlay/styles.ts @@ -3,41 +3,28 @@ import { StyleSheet } from 'react-native'; import { pTd } from 'utils/unit'; const styles = StyleSheet.create({ - titleWrap: { - paddingHorizontal: pTd(16), - marginBottom: pTd(8), - }, - titleLabel: { - textAlign: 'center', - marginVertical: pTd(16), - }, - titleInputWrap: { - height: pTd(44), - }, - titleInput: { - fontSize: pTd(14), - }, - titleIcon: { - marginLeft: pTd(16), + scrollWrap: { + marginTop: pTd(8), }, itemRow: { height: pTd(72), flexDirection: 'row', alignItems: 'center', - paddingLeft: pTd(20), + marginLeft: pTd(20), + marginRight: pTd(20), + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: defaultColors.border6, }, itemContent: { flex: 1, marginLeft: pTd(12), - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: defaultColors.border6, height: pTd(72), flexDirection: 'row', alignItems: 'center', }, itemIcon: { position: 'absolute', - right: 26, + right: 0, }, noResult: { lineHeight: pTd(22), diff --git a/packages/mobile-app-did/js/components/SelectChain/index.tsx b/packages/mobile-app-did/js/components/SelectChain/index.tsx new file mode 100644 index 0000000000..a18b25ef18 --- /dev/null +++ b/packages/mobile-app-did/js/components/SelectChain/index.tsx @@ -0,0 +1,80 @@ +import React, { useCallback, useMemo } from 'react'; +import { StyleSheet } from 'react-native'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import ListItem from 'components/ListItem'; +import GStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; +import { ChainId, NetworkType } from '@portkey-wallet/types'; +import { formatChainInfoToShow } from '@portkey-wallet/utils'; +import ChainOverlay from 'components/ChainOverlay'; +import { ChainItemType } from '@portkey-wallet/store/store-ca/wallet/type'; +import { useCurrentNetwork } from '@portkey-wallet/hooks/hooks-ca/network'; + +interface SelectChainProps { + currentNetwork: NetworkType; + chainId: ChainId; + chainList: ChainItemType[]; + onChainPress: (chainId: ChainId) => void; +} + +const SelectChain: React.FC = ({ currentNetwork, chainId, chainList, onChainPress }) => { + const networkType = useCurrentNetwork(); + + const _chainList = useMemo( + () => + chainList.map(ele => ({ + ...ele, + customChainName: formatChainInfoToShow(ele.chainId, currentNetwork), + })), + [chainList, currentNetwork], + ); + const onPressItem = useCallback(() => { + ChainOverlay.showList({ + list: _chainList, + value: chainId, + labelAttrName: 'customChainName', + callBack: item => { + onChainPress(item.chainId); + }, + }); + }, [_chainList, chainId, onChainPress]); + + return ( + : + } + titleStyle={[GStyles.flexRowWrap, GStyles.itemCenter]} + titleTextStyle={styles.chainSelectTitleStyle} + style={styles.selectedItem} + title={formatChainInfoToShow(chainId, currentNetwork)} + rightElement={} + /> + ); +}; + +export default SelectChain; + +const styles = StyleSheet.create({ + addressHeader: { + flexDirection: 'row', + height: pTd(20), + justifyContent: 'space-between', + alignItems: 'center', + ...GStyles.marginArg(0, 10, 8), + }, + addressTitle: { + lineHeight: pTd(20), + }, + chainSelectTitleStyle: { + marginLeft: pTd(8), + fontSize: pTd(14), + }, + selectedItem: { + marginBottom: pTd(12), + borderRadius: pTd(6), + height: pTd(56), + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx index 63e920d17c..888296ab5e 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx @@ -18,7 +18,7 @@ import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import { ADDRESS_NUM_LIMIT } from '@portkey-wallet/constants/constants-ca/contact'; import ContactAddress from './components/ContactAddress'; import { isValidCAWalletName } from '@portkey-wallet/utils/reg'; -import ChainOverlay from './components/ChainOverlay'; +import ChainOverlay from 'components/ChainOverlay'; import { getAelfAddress, isAelfAddress } from '@portkey-wallet/utils/aelf'; import { ChainItemType } from '@portkey-wallet/store/store-ca/wallet/type'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; diff --git a/packages/store/store-ca/tokenManagement/action.ts b/packages/store/store-ca/tokenManagement/action.ts index d9c900c2ea..8dd58724c1 100644 --- a/packages/store/store-ca/tokenManagement/action.ts +++ b/packages/store/store-ca/tokenManagement/action.ts @@ -11,13 +11,9 @@ export const deleteTokenInCurrentAccount = createAction('to export const fetchAllTokenListAsync = createAsyncThunk( 'tokenManagement/fetchAllTokenListAsync', async ({ keyword = '', chainIdArray }: { keyword?: string; chainIdArray?: string[] }) => { - // if (totalRecordCount === 0 || totalRecordCount > accountTokenList.length) { const response = await fetchAllTokenList({ keyword, chainIdArray: chainIdArray || [] }); return { list: response.items, totalRecordCount: response.totalRecordCount }; }, - - // return { list: [], totalRecordCount }; - // }, ); export const getSymbolImagesAsync = createAsyncThunk('tokenManagement/getSymbolImagesAsync', async () => { From 6ccbd98ecf958bfc67a7d96c278f3389718f8fd7 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 30 Jun 2023 16:00:57 +0800 Subject: [PATCH 242/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20errorPage?= =?UTF-8?q?=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/App.tsx | 36 ++++++------ .../js/assets/image/pngs/error.png | Bin 0 -> 4216 bytes .../js/components/Fallback/index.tsx | 54 ++++++++++++++---- .../js/pages/Referral/index.tsx | 7 ++- 4 files changed, 65 insertions(+), 32 deletions(-) create mode 100755 packages/mobile-app-did/js/assets/image/pngs/error.png diff --git a/packages/mobile-app-did/App.tsx b/packages/mobile-app-did/App.tsx index 8259a17937..a5dac4f708 100644 --- a/packages/mobile-app-did/App.tsx +++ b/packages/mobile-app-did/App.tsx @@ -54,27 +54,27 @@ const App = () => { }, []); return ( - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + ); }; diff --git a/packages/mobile-app-did/js/assets/image/pngs/error.png b/packages/mobile-app-did/js/assets/image/pngs/error.png new file mode 100755 index 0000000000000000000000000000000000000000..7850daf76272494f38cda95a3488f4e49f251502 GIT binary patch literal 4216 zcmV-;5Qp!HP)004{#1^@s6`=O#>00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPlXoxL!X)Ipmf2;D5r%~THoiZ-xP6lj4SM0YvlWa-dDTDYcLq&c)G zV$llPJ&UETx+||DUHaM3omOj2b1F#0#*rWM;Pz9p?{Zng)*=e}E@{G-1qALP@j$ z+3Mw3cuRcbXn2_mr;ki|DeLNnPRI{`}+!<1YmN!UQfeorc@*B8oW_8On~W}ZWt{p=32e9jtn zmR;lHeHKXd%#5M@oL)(?0MlM-@s2^edtCz2tq>JiWQVWX;=7I6az=dC;Nz*XiQ9{O zbK&(FW05qHWC02uaID=?7T>ht)^(D_SmX1Fvopp#X&}*Au%N~>WwXH6?mUaA zWCZsdIX>oCi0Aova}*E}V9H^klpA)nAV+%1^Yfm6x@-oD#YKQAhQ&8W;f@d?X?cxB zc$|DPa01M*4;sqoJ2DEDRIkse14n#CiU7FwFPBYULLZnUWD6I`c5b!LI=RHb)w)St z|L$kDlV?`Bs=RWOv(R<&%#*f7DpG{-%PezD=(%Jw5Ec-^g9vns?T=z#*~PExA1@Gn`yhoy5oI6M ziwD92h5_QUPFV4#Myso8ML(>o*Z6+$j4ei&`^QKd4G&gW$}m1b{MU{kabFPr^D)u2 zo4v1FzCrZI^S1cD_9)R44-&odO&(|au2!p9P>ls;D9#6Py`8etpmCpHAV5_hK!~l` z3jXT@qJO?i+SqDm;XOOGMSwo&`?eO|cmiQ09)|Bh|-z;DmkX9%&I_`vu1F~^?W z7&9uM)SD;*^hmonE3UZD<=u;F*TKa?c$2T%=fA#f$ACDx@wd0{SWkfKfAL(;m@%u; ztRODf1F(G8*EZiDSKJ5SdR3AS@T=1se}=H&UcYs)yA^a}MjQ~|mmI-f_>_8)2Lef-^B%z#za4cLLT9v0rnI5TbkG&!i2B0X<;3+Lm_=;!|~jx@u5NBD2dZYje)-{FYyrak0ykzvR(wvu3T6}p zC>GEot_YhgA*|ra4T=<4z98|RD$7m(7u+ur64n3pI#IRPHDUpM(9RNKbObF_Q6(fn zV?V#NX40KtXHcuo7~+D%qMZm~8T>S#`oXXDgA2uRVBej>@;evrShtUQmsXT84Ym2n z;(`Ov+OLO;9@!(=1MkE|)NN`6=f?}9-O65ASfC0JQ)Dx(4 zPDeNVCLgHxA)`eduepZzh$BE)#@3-aKfwK}PGA=c7y!85YfS=#ON$~CRvmh41=g-z z0U5$XLHh8kwhM*`1GZ)@s9q=n3_$b92s&Z5Mc_~1e#5H$!6l=}4579*tf~U*$D*27 zy~fIitaVkaTDSb)PZwqxzxX&jICznj6>s3zHz?1F26!{48yu0@|;(vj}fRPby{~~wK5!3JIR=~Ho8$bD8KXxt zXi#{xW050}gMP&aQf~oKr$d$+M!z1c;HTFqRM3I#^-JUk0uzE}pDLS}n}J0s4{6*{ z0Asu}Z+J{N+L3wzx}7Rx-j}%O$#5pzMpOuIIbfH~*%<>JDMj3VqHIn&>qz@u;2mp6 zpJb0eTba?b8vj7VfUtr{7$8F3qJedVi_|#+g6CVkX}|7u1^27UIeU0)u~0Urud)!? zg&yY2HQ&`T8tle-T$is|d?SR#$_FM1!75fCUn7BWtk=AAxC@tWm`2IMsPXr;UYQPe z-x0}z1y_(2iUApl6JkXKD8z~gP>2-~pb#q}Kp|EJA`3>{56uH{tzO`lwGM01q>0oE z(3-urJ-)?*f`W#JsOKCIG;dSbQ!?3mv?WdI3f;Pd7|3 z7TnM{9^7xFUVzq?R|$=wAU?Evh1Jv84-7;WyyA40B+VVCS$Hl3(~MxXHKo{Q zWGGI7UgC4ecG^+fnQ>Kb^8!6>0pCg*YH2bO(gQ{Dxpdr78*@(E+!siIC_pFq1H|G( zG7{1SY)H4JE_0PD$AHYEUJ)9 zv=^dTg!35j7`B%LJKp1Rw=8p{`A)cXCmG4I8gmp7Bp_ie(H16%T~|_`1&31Ngj;4; zT7p|P4C=LE$)>W5GhC;|3c8b$+>ND^bYK)`&nTsuP;2aP%dbxx&r=V#+-PGdu^gCl zGf)|3M?#0;TrdhEBOw8STjr9>c$W;f&}s#pWCTYb0m`JL{wC7zmg|dbitFU^D5)Im}Lt&gZrFL?LfzxF z8rg5aX--Be5G1VJ+6r5?3{A0Xopdc+FGPTwQ54{9oEW@fnzRs?b<`$6$NhTUW3iTh zfU;lt9AzKkHxGv@UId6T#?}aDK*yE2dKF+Z5F@|r6C*=Zi86N7_OYOE&xe|G)er4G zY9IOx(XnSqJ3}!o&xS_Wuy;|?3HA!%w`gvyfejGN`Jo{?5rq)73DD@=wNmF_)*l?B z`WNn|{PKy!;=D_%<`ft8X3ZFc5a&oplGi1)Fsa1Wu*lYMA}nhd`df7Qs(F0f zY(Et<2&yq42K0A_uuq%{7q3R!$r2<6>;d+On+PLo4s|5t;<8x?LwsJqXhPJxil=Z{ zwJZzewJTTr4~cdn+}zOU*$)^ApWd+h>Y{STT-He4L~0ixUOv6J&Wd zE#yfvVG9t+NKdnk^-pdz?3GO2sfagHzCD3p6RrSFI@#)mFI^-0?=qh`K)<|dyGFRI zv8{Gl#_9&_!= ze`QDXqH9LC3(k?Z1&VIt40Ky=8O z6@vwb$w>VqxK^|ZuwnNA9wyB=Z{8x^S+2v%w)t=6aX8()kDIoo)ZVetuQ+o$s{DVy zkMsULEVdm~>sPV0LwkS7&zH<47UD@Rzi6+TtKgJ@kPPKmoH<9F`^gq&PKSk#2)D7f z=IkHlGfJ{DFDoAlsFi@0LrXG`WJ3-JnD!%&8A zg!B+vizaGbLoVj$7T@Rt~?0gZ9 zq9ut%80yLZgHF=t?uUjGv!bYeg O0000 - - - {error.toString()} - {componentStack} - - - + + + + + Keyboard.dismiss()}> + + + + + {`Oops! We're having trouble displaying your information right now. But don't worry, your wallet and funds are safe and sound.`} + + + + + + ); } + +const styles = StyleSheet.create({ + headerWrap: { + height: pTd(52), + width: '100%', + backgroundColor: defaultColors.bg5, + }, + contentWrap: { + paddingHorizontal: pTd(20), + paddingTop: pTd(120), + paddingBottom: pTd(16), + }, + errorImgWrap: { + width: pTd(160), + height: pTd(140), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Referral/index.tsx b/packages/mobile-app-did/js/pages/Referral/index.tsx index b13039395f..2abfddcde1 100644 --- a/packages/mobile-app-did/js/pages/Referral/index.tsx +++ b/packages/mobile-app-did/js/pages/Referral/index.tsx @@ -17,21 +17,22 @@ import { sleep } from '@portkey-wallet/utils'; export default function Referral() { const credentials = useCredentials(); - const { address } = useCurrentWalletInfo(); + const { address, caHash } = useCurrentWalletInfo(); const gStyles = useGStyles(); const { t } = useLanguage(); + const init = useCallback(async () => { await sleep(200); if (address) { let name: keyof RootStackParamList = 'SecurityLock'; - if (credentials) name = 'Tab'; + if (credentials && caHash) name = 'Tab'; navigationService.reset(name); } setTimeout(() => { SplashScreen.hideAsync(); }, 500); - }, [credentials, address]); + }, [credentials, address, caHash]); useEffect(() => { init(); }, [init]); From 3cbce893d4a114495d9a107d917eccef2540b10f Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 30 Jun 2023 17:57:29 +0800 Subject: [PATCH 243/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20the=20l?= =?UTF-8?q?ogic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/AmountInput/TokenInput.tsx | 5 +++- .../app/web/pages/Send/index.tsx | 29 +++++-------------- .../app/web/pages/SendTransactions/index.tsx | 22 +++++++------- .../app/web/pages/Token/Custom/index.tsx | 24 ++++++++++----- .../app/web/pages/Token/Manage/index.less | 5 ++++ .../app/web/pages/Token/Manage/index.tsx | 12 ++++---- .../components/PermissionCheck/index.tsx | 2 ++ 7 files changed, 53 insertions(+), 46 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx index 721b7f3b20..43f8da44ed 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx @@ -44,6 +44,7 @@ export default function TokenInput({ const [, getTokenPrice] = useGetCurrentAccountTokenPrice(); const amountInUsdShow = useAmountInUsdShow(); const checkManagerSyncState = useCheckManagerSyncState(); + const [isManagerSynced, setIsManagerSynced] = useState(true); const amountInUsd = useMemo( () => amountInUsdShow(value || amount, 0, token.symbol), @@ -82,6 +83,7 @@ export default function TokenInput({ return; } const _isManagerSynced = await checkManagerSyncState(token.chainId); + setIsManagerSynced(_isManagerSynced); if (!_isManagerSynced) return; const fee = await getTranslationInfo(divDecimals(balance, token.decimals).toString()); if (fee) { @@ -119,6 +121,7 @@ export default function TokenInput({ const handleMax = useCallback(async () => { const _isManagerSynced = await checkManagerSyncState(token.chainId); + setIsManagerSynced(_isManagerSynced); if (_isManagerSynced) { setAmount(maxAmount); onChange({ amount: maxAmount, balance }); @@ -176,7 +179,7 @@ export default function TokenInput({
    - {errorMsg && {errorMsg}} + {errorMsg && {errorMsg}}
    ); } diff --git a/packages/web-extension-did/app/web/pages/Send/index.tsx b/packages/web-extension-did/app/web/pages/Send/index.tsx index 03aabce558..ce940ad6af 100644 --- a/packages/web-extension-did/app/web/pages/Send/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/index.tsx @@ -70,7 +70,6 @@ export default function Send() { const [balance, setBalance] = useState(''); const isValidSuffix = useIsValidSuffix(); const checkManagerSyncState = useCheckManagerSyncState(); - const [isManagerSynced, setIsManagerSynced] = useState(true); const [txFee, setTxFee] = useState(); const currentChain = useCurrentChain(state.chainId); @@ -197,6 +196,10 @@ export default function Send() { try { setLoading(true); if (!ZERO.plus(amount).toNumber()) return 'Please input amount'; + const _isManagerSynced = await checkManagerSyncState(state.chainId); + if (!_isManagerSynced) { + return 'Synchronizing on-chain account information...'; + } if (type === 'token') { if (timesDecimals(amount, tokenInfo.decimals).isGreaterThan(ZERO.plus(balance))) { return TransactionError.TOKEN_NOT_ENOUGH; @@ -213,11 +216,6 @@ export default function Send() { } else { return 'input error'; } - const _isManagerSynced = await checkManagerSyncState(state.chainId); - setIsManagerSynced(_isManagerSynced); - if (!_isManagerSynced) { - return 'Synchronizing on-chain account information...'; - } const fee = await getTranslationInfo(); console.log('---getTranslationInfo', fee); if (fee) { @@ -390,7 +388,7 @@ export default function Send() { setBalance(balance); }} getTranslationInfo={getTranslationInfo} - setErrorMsg={setErrorMsg} + setErrorMsg={setTipMsg} /> ), }, @@ -475,7 +473,7 @@ export default function Send() { )}
    - {errorMsg && {errorMsg}} + {errorMsg && {errorMsg}}
    )}
    {StageObj[stage].element}
    @@ -487,20 +485,7 @@ export default function Send() { {isPrompt ? : null}
    ); - }, [ - StageObj, - btnDisabled, - errorMsg, - isManagerSynced, - isPrompt, - navigate, - stage, - symbol, - t, - toAccount, - type, - walletName, - ]); + }, [StageObj, btnDisabled, errorMsg, isPrompt, navigate, stage, symbol, t, toAccount, type, walletName]); return <>{isPrompt ? : mainContent()}; } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index ff490487bf..88e01e3fea 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -56,7 +56,7 @@ export default function SendTransactions() { ); const [txParams, setTxParams] = useState({}); const checkManagerSyncState = useCheckManagerSyncState(); - const [isManagerSynced, setIsManagerSynced] = useState(true); + const [isManagerSynced, setIsManagerSynced] = useState(false); const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { @@ -129,16 +129,16 @@ export default function SendTransactions() { return; } const params = JSON.parse(txPayload[transactionInfoId]); - getTokenDecimals(params.symbol, params.chainId); + getTokenDecimals(params.symbol, payload?.chainId); setTxParams(params); - const _isManagerSynced = await checkManagerSyncState(params?.chainId); + const _isManagerSynced = await checkManagerSyncState(payload?.chainId); setIsManagerSynced(_isManagerSynced); if (_isManagerSynced) { getFee(params); } else { setErrMsg('the manager has not been synchronized yet'); // TODO } - }, [checkManagerSyncState, getFee, getTokenDecimals, transactionInfoId]); + }, [checkManagerSyncState, getFee, getTokenDecimals, payload?.chainId, transactionInfoId]); useEffect(() => { getTxPayload(); @@ -180,7 +180,7 @@ export default function SendTransactions() {
    {loading ? : `${formatAmountShow(divDecimals(amount, decimals), 8)}`} - {symbol} +  {symbol}
    {isMainnet &&
    {formatAmountInUsdShow(amount, decimals, symbol)}
    }
    @@ -190,7 +190,7 @@ export default function SendTransactions() {
    {loading ? : `${formatAmountShow(fee, 8)}`} - ELF +  ELF
    {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    @@ -207,7 +207,7 @@ export default function SendTransactions() { `${formatAmountShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee), 8)}` )} - {symbol} +  {symbol}
    {isMainnet && (
    @@ -220,14 +220,14 @@ export default function SendTransactions() {
    {loading ? : `${formatAmountShow(fee, 8)}`} - ELF +  ELF
    {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    - {loading ? : `${formatAmountShow(amount)}`} - {symbol} + {loading ? : `${formatAmountShow(divDecimals(amount, decimals), 8)}`} +  {symbol}
    {isMainnet &&
    {formatAmountInUsdShow(amount, 0, symbol)}
    }
    @@ -260,7 +260,7 @@ export default function SendTransactions() {
    {loading ? : `${formatAmountShow(fee)}`} - ELF +  ELF
    {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    }
    diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx index 0a3654dad1..417e6e8b8a 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx @@ -43,19 +43,20 @@ export default function CustomToken() { [chainList, isMainnet], ); - const handleChangeChainId = useCallback((chainId: ChainId) => { - setCurChain(chainId); - }, []); - const handleSearch = useCallback( - async (keyword: string) => { + async (keyword: string, chainId = curChainId) => { try { - if (!keyword) return; + if (!keyword) { + setCurToken({}); + setValue(''); + setErrorMsg(''); + return; + } setLoading(true); const res = await request.token.fetchTokenItemBySearch({ params: { symbol: keyword, - chainId: curChainId, + chainId, }, }); const { symbol, decimals, id } = res; @@ -63,6 +64,7 @@ export default function CustomToken() { setCurToken(res); setValue(symbol); } + setErrorMsg(''); } catch (error) { setCurToken({}); console.log('filter search error', error); @@ -75,6 +77,14 @@ export default function CustomToken() { const searchDebounce = useDebounceCallback(handleSearch, [value], 500); + const handleChangeChainId = useCallback( + (chainId: ChainId) => { + setCurChain(chainId); + if (value) handleSearch(value, chainId); + }, + [handleSearch, value], + ); + const handleBack = useCallback(() => { navigate('/add-token'); }, [navigate]); diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.less b/packages/web-extension-did/app/web/pages/Token/Manage/index.less index 9254f8e2de..98ea7eae75 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.less +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.less @@ -141,6 +141,7 @@ .add-button { width: fit-content; + margin-bottom: 24px; background-color: transparent; color: @font-9; border: none; @@ -189,6 +190,10 @@ } +.add-token.detail-page-prompt { + margin-bottom: 24px; +} + .left-icon { display: inline-block; font-size: 15px; diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx index 5ab3459036..ead2e141cc 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx @@ -13,7 +13,6 @@ import { transNetworkText } from '@portkey-wallet/utils/activity'; import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import PromptFrame from 'pages/components/PromptFrame'; import clsx from 'clsx'; -import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { request } from '@portkey-wallet/api/api-did'; import { useDebounceCallback } from '@portkey-wallet/hooks'; @@ -28,7 +27,7 @@ export default function AddToken() { const appDispatch = useAppDispatch(); const chainIdArray = useChainIdList(); const isMainnet = useIsMainnet(); - const [tokenShowList, setTokenShowList] = useState(tokenDataShowInMarket); + const [tokenShowList, setTokenShowList] = useState(tokenDataShowInMarket); useEffect(() => { if (!filterWord) { @@ -55,8 +54,10 @@ export default function AddToken() { chainIds: chainIdArray, }, }); + // TODO transfer data structure, includes isAdded, userTokenId setTokenShowList(res?.data || []); } catch (error) { + setTokenShowList([]); console.log('filter search error', error); } }, @@ -102,6 +103,8 @@ export default function AddToken() { [appDispatch, chainIdArray, filterWord, handleSearch], ); + const displayToken = useDebounceCallback(handleUserTokenDisplay, [], 500); + const renderTokenItemBtn = useCallback( (item: TokenItemShowType) => { const { isDefault = false, isAdded = true } = item; @@ -117,13 +120,13 @@ export default function AddToken() { ); }, - [handleUserTokenDisplay, t], + [displayToken, t], ); const renderTokenItem = useCallback( @@ -211,7 +214,6 @@ export default function AddToken() { />
    {renderTokenList} - {isPrompt ? : null} ); }, [filterWord, isPrompt, navigate, renderTokenList, rightElement, searchDebounce, t]); diff --git a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx index 30d1850c0e..d1e63ba843 100644 --- a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx @@ -10,6 +10,7 @@ import { useStorage } from 'hooks/useStorage'; import { sleep } from '@portkey-wallet/utils'; import { useIsNotLessThan768 } from 'hooks/useScreen'; import { useEffectOnce } from 'react-use'; +import OpenNewTabController from 'controllers/openNewTabController'; const timeout = async () => { // TODO This is a bug @@ -94,6 +95,7 @@ export default function PermissionCheck({ if (caHash) return getPassword(); if (pageType == 'Popup') { + await OpenNewTabController.closeOpenTabs(); return InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); } else { if (caInfo?.managerInfo) return navigate('/query-page'); From 46e50d0caa03444a04829157f314bb61e7afff96 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 3 Jul 2023 10:23:31 +0800 Subject: [PATCH 244/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20update=20provid?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/package.json | 8 +-- packages/types/package.json | 2 +- packages/utils/package.json | 6 +- packages/web-extension-did/package.json | 8 +-- yarn.lock | 87 ++++++++++++++++--------- 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index d7cdf1eac0..8707bafc37 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -51,10 +51,10 @@ "@portkey-wallet/i18n": "^1.3.0", "@portkey-wallet/store": "^1.3.0", "@portkey-wallet/utils": "^1.3.0", - "@portkey/mobile-provider": "0.0.1-alpha.25", - "@portkey/provider-types": "0.0.1-alpha.25", - "@portkey/provider-utils": "0.0.1-alpha.25", - "@portkey/providers": "0.0.1-alpha.25", + "@portkey/mobile-provider": "1.0.1-alpha.0", + "@portkey/provider-types": "1.0.1-alpha.0", + "@portkey/provider-utils": "1.0.1-alpha.0", + "@portkey/providers": "1.0.1-alpha.0", "@react-native-async-storage/async-storage": "^1.17.6", "@react-native-firebase/analytics": "^15.3.0", "@react-native-firebase/app": "^15.3.0", diff --git a/packages/types/package.json b/packages/types/package.json index 1aba2963cb..37a68b71be 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "version": "1.3.0", "type": "commonjs", "dependencies": { - "@portkey/provider-types": "0.0.1-alpha.25" + "@portkey/provider-types": "1.0.1-alpha.0" }, "scripts": { "prebuild": "rm -rf dist", diff --git a/packages/utils/package.json b/packages/utils/package.json index e691d4082c..5d3ca845d4 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -17,9 +17,9 @@ "query-string": "^7.1.1" }, "devDependencies": { - "@portkey/provider-types": "0.0.1-alpha.25", - "@portkey/provider-utils": "0.0.1-alpha.25", - "@portkey/providers": "0.0.1-alpha.25", + "@portkey/provider-types": "1.0.1-alpha.0", + "@portkey/provider-utils": "1.0.1-alpha.0", + "@portkey/providers": "1.0.1-alpha.0", "@react-native-async-storage/async-storage": "^1.17.6", "@types/is-url": "^1.2.30", "@types/react-native": "^0.69.7", diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index ad00093adb..30b1540088 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,10 +16,10 @@ "@portkey-wallet/store": "^1.3.0", "@portkey-wallet/types": "^1.3.0", "@portkey-wallet/utils": "^1.3.0", - "@portkey/extension-provider": "0.0.1-alpha.25", - "@portkey/provider-utils": "0.0.1-alpha.25", - "@portkey/provider-types": "0.0.1-alpha.25", - "@portkey/providers": "0.0.1-alpha.25", + "@portkey/extension-provider": "1.0.1-alpha.0", + "@portkey/provider-utils": "1.0.1-alpha.0", + "@portkey/provider-types": "1.0.1-alpha.0", + "@portkey/providers": "1.0.1-alpha.0", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index d13190f088..b3ca7ed917 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5184,55 +5184,80 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/chain@^0.0.1-alpha.25": - version "0.0.1-alpha.25" - resolved "https://registry.npmjs.org/@portkey/chain/-/chain-0.0.1-alpha.25.tgz#1fb79d5dc56067faed534fdfb639350c6f0e8e26" - integrity sha512-0dwAfghdWUWZYwQZoUyOl7yHvzVUUkd8+JJIbM2srG3c9AEdRticG18fNQvUARAUseEX6O1bd1lzXmlMMutfLg== +"@portkey/chain@^1.0.1-alpha.0": + version "1.0.1-alpha.0" + resolved "https://registry.npmjs.org/@portkey/chain/-/chain-1.0.1-alpha.0.tgz#fa3140d3159d3d465249b4fa7f8e9584fd533dea" + integrity sha512-+RuXkAYpXvQbZYHMs/g1oGy7b2uojMs30w0Og/1roTzes7nwIwpuEAmB/vc7s9fIbIKUHgMY5fSAWmbUUE29jg== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.25" + "@portkey/contracts" "^1.0.0-alpha.4" + "@portkey/provider-types" "^1.0.1-alpha.0" aelf-sdk "^3.2.44" -"@portkey/extension-provider@0.0.1-alpha.25": - version "0.0.1-alpha.25" - resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-0.0.1-alpha.25.tgz#c6931098315cc1e12e551a9758064dbc2a778555" - integrity sha512-hVBsvINepbqrnR/7CvxJDOfiZ8F4joAzr7uOGB28Fg/VFHpjonWpdk6/sw4hZ0jG+gmYJi/LMjj3t5xW3znfQQ== +"@portkey/contracts@^1.0.0-alpha.4": + version "1.0.0-alpha.4" + resolved "https://registry.npmjs.org/@portkey/contracts/-/contracts-1.0.0-alpha.4.tgz#eb69538b5188a578794090f2694a5ef3ba1a05bb" + integrity sha512-JkIbQwqFOOKMGBEKN9ax//9xJlzLVLvZchod5uhKO9tMQYCUmRS6ZLFUhlS0kwe0xyFoqk2CkPkDw+A8dWJ2xQ== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.25" - "@portkey/providers" "^0.0.1-alpha.25" + "@portkey/types" "^1.0.0-alpha.4" + "@portkey/utils" "^1.0.0-alpha.4" + aelf-sdk "^3.2.44" + +"@portkey/extension-provider@1.0.1-alpha.0": + version "1.0.1-alpha.0" + resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-1.0.1-alpha.0.tgz#3f106032eb21209c6fe7b7921879e07c0fbb1e61" + integrity sha512-LP3sxExlk7bDXRSZiv3KnXgibO2U75lbQxw8R4rkqmUaDmsPi54Q+T7LccKhaH/k/q/jCBn3lS0T76GcZCTniQ== + dependencies: + "@portkey/provider-types" "^1.0.1-alpha.0" + "@portkey/providers" "^1.0.1-alpha.0" "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/mobile-provider@0.0.1-alpha.25": - version "0.0.1-alpha.25" - resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-0.0.1-alpha.25.tgz#e7bf00dd87a3ac4bef5595085ac3051fbe2842a2" - integrity sha512-aOqWkFqqtgYr1IFTQVVIdgkt4q3SCLpiX5NcIuO9YVFJzM4suTIJoMfC8Ag0Jtwklrm50Bl1KRji0MBIHPcrfQ== +"@portkey/mobile-provider@1.0.1-alpha.0": + version "1.0.1-alpha.0" + resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-1.0.1-alpha.0.tgz#b059d3bd317e010358f031e71f778a86c26f9d60" + integrity sha512-4yYY+YS17vlKz1FsavusAI/NGbcvl6OQtR0GL9LzEhr2AvmQsLZjJd4nzwdx3cMZvBt4jAt9Hjq/hYF3678uhQ== -"@portkey/provider-types@0.0.1-alpha.25", "@portkey/provider-types@^0.0.1-alpha.25": - version "0.0.1-alpha.25" - resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-0.0.1-alpha.25.tgz#d1de731f939e4cee8dba188f7128c5bf7cf2527a" - integrity sha512-0rQh9qUMytI3ILFdavCFiOVvteJif7BLRIUt4ojC4Ae6cQYclKYHsXnBZifQw8tJjR0aPIN6tBsLNBs2FdHAXw== +"@portkey/provider-types@1.0.1-alpha.0", "@portkey/provider-types@^1.0.1-alpha.0": + version "1.0.1-alpha.0" + resolved "https://registry.npmjs.org/@portkey/provider-types/-/provider-types-1.0.1-alpha.0.tgz#303b383ac7306d91923b6b88c6ec356935371b2e" + integrity sha512-4tgoL7wJwW3ksS579KvdFFfoQhItKuURf5oKla80dGN9pdxaJ5MfW5vj/SORUZEYObe7WLwwP07zuPPWbBMO+g== dependencies: + "@portkey/types" "1.0.0-alpha.4" "@types/readable-stream" "^2.3.15" -"@portkey/provider-utils@0.0.1-alpha.25", "@portkey/provider-utils@^0.0.1-alpha.25": - version "0.0.1-alpha.25" - resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-0.0.1-alpha.25.tgz#072872ee290bf329daacdaef3786eb6d55b70763" - integrity sha512-S7bGEdhOqUTR7BhBoe67rJ3umy/Z0drJf5AK53huaLs8VGo8ll14Zo9hm9GGkHec3BIkBkuqTUmJziE6EaELtg== +"@portkey/provider-utils@1.0.1-alpha.0", "@portkey/provider-utils@^1.0.1-alpha.0": + version "1.0.1-alpha.0" + resolved "https://registry.npmjs.org/@portkey/provider-utils/-/provider-utils-1.0.1-alpha.0.tgz#0ef08b21ce0f6e2f5b259fdd696a9384e0c5948b" + integrity sha512-05kLcl7i2giqsdGGjBIdEBTavsipW9lkt/Ea1dy0JMA4mwOLdyLq3mW+S8N1vLRZ8u0IuQocsrE6kA7sRNnheA== dependencies: - "@portkey/provider-types" "^0.0.1-alpha.25" + "@portkey/provider-types" "^1.0.1-alpha.0" -"@portkey/providers@0.0.1-alpha.25", "@portkey/providers@^0.0.1-alpha.25": - version "0.0.1-alpha.25" - resolved "https://registry.npmjs.org/@portkey/providers/-/providers-0.0.1-alpha.25.tgz#f083a81f9ac38207cc62e6f986eba1c3946969fd" - integrity sha512-MDVjkCM0DsV4/zKP0GU4SUGBnB18Bu28NSHNBKsP8PvSQC9fPKlZqGb9nedyReKdMg2Lnf74WgH0Vbhdf9c6mw== +"@portkey/providers@1.0.1-alpha.0", "@portkey/providers@^1.0.1-alpha.0": + version "1.0.1-alpha.0" + resolved "https://registry.npmjs.org/@portkey/providers/-/providers-1.0.1-alpha.0.tgz#cd2ce0d5afd63c3bda0de1d0923a092d03e477db" + integrity sha512-46KDNeXB0fwzl5H/3DnLZLhJgAJU7e4+wqAoV3wjPc7vLSgXM7RLdqN2TpOUilPtJcsiOP8WtHIrYpb18e1S0A== dependencies: - "@portkey/chain" "^0.0.1-alpha.25" - "@portkey/provider-types" "^0.0.1-alpha.25" - "@portkey/provider-utils" "^0.0.1-alpha.25" + "@portkey/chain" "^1.0.1-alpha.0" + "@portkey/provider-types" "^1.0.1-alpha.0" + "@portkey/provider-utils" "^1.0.1-alpha.0" "@types/readable-stream" "^2.3.15" lodash "^4.17.21" readable-stream "^4.4.0" +"@portkey/types@1.0.0-alpha.4", "@portkey/types@^1.0.0-alpha.4": + version "1.0.0-alpha.4" + resolved "https://registry.npmjs.org/@portkey/types/-/types-1.0.0-alpha.4.tgz#2f22a75a1af6df31db15fce1c473ca9e1f07f127" + integrity sha512-mOSl89fN+hQ4zun6MF2uuQH5Bz8Owf6eA+N1HJ5fwKUMbQqr+7uI1HJZstoTSLFTLPjVSAIaIauXb2sfdRIm7A== + dependencies: + "@types/elliptic" "^6.4.14" + +"@portkey/utils@^1.0.0-alpha.4": + version "1.0.0-alpha.4" + resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.4.tgz#54565ff7018cdafb1604ad27ffc95e0a64bcf7fc" + integrity sha512-lw1mp6/U74fTHL8IQj9yn8orxDjsvkt1M9Klae0+eFnluudn78ELSP40HN96Ix2uFO5pEueJWIzpnYwCGUiJhA== + dependencies: + aelf-sdk "^3.2.44" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From d596788109c4a02017e80ef7624aad15585dcc48 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 3 Jul 2023 10:29:46 +0800 Subject: [PATCH 245/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20checkQRCod?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/message/index.ts | 1 + packages/api/api-did/message/utils.ts | 4 ++++ .../js/pages/Login/ScanLogin/index.tsx | 18 ++++++++++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/api/api-did/message/index.ts b/packages/api/api-did/message/index.ts index ae7a189d22..26984dbc29 100644 --- a/packages/api/api-did/message/index.ts +++ b/packages/api/api-did/message/index.ts @@ -1,4 +1,5 @@ export default { sendScanLoginSuccess: '/api/app/message/scanLoginSuccess', sendScanLogin: '/api/app/message/scanLogin', + checkQRCodeExist: '/api/app/qrcode/exist', }; diff --git a/packages/api/api-did/message/utils.ts b/packages/api/api-did/message/utils.ts index fbb857651c..e7837cac55 100644 --- a/packages/api/api-did/message/utils.ts +++ b/packages/api/api-did/message/utils.ts @@ -6,3 +6,7 @@ type sendScanLoginSuccessParams = { export async function sendScanLoginSuccess({ targetClientId }: sendScanLoginSuccessParams) { return request.message.sendScanLoginSuccess({ params: { targetClientId } }); } + +export const checkQRCodeExist = (id: string): Promise => { + return request.message.checkQRCodeExist({ params: { id } }); +}; diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index 6145fdef1d..cccaf3eec7 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import PageContainer from 'components/PageContainer'; import Svg from 'components/Svg'; import { pTd } from 'utils/unit'; @@ -20,6 +20,7 @@ import { extraDataEncode, getDeviceInfoFromQR } from '@portkey-wallet/utils/devi import socket from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; import useEffectOnce from 'hooks/useEffectOnce'; +import { checkQRCodeExist } from '@portkey-wallet/api/api-did/message/utils'; const ScrollViewProps = { disabled: true }; @@ -31,12 +32,14 @@ export default function ScanLogin() { const [loading, setLoading] = useState(); const getCurrentCAContract = useGetCurrentCAContract(); + const targetClientId = useMemo(() => (time ? `${managerAddress}_${time}` : undefined), [managerAddress, time]); + useEffectOnce(() => { - const timeData = time || Math.floor(Date.now() / 1000); + if (!targetClientId) return; try { request.message.sendScanLogin({ params: { - targetClientId: `${managerAddress}_${timeData}`, + targetClientId, }, }); } catch (error) { @@ -48,6 +51,13 @@ export default function ScanLogin() { if (!caHash || loading || !managerAddress) return; try { setLoading(true); + if (targetClientId) { + const isQRCodeExist = await checkQRCodeExist(targetClientId); + if (isQRCodeExist) { + // TODO: add Toast + } + } + const deviceInfo = getDeviceInfoFromQR(qrExtraData, deviceType); const contract = await getCurrentCAContract(); const extraData = await extraDataEncode(deviceInfo || {}, true); @@ -62,7 +72,7 @@ export default function ScanLogin() { CommonToast.failError(error); } setLoading(false); - }, [caHash, loading, qrExtraData, deviceType, getCurrentCAContract, address, managerAddress]); + }, [caHash, loading, managerAddress, targetClientId, qrExtraData, deviceType, getCurrentCAContract, address]); return ( Date: Mon, 3 Jul 2023 15:11:27 +0800 Subject: [PATCH 246/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20verify?= =?UTF-8?q?=20operationType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/verification/utils.ts | 4 +- .../mobile-app-did/js/hooks/authentication.ts | 4 +- .../pages/Guardian/SelectVerifier/index.tsx | 14 +-- .../pages/Guardian/VerifierDetails/index.tsx | 86 +++++++++---------- .../components/GuardianItem/index.tsx | 53 +++++------- .../My/Guardian/GuardianDetail/index.tsx | 4 +- .../pages/My/Guardian/GuardianEdit/index.tsx | 12 +-- packages/mobile-app-did/js/utils/api.ts | 4 +- packages/types/types-ca/authentication.ts | 4 +- packages/types/verifier.ts | 9 +- .../components/GuardianItems/index.tsx | 70 ++++++--------- .../pages/Guardians/GuardiansAdd/index.tsx | 6 +- .../pages/Guardians/GuardiansView/index.tsx | 6 +- .../app/web/pages/SelectVerifier/index.tsx | 6 +- .../app/web/pages/VerifierAccount/index.tsx | 32 +++---- .../pages/components/VerifierPage/index.tsx | 16 ++-- .../web-extension-did/app/web/utils/api.ts | 4 +- .../app/web/utils/lib/checkReCaptcha.ts | 4 +- 18 files changed, 139 insertions(+), 199 deletions(-) diff --git a/packages/api/api-did/verification/utils.ts b/packages/api/api-did/verification/utils.ts index 7fa4321586..ef78963706 100644 --- a/packages/api/api-did/verification/utils.ts +++ b/packages/api/api-did/verification/utils.ts @@ -2,7 +2,7 @@ import { IStorage, StorageBaseLoader } from '@portkey-wallet/types/storage'; import { request } from '@portkey-wallet/api/api-did'; import { RequestConfig } from '../../types'; import { LoginKeyType } from '@portkey-wallet/types/types-ca/wallet'; -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; type VerifierInfo = { verifierSessionId: string; @@ -15,7 +15,7 @@ export interface SendVerificationConfig extends RequestConfig { guardianIdentifier?: string; verifierId?: string; chainId: string | number; - operationType: RecaptchaType; + operationType: OperationTypeEnum; }; } diff --git a/packages/mobile-app-did/js/hooks/authentication.ts b/packages/mobile-app-did/js/hooks/authentication.ts index e9b52df492..e47fda7ada 100644 --- a/packages/mobile-app-did/js/hooks/authentication.ts +++ b/packages/mobile-app-did/js/hooks/authentication.ts @@ -16,7 +16,7 @@ import { changeCanLock } from 'utils/LockManager'; import { AppState } from 'react-native'; import appleAuth, { appleAuthAndroid } from '@invertase/react-native-apple-authentication'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; -import { VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; if (!isIos) { GoogleSignin.configure({ @@ -246,7 +246,7 @@ export type VerifyTokenParams = { verifierId?: string; chainId: ChainId; id: string; - verifierCodeOperation: VerifierCodeOperationType; + operationType: OperationTypeEnum; }; export function useVerifyGoogleToken() { diff --git a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx index 122fbc3df4..a3409b4045 100644 --- a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { StyleSheet, Text } from 'react-native'; import PageContainer from 'components/PageContainer'; import { TextL, TextM, TextS, TextXXXL } from 'components/CommonText'; @@ -23,13 +23,7 @@ import { VerifierImage } from '../components/VerifierImage'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import myEvents from 'utils/deviceEvent'; import { verification } from 'utils/api'; -import { - AuthenticationInfo, - RecaptchaType, - VerificationType, - VerifierCodeOperationType, - VerifierItem, -} from '@portkey-wallet/types/verifier'; +import { AuthenticationInfo, VerificationType, OperationTypeEnum, VerifierItem } from '@portkey-wallet/types/verifier'; import { useVerifyToken } from 'hooks/authentication'; import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useOnRequestOrSetPin } from 'hooks/login'; @@ -64,7 +58,7 @@ export default function SelectVerifier() { id: loginAccount, verifierId: selectedVerifier?.id, chainId: originChainId, - verifierCodeOperation: VerifierCodeOperationType.register, + operationType: OperationTypeEnum.register, }); onRequestOrSetPin({ showLoading: !isRequestResult, @@ -101,7 +95,7 @@ export default function SelectVerifier() { guardianIdentifier: loginAccount, verifierId: selectedVerifier?.id, chainId: originChainId, - operationType: RecaptchaType.register, + operationType: OperationTypeEnum.register, }, }); if (requestCodeResult.verifierSessionId) { diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index 92c466896e..0dc926de3d 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -9,9 +9,8 @@ import { StyleSheet, Text } from 'react-native'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; import { ApprovalType, - RecaptchaType, VerificationType, - VerifierCodeOperationType, + OperationTypeEnum, VerifierInfo, VerifyStatus, } from '@portkey-wallet/types/verifier'; @@ -105,39 +104,34 @@ export default function VerifierDetails() { } }, [caHash, getCurrentCAContract, guardianItem, managerAddress]); + const operationType: OperationTypeEnum = useMemo(() => { + switch (verificationType) { + case VerificationType.register: + return OperationTypeEnum.register; + case VerificationType.communityRecovery: + return OperationTypeEnum.communityRecovery; + case VerificationType.addGuardian: + case VerificationType.addGuardianByApprove: + return OperationTypeEnum.addGuardian; + case VerificationType.deleteGuardian: + return OperationTypeEnum.deleteGuardian; + case VerificationType.editGuardian: + return OperationTypeEnum.editGuardian; + case VerificationType.removeOtherManager: + return OperationTypeEnum.removeOtherManager; + case VerificationType.setLoginAccount: + return OperationTypeEnum.setLoginAccount; + default: + return OperationTypeEnum.unknown; + } + }, [verificationType]); + const onFinish = useLockCallback( async (code: string) => { if (!requestCodeResult || !guardianItem || !code) return; const isRequestResult = pin && verificationType === VerificationType.register && managerAddress; Loading.show(isRequestResult ? { text: 'Creating address on the chain...' } : undefined); try { - let verifierCodeOperationType: VerifierCodeOperationType; - switch (verificationType) { - case VerificationType.register: - verifierCodeOperationType = VerifierCodeOperationType.register; - break; - case VerificationType.communityRecovery: - verifierCodeOperationType = VerifierCodeOperationType.communityRecovery; - break; - case VerificationType.addGuardian: - case VerificationType.addGuardianByApprove: - verifierCodeOperationType = VerifierCodeOperationType.addGuardian; - break; - case VerificationType.deleteGuardian: - verifierCodeOperationType = VerifierCodeOperationType.deleteGuardian; - break; - case VerificationType.editGuardian: - verifierCodeOperationType = VerifierCodeOperationType.editGuardian; - break; - case VerificationType.removeOtherManager: - verifierCodeOperationType = VerifierCodeOperationType.removeOtherManager; - break; - case VerificationType.setLoginAccount: - default: - verifierCodeOperationType = VerifierCodeOperationType.unknown; - break; - } - const rst = await verification.checkVerificationCode({ params: { type: LoginType[guardianItem?.guardianType as LoginType], @@ -146,7 +140,7 @@ export default function VerifierDetails() { ...requestCodeResult, verifierId: guardianItem?.verifier?.id, chainId: originChainId, - verifierCodeOperationType, + operationType, }, }); !isRequestResult && CommonToast.success('Verified Successfully'); @@ -201,25 +195,31 @@ export default function VerifierDetails() { } !isRequestResult && Loading.hide(); }, - [requestCodeResult, guardianItem, originChainId, verificationType, setGuardianStatus, onSetLoginAccount], + [ + requestCodeResult, + guardianItem, + pin, + verificationType, + managerAddress, + originChainId, + operationType, + setGuardianStatus, + onSetLoginAccount, + onRequestOrSetPin, + ], ); + const resendCode = useCallback(async () => { try { Loading.show(); - let recaptchaType = RecaptchaType.optGuardian; - if (verificationType === VerificationType.register) { - recaptchaType = RecaptchaType.register; - } else if (verificationType === VerificationType.communityRecovery) { - recaptchaType = RecaptchaType.communityRecovery; - } const req = await verification.sendVerificationCode({ params: { type: LoginType[guardianItem?.guardianType as LoginType], guardianIdentifier: guardianItem?.guardianAccount, verifierId: guardianItem?.verifier?.id, chainId: originChainId, - operationType: recaptchaType, + operationType, }, }); if (req.verifierSessionId) { @@ -235,14 +235,8 @@ export default function VerifierDetails() { } digitInput.current?.reset(); Loading.hide(); - }, [ - guardianItem?.guardianAccount, - guardianItem?.guardianType, - guardianItem?.verifier?.id, - originChainId, - setGuardianStatus, - verificationType, - ]); + }, [guardianItem, operationType, originChainId, setGuardianStatus]); + return ( {guardianItem ? : null} diff --git a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx index 98de19950d..a7f11cd6aa 100644 --- a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx @@ -15,9 +15,8 @@ import { sleep } from '@portkey-wallet/utils'; import { ApprovalType, AuthenticationInfo, - RecaptchaType, VerificationType, - VerifierCodeOperationType, + OperationTypeEnum, VerifierInfo, VerifyStatus, } from '@portkey-wallet/types/verifier'; @@ -89,6 +88,24 @@ function GuardianItemButton({ verificationType: _verificationType, }; }, [approvalType, guardianItem]); + + const operationType: OperationTypeEnum = useMemo(() => { + switch (approvalType) { + case ApprovalType.addGuardian: + return OperationTypeEnum.addGuardian; + case ApprovalType.editGuardian: + return OperationTypeEnum.editGuardian; + case ApprovalType.deleteGuardian: + return OperationTypeEnum.deleteGuardian; + case ApprovalType.removeOtherManager: + return OperationTypeEnum.removeOtherManager; + case ApprovalType.communityRecovery: + return OperationTypeEnum.communityRecovery; + default: + return OperationTypeEnum.unknown; + } + }, [approvalType]); + const onSetGuardianStatus = useCallback( (guardianStatus: GuardiansStatusItem) => { setGuardianStatus?.({ key: guardianItem.key, status: guardianStatus }); @@ -106,10 +123,7 @@ function GuardianItemButton({ guardianIdentifier: guardianInfo.guardianItem.guardianAccount, verifierId: guardianInfo.guardianItem.verifier?.id, chainId: originChainId, - operationType: - approvalType === ApprovalType.communityRecovery - ? RecaptchaType.communityRecovery - : RecaptchaType.optGuardian, + operationType, }, }); if (req.verifierSessionId) { @@ -132,39 +146,18 @@ function GuardianItemButton({ CommonToast.failError(error); } Loading.hide(); - }, [onSetGuardianStatus, guardianInfo, approvalType, originChainId]); + }, [guardianInfo, originChainId, operationType, onSetGuardianStatus]); const onVerifierAuth = useCallback(async () => { try { Loading.show(); - let verifierCodeOperation: VerifierCodeOperationType; - switch (approvalType) { - case ApprovalType.addGuardian: - verifierCodeOperation = VerifierCodeOperationType.addGuardian; - break; - case ApprovalType.editGuardian: - verifierCodeOperation = VerifierCodeOperationType.editGuardian; - break; - case ApprovalType.deleteGuardian: - verifierCodeOperation = VerifierCodeOperationType.deleteGuardian; - break; - case ApprovalType.removeOtherManager: - verifierCodeOperation = VerifierCodeOperationType.removeOtherManager; - break; - case ApprovalType.communityRecovery: - verifierCodeOperation = VerifierCodeOperationType.communityRecovery; - break; - default: - verifierCodeOperation = VerifierCodeOperationType.unknown; - break; - } const rst = await verifyToken(guardianItem.guardianType, { accessToken: authenticationInfo?.[guardianItem.guardianAccount], id: guardianItem.guardianAccount, verifierId: guardianItem.verifier?.id, chainId: originChainId, - verifierCodeOperation, + operationType, }); if (rst.accessToken) { @@ -184,12 +177,12 @@ function GuardianItemButton({ } Loading.hide(); }, [ - approvalType, authenticationInfo, guardianItem.guardianAccount, guardianItem.guardianType, guardianItem.verifier?.id, onSetGuardianStatus, + operationType, originChainId, verifyToken, ]); diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx index e20167624a..1fed662985 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx @@ -14,7 +14,7 @@ import { useGuardiansInfo } from 'hooks/store'; import { useGetGuardiansInfo } from 'hooks/guardian'; import Loading from 'components/Loading'; import CommonToast from 'components/CommonToast'; -import { RecaptchaType, VerificationType } from '@portkey-wallet/types/verifier'; +import { VerificationType, OperationTypeEnum } from '@portkey-wallet/types/verifier'; import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import myEvents from 'utils/deviceEvent'; import { VerifierImage } from 'pages/Guardian/components/VerifierImage'; @@ -95,7 +95,7 @@ export default function GuardianDetail() { guardianIdentifier: guardian.guardianAccount, verifierId: guardian.verifier?.id, chainId: originChainId, - operationType: RecaptchaType.optGuardian, + operationType: OperationTypeEnum.setLoginAccount, }, }); if (req.verifierSessionId) { diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx index 39e058d2b3..96b25933a5 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx @@ -15,13 +15,7 @@ import { checkEmail } from '@portkey-wallet/utils/check'; import { useGuardiansInfo } from 'hooks/store'; import { LOGIN_TYPE_LIST } from '@portkey-wallet/constants/verifier'; import { PRIVATE_GUARDIAN_ACCOUNT } from '@portkey-wallet/constants/constants-ca/guardian'; -import { - ApprovalType, - RecaptchaType, - VerificationType, - VerifierCodeOperationType, - VerifierItem, -} from '@portkey-wallet/types/verifier'; +import { ApprovalType, VerificationType, OperationTypeEnum, VerifierItem } from '@portkey-wallet/types/verifier'; import { INIT_HAS_ERROR, INIT_NONE_ERROR } from 'constants/common'; import GuardianTypeSelectOverlay from '../components/GuardianTypeSelectOverlay'; import VerifierSelectOverlay from '../components/VerifierSelectOverlay'; @@ -171,7 +165,7 @@ const GuardianEdit: React.FC = () => { id: thirdPartyInfo.id, verifierId: verifierInfo.id, chainId: originChainId, - verifierCodeOperation: VerifierCodeOperationType.addGuardian, + operationType: OperationTypeEnum.addGuardian, }); Loading.hide(); @@ -256,7 +250,7 @@ const GuardianEdit: React.FC = () => { guardianIdentifier: guardianAccount, verifierId: selectedVerifier.id, chainId: originChainId, - operationType: RecaptchaType.optGuardian, + operationType: OperationTypeEnum.addGuardian, }, }); if (req.verifierSessionId) { diff --git a/packages/mobile-app-did/js/utils/api.ts b/packages/mobile-app-did/js/utils/api.ts index d627cfd013..d7938e0282 100644 --- a/packages/mobile-app-did/js/utils/api.ts +++ b/packages/mobile-app-did/js/utils/api.ts @@ -5,7 +5,7 @@ import { Verification, } from '@portkey-wallet/api/api-did/verification/utils'; import { IStorage } from '@portkey-wallet/types/storage'; -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; import { baseStore } from '@portkey-wallet/utils/mobile/storage'; import { verifyHumanMachine } from 'components/VerifyHumanMachine'; @@ -22,7 +22,7 @@ class MobileVerification extends Verification { if (item) { return item; } else { - let isNeedRecaptcha = operationType === RecaptchaType.register; + let isNeedRecaptcha = operationType === OperationTypeEnum.register; if (!isNeedRecaptcha) { const result = await request.verify.checkGoogleRecaptcha({ params: { diff --git a/packages/types/types-ca/authentication.ts b/packages/types/types-ca/authentication.ts index ef27df7b8c..a99ea6d9f9 100644 --- a/packages/types/types-ca/authentication.ts +++ b/packages/types/types-ca/authentication.ts @@ -1,10 +1,10 @@ import { ChainId } from '@portkey-wallet/types'; -import { VerifierCodeOperationType } from '../verifier'; +import { OperationTypeEnum } from '../verifier'; export type VerifyTokenParams = { accessToken?: string; verifierId?: string; chainId: ChainId; id: string; - verifierCodeOperation: VerifierCodeOperationType; + operationType: OperationTypeEnum; }; diff --git a/packages/types/verifier.ts b/packages/types/verifier.ts index c3f7d8c483..85138fb296 100644 --- a/packages/types/verifier.ts +++ b/packages/types/verifier.ts @@ -33,14 +33,8 @@ export enum ApprovalType { removeOtherManager, } -export enum RecaptchaType { - register = 0, - communityRecovery = 1, - optGuardian = 2, -} - // Indicates the type of operation to generate a signature file -export enum VerifierCodeOperationType { +export enum OperationTypeEnum { unknown = 0, register = 1, communityRecovery = 2, @@ -48,6 +42,7 @@ export enum VerifierCodeOperationType { deleteGuardian = 4, editGuardian = 5, removeOtherManager = 6, + setLoginAccount = 7, } export interface VerifierInfo { diff --git a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx index 2caab26380..963660a7ab 100644 --- a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx +++ b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx @@ -1,12 +1,6 @@ import { setCurrentGuardianAction, setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; import { UserGuardianItem, UserGuardianStatus } from '@portkey-wallet/store/store-ca/guardians/type'; -import { - ApprovalType, - RecaptchaType, - VerifierCodeOperationType, - VerifierInfo, - VerifyStatus, -} from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import { Button, message } from 'antd'; import clsx from 'clsx'; import VerifierPair from 'components/VerifierPair'; @@ -54,6 +48,24 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount [item.guardianType], ); + const operationType: OperationTypeEnum = useMemo(() => { + switch (query) { + case 'login': + return OperationTypeEnum.communityRecovery; + case 'guardians/add': + return OperationTypeEnum.addGuardian; + case 'guardians/edit': + return OperationTypeEnum.editGuardian; + case 'guardians/del': + return OperationTypeEnum.deleteGuardian; + default: + if (query?.indexOf('removeManage') !== -1) { + return OperationTypeEnum.removeOtherManager; + } + return OperationTypeEnum.unknown; + } + }, [query]); + const guardianSendCode = useCallback( async (item: UserGuardianItem) => { try { @@ -69,7 +81,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount type: LoginType[item.guardianType], verifierId: item?.verifier?.id || '', chainId: originChainId, - operationType: RecaptchaType.optGuardian, + operationType, }, }); setLoading(false); @@ -98,7 +110,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount message.error(verifyErrorHandler(error)); } }, - [setLoading, dispatch, navigate, query, originChainId], + [dispatch, originChainId, operationType, setLoading, navigate, query], ); const SendCode = useCallback( @@ -114,21 +126,13 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount throw 'User registration information is invalid, please fill in the registration method again'; } - let approvalType = ApprovalType.communityRecovery; - if (query && query.indexOf('removeManage') !== -1) { - approvalType = ApprovalType.removeOtherManager; - } - const result = await verification.sendVerificationCode({ params: { guardianIdentifier: item?.guardianAccount, type: LoginType[item.guardianType], verifierId: item.verifier?.id || '', chainId: originChainId, - operationType: - approvalType === ApprovalType.communityRecovery - ? RecaptchaType.communityRecovery - : RecaptchaType.optGuardian, + operationType, }, }); setLoading(false); @@ -149,7 +153,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount status: VerifyStatus.Verifying, }), ); - if (approvalType === ApprovalType.removeOtherManager) { + if (query && query.indexOf('removeManage') !== -1) { navigate('/setting/wallet-security/manage-devices/verifier-account', { state: query }); } else { navigate('/login/verifier-account', { state: 'login' }); @@ -162,7 +166,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount message.error(_error); } }, - [query, loginAccount, originChainId, setLoading, guardianSendCode, dispatch, navigate], + [setLoading, query, loginAccount, originChainId, operationType, guardianSendCode, dispatch, navigate], ); const verifyToken = useVerifyToken(); @@ -171,34 +175,12 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount async (item: UserGuardianItem) => { try { setLoading(true); - let verifierCodeOperation: VerifierCodeOperationType; - switch (query) { - case 'login': - verifierCodeOperation = VerifierCodeOperationType.communityRecovery; - break; - case 'guardians/add': - verifierCodeOperation = VerifierCodeOperationType.addGuardian; - break; - case 'guardians/edit': - verifierCodeOperation = VerifierCodeOperationType.editGuardian; - break; - case 'guardians/del': - verifierCodeOperation = VerifierCodeOperationType.deleteGuardian; - break; - default: - if (query?.indexOf('removeManage') !== -1) { - verifierCodeOperation = VerifierCodeOperationType.removeOtherManager; - } else { - verifierCodeOperation = VerifierCodeOperationType.unknown; - } - break; - } const result = await verifyToken(item.guardianType, { accessToken: loginAccount?.authenticationInfo?.[item.guardianAccount], id: item.guardianAccount, verifierId: item.verifier?.id, chainId: originChainId, - verifierCodeOperation, + operationType, }); const verifierInfo: VerifierInfo = { ...result, verifierId: item?.verifier?.id }; const { guardianIdentifier } = handleVerificationDoc(verifierInfo.verificationDoc); @@ -218,7 +200,7 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount setLoading(false); } }, - [dispatch, loginAccount, originChainId, setLoading, verifyToken, query], + [setLoading, verifyToken, loginAccount, originChainId, operationType, dispatch], ); const verifyingHandler = useCallback( diff --git a/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx b/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx index a56775fe75..9f9822882a 100644 --- a/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx +++ b/packages/web-extension-did/app/web/pages/Guardians/GuardiansAdd/index.tsx @@ -29,7 +29,7 @@ import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { request } from '@portkey-wallet/api/api-did'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { handleVerificationDoc } from '@portkey-wallet/utils/guardian'; -import { RecaptchaType, VerifierCodeOperationType, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum, VerifyStatus } from '@portkey-wallet/types/verifier'; import verificationApiConfig from '@portkey-wallet/api/api-did/verification'; import GuardianAddPrompt from './Prompt'; import GuardianAddPopup from './Popup'; @@ -301,7 +301,7 @@ export default function AddGuardian() { type: LoginType[guardianType as LoginType], verifierId: selectVerifierItem?.id || '', chainId: currentChain?.chainId || originChainId, - operationType: RecaptchaType.optGuardian, + operationType: OperationTypeEnum.addGuardian, }, }); setLoading(false); @@ -376,7 +376,7 @@ export default function AddGuardian() { verifierId: verifierVal, chainId: currentChain?.chainId || originChainId, accessToken: socialValue?.accessToken, - verifierCodeOperation: VerifierCodeOperationType.addGuardian, + operationType: OperationTypeEnum.addGuardian, }; let res; if (guardianType === LoginType.Apple) { diff --git a/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx b/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx index 17d1de2975..c22f8ffc6a 100644 --- a/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx +++ b/packages/web-extension-did/app/web/pages/Guardians/GuardiansView/index.tsx @@ -34,7 +34,7 @@ import { useCommonState } from 'store/Provider/hooks'; import AccountShow from '../components/AccountShow'; import { guardianIconMap } from '../utils'; import './index.less'; -import { RecaptchaType, VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; export default function GuardiansView() { const { t } = useTranslation(); @@ -76,7 +76,7 @@ export default function GuardiansView() { verifierId: opGuardian?.verifier?.id, chainId: currentChain?.chainId || originChainId, accessToken: data?.access_token, - verifierCodeOperation: VerifierCodeOperationType.unknown, + operationType: OperationTypeEnum.setLoginAccount, }; if (v === 'Google') { await getGoogleUserInfo(data?.access_token); @@ -186,7 +186,7 @@ export default function GuardiansView() { type: LoginType[opGuardian?.guardianType as LoginType], verifierId: opGuardian?.verifier?.id || '', chainId: originChainId, - operationType: RecaptchaType.optGuardian, + operationType: OperationTypeEnum.setLoginAccount, }, }); setLoading(false); diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx index cedb439f09..9efcc07dcb 100644 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx +++ b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx @@ -19,7 +19,7 @@ import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQ import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import './index.less'; -import { RecaptchaType, VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; export default function SelectVerifier() { const { verifierMap } = useGuardiansInfo(); @@ -67,7 +67,7 @@ export default function SelectVerifier() { type: LoginType[loginAccount.loginType], verifierId: selectItem.id, chainId: originChainId, - operationType: RecaptchaType.register, + operationType: OperationTypeEnum.register, }, }); setLoading(false); @@ -109,7 +109,7 @@ export default function SelectVerifier() { id: loginAccount.guardianAccount, verifierId: selectItem?.id, chainId: originChainId, - verifierCodeOperation: VerifierCodeOperationType.register, + operationType: OperationTypeEnum.register, }); dispatch( setRegisterVerifierAction({ diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 2bfc645a56..8c07e45edc 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -4,7 +4,7 @@ import { useAppDispatch, useLoginInfo, useGuardiansInfo, useUserInfo, useLoading import { useCallback, useMemo } from 'react'; import { message } from 'antd'; import { setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; -import { RecaptchaType, VerifierCodeOperationType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import useLocationState from 'hooks/useLocationState'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { handleGuardian } from 'utils/sandboxUtil/handleGuardian'; @@ -189,33 +189,24 @@ export default function VerifierAccount() { return !!currentGuardian?.isInitStatus; }, [currentGuardian, state]); - const recaptchaType = useMemo(() => { - if (state === 'register') { - return RecaptchaType.register; - } else if (state === 'login') { - return RecaptchaType.communityRecovery; - } - return RecaptchaType.optGuardian; - }, [state]); - - const verifierCodeOperationType = useMemo(() => { + const operationType: OperationTypeEnum = useMemo(() => { switch (state) { case 'register': - return VerifierCodeOperationType.register; + return OperationTypeEnum.register; case 'login': - return VerifierCodeOperationType.communityRecovery; + return OperationTypeEnum.communityRecovery; case 'guardians/add': - return VerifierCodeOperationType.addGuardian; + return OperationTypeEnum.addGuardian; case 'guardians/edit': - return VerifierCodeOperationType.editGuardian; + return OperationTypeEnum.editGuardian; case 'guardians/del': - return VerifierCodeOperationType.deleteGuardian; + return OperationTypeEnum.deleteGuardian; case 'guardians/setLoginAccount': default: if (state?.indexOf('removeManage') !== -1) { - return VerifierCodeOperationType.removeOtherManager; + return OperationTypeEnum.removeOtherManager; } else { - return VerifierCodeOperationType.unknown; + return OperationTypeEnum.unknown; } } }, [state]); @@ -229,12 +220,11 @@ export default function VerifierAccount() { currentGuardian={currentGuardian} guardianType={loginAccount?.loginType} onSuccess={onSuccess} - recaptchaType={recaptchaType} - verifierCodeOperationType={verifierCodeOperationType} + operationType={operationType} /> ), - [currentGuardian, isInitStatus, loginAccount, onSuccess, recaptchaType, verifierCodeOperationType], + [currentGuardian, isInitStatus, loginAccount, onSuccess, operationType], ); const props = useMemo( diff --git a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx index 4d1e9af314..0d8979aafd 100644 --- a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx @@ -17,7 +17,7 @@ import { verification } from 'utils/api'; import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCommonState } from 'store/Provider/hooks'; import { useLocation } from 'react-router'; -import { RecaptchaType, VerifierCodeOperationType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; const MAX_TIMER = 60; @@ -27,8 +27,7 @@ enum VerificationError { } interface VerifierPageProps { - recaptchaType: RecaptchaType; - verifierCodeOperationType: VerifierCodeOperationType; + operationType: OperationTypeEnum; loginAccount?: LoginInfo; currentGuardian?: UserGuardianItem; guardianType?: LoginType; @@ -37,8 +36,7 @@ interface VerifierPageProps { } export default function VerifierPage({ - recaptchaType, - verifierCodeOperationType, + operationType, currentGuardian, guardianType, isInitStatus, @@ -81,7 +79,7 @@ export default function VerifierPage({ verificationCode: code, verifierId: currentGuardian.verifier?.id || '', chainId: originChainId, - verifierCodeOperationType, + operationType, }, }); @@ -103,7 +101,7 @@ export default function VerifierPage({ message.error(_error); } }, - [guardianType, originChainId, currentGuardian, setLoading, onSuccess, t, verifierCodeOperationType], + [guardianType, originChainId, currentGuardian, setLoading, onSuccess, t, operationType], ); const resendCode = useCallback(async () => { @@ -118,7 +116,7 @@ export default function VerifierPage({ type: LoginType[guardianType], verifierId: currentGuardian.verifier?.id || '', chainId: originChainId, - operationType: recaptchaType, + operationType, }, }); setLoading(false); @@ -140,7 +138,7 @@ export default function VerifierPage({ const _error = verifyErrorHandler(error); message.error(_error); } - }, [currentGuardian, guardianType, originChainId, dispatch, setLoading, recaptchaType]); + }, [currentGuardian, guardianType, originChainId, dispatch, setLoading, operationType]); useEffect(() => { if (timer !== MAX_TIMER) return; diff --git a/packages/web-extension-did/app/web/utils/api.ts b/packages/web-extension-did/app/web/utils/api.ts index 4aa925e5dd..e25a5bdece 100644 --- a/packages/web-extension-did/app/web/utils/api.ts +++ b/packages/web-extension-did/app/web/utils/api.ts @@ -7,7 +7,7 @@ import { localStorage } from 'redux-persist-webextension-storage'; import { IStorage } from '@portkey-wallet/types/storage'; import { request } from '@portkey-wallet/api/api-did'; import { checkReCaptcha } from './lib/checkReCaptcha'; -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; export class ExtensionVerification extends Verification { constructor(store: IStorage) { @@ -22,7 +22,7 @@ export class ExtensionVerification extends Verification { if (item) { return item; } else { - const isNeedRecaptcha = operationType === RecaptchaType.register; + const isNeedRecaptcha = operationType === OperationTypeEnum.register; const reCaptcha = await checkReCaptcha(operationType, isNeedRecaptcha); if (reCaptcha) { config.headers = { diff --git a/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts b/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts index ad9fd5508b..7cdc0b300f 100644 --- a/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts +++ b/packages/web-extension-did/app/web/utils/lib/checkReCaptcha.ts @@ -1,4 +1,4 @@ -import { RecaptchaType } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; import { reCAPTCHAAction } from './serviceWorkerAction'; import { request } from '@portkey-wallet/api/api-did'; @@ -6,7 +6,7 @@ import { request } from '@portkey-wallet/api/api-did'; * check is need to call Google reCAPTCHA * @returns {string} check response */ -export const checkReCaptcha = async (operationType = RecaptchaType.register, isNeedRecaptcha = false) => { +export const checkReCaptcha = async (operationType = OperationTypeEnum.register, isNeedRecaptcha = false) => { if (!isNeedRecaptcha) { const req = await request.verify.checkGoogleRecaptcha({ params: { From 4669b93450c334aaf6f8e3d0e48856ebfc19c336 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 4 Jul 2023 10:38:38 +0800 Subject: [PATCH 247/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20http=20ico?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/assets/image/svgs.js | 2 +- .../js/assets/image/svgs/httpWarn.svg | 5 + .../js/assets/image/svgs/httpsLock.svg | 5 + .../js/components/GameImage/index.tsx | 60 ------ .../components/TabsOverlay/index.tsx | 6 +- .../components/TextWithProtocolIcon/index.tsx | 79 +++++++ .../DiscoverCmsListSection/index.tsx | 7 +- .../Discover/components/RecordItem/index.tsx | 8 +- .../SearchDiscoverSection/index.tsx | 7 +- .../js/pages/My/WalletSecurity/Dapp/index.tsx | 2 + .../js/pages/Token/transferData.json | 200 ------------------ 11 files changed, 104 insertions(+), 277 deletions(-) create mode 100644 packages/mobile-app-did/js/assets/image/svgs/httpWarn.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/httpsLock.svg delete mode 100644 packages/mobile-app-did/js/components/GameImage/index.tsx create mode 100644 packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/Token/transferData.json diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index a94257f070..aec3f18a62 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/httpWarn.svg b/packages/mobile-app-did/js/assets/image/svgs/httpWarn.svg new file mode 100644 index 0000000000..26bbac4697 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/httpWarn.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/httpsLock.svg b/packages/mobile-app-did/js/assets/image/svgs/httpsLock.svg new file mode 100644 index 0000000000..ad278e14a7 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/httpsLock.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/components/GameImage/index.tsx b/packages/mobile-app-did/js/components/GameImage/index.tsx deleted file mode 100644 index 9a9bbe2612..0000000000 --- a/packages/mobile-app-did/js/components/GameImage/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import Svg from 'components/Svg'; -import { pTd } from 'utils/unit'; -import { StyleSheet } from 'react-native'; -import { defaultColors } from 'assets/theme'; -import { Image } from 'react-native'; - -const GameImgMap = { - bingoGame: require('../../assets/image/pngs/bingoGame.png'), -} as const; - -export type GameImgMapKeyType = keyof typeof GameImgMap; - -interface CommonAvatarProps { - size?: number; - style?: any; - pngName?: GameImgMapKeyType; -} - -export default function DiscoverImage(props: CommonAvatarProps) { - const { size = pTd(32), pngName = 'bingoGame' } = props; - const [isError, setError] = React.useState(false); - - if (isError) return ; - return ( - { - setError(true); - }} - /> - ); -} - -const styles = StyleSheet.create({ - avatarWrap: { - width: pTd(48), - height: pTd(48), - borderRadius: pTd(48), - color: defaultColors.font5, - backgroundColor: defaultColors.bg4, - display: 'flex', - fontSize: pTd(20), - lineHeight: '100%', - overflow: 'hidden', - textAlign: 'center', - }, - hasBorder: { - borderWidth: pTd(1), - borderColor: defaultColors.border1, - }, - squareStyle: { - borderRadius: pTd(6), - backgroundColor: defaultColors.bg7, - borderWidth: 0, - color: defaultColors.font7, - }, -}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index 5ba3ff5718..d4cd3a6553 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -13,11 +13,11 @@ import { FontStyles } from 'assets/theme/styles'; import { setStringAsync } from 'expo-clipboard'; import CommonToast from 'components/CommonToast'; import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; - import { isIOS } from '@rneui/base'; import { useAppCASelector } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; enum HANDLE_TYPE { REFRESH = 'Refresh', @@ -112,9 +112,7 @@ const BrowserEditModal = ({ - - {browserInfo?.name || getHost(browserInfo?.url)} - + {browserInfo?.url} diff --git a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx new file mode 100644 index 0000000000..08f3ade79a --- /dev/null +++ b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx @@ -0,0 +1,79 @@ +import { isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; +import { defaultColors } from 'assets/theme'; + +import { TextM } from 'components/CommonText'; +import Svg from 'components/Svg'; +import React, { useMemo } from 'react'; +import { StyleSheet, View, ViewProps, StyleProp } from 'react-native'; +import { pTd } from 'utils/unit'; + +interface ITextWithProtocolIconProps { + title?: string; + url: string; + textFontSize?: number; + wrapStyle?: StyleProp; + iconSize?: number; + type?: 'iconLeft' | 'iconRight'; + location?: 'header' | 'other'; +} + +export default function TextWithProtocolIcon({ + title = '', + url, + textFontSize = pTd(14), + wrapStyle = {}, + iconSize = pTd(14), + type = 'iconRight', + location = 'other', +}: ITextWithProtocolIconProps) { + const isDanger = isDangerousLink(url); + + const fontSizeObj: any = { + fontSize: textFontSize, + }; + + console.log(isDanger); + + const ProtocolIcon = useMemo(() => { + if (isDanger) { + return ; + } else { + return ( + + ); + } + }, [iconSize, isDanger, location]); + + return ( + + {type === 'iconLeft' && ProtocolIcon} + + {title || url} + + {type === 'iconRight' && ProtocolIcon} + + ); +} + +const styles = StyleSheet.create({ + wrap: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + }, + text: { + paddingRight: pTd(0), + maxWidth: pTd(240), + overflow: 'hidden', + }, + iconStyle: { + marginRight: pTd(4), + marginLeft: pTd(4), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 6680d49157..be524d3fe8 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -4,11 +4,12 @@ import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; -import { TextL, TextM, TextS } from 'components/CommonText'; +import { TextM, TextS } from 'components/CommonText'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import React, { useCallback } from 'react'; import { ScrollView, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; export function DiscoverCmsListSection() { const GroupList = useDiscoverGroupList(); @@ -38,9 +39,7 @@ export function DiscoverCmsListSection() { onClickJump(item)}> - - {item.title} - + {item.description} diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx index eeb4c93ed3..e239eb1f7d 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx @@ -1,12 +1,14 @@ import { defaultColors } from 'assets/theme'; import { FontStyles } from 'assets/theme/styles'; -import { TextM, TextS } from 'components/CommonText'; +import { TextS } from 'components/CommonText'; import React, { memo } from 'react'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { pTd } from 'utils/unit'; import { IRecordsItemType } from '@portkey-wallet/types/types-ca/discover'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; + type RecordListItemType = { item: IRecordsItemType; onPress?: () => void; @@ -20,9 +22,7 @@ const RecordItem: React.FC = props => { - - {item?.title || item?.url} - + {item?.url || ''} diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx index e46144c8d0..9370913c96 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react'; import { StyleSheet, View, ScrollView, TouchableOpacity } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { useLanguage } from 'i18n/hooks'; -import { TextM, TextS } from 'components/CommonText'; +import { TextS } from 'components/CommonText'; import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import NoData from 'components/NoData'; @@ -12,6 +12,7 @@ import { defaultColors } from 'assets/theme'; import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; interface ISearchDiscoverSectionProps { searchedDiscoverList: DiscoverItem[]; } @@ -44,9 +45,7 @@ export default function SearchDiscoverSection(props: ISearchDiscoverSectionProps - - {item?.title} - + {item?.description} diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx index 8c68957c1d..9061fd6c8d 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -19,6 +19,7 @@ import fonts from 'assets/theme/fonts'; import NoData from 'components/NoData'; import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; const DeviceList: React.FC = () => { const { refetch } = useDeviceList(); @@ -49,6 +50,7 @@ const DeviceList: React.FC = () => { {item?.name || getHost(item.origin)} + {item?.origin || getHost(item.origin)} diff --git a/packages/mobile-app-did/js/pages/Token/transferData.json b/packages/mobile-app-did/js/pages/Token/transferData.json deleted file mode 100644 index e110dea8b8..0000000000 --- a/packages/mobile-app-did/js/pages/Token/transferData.json +++ /dev/null @@ -1,200 +0,0 @@ -[ { -"id": 206381, -"txId": "7b2fb47709dec122ec2c2e36a8032f6a2999919af6c1660f97ea0b6e97b18439", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "FXM24cEKUDqHoXFnAo9H1oza2wkELVFh7oH2eGCFoSbykAgwy", -"amount": "4600000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-09-09T06:57:08.1718905Z", -"method": "Release", -"blockHeight": 108110910, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 202813, -"txId": "8505bd29cdb86f85bb820fe2706194f65b95b85c9c7d299af7d4cb6f7624d6c1", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "pHra7X9jLj5NAnccFSJQWqeVhJyTKenTf9ivK421TgGPpHv9U", -"amount": "1200000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-09-01T11:13:24.141468Z", -"method": "Release", -"blockHeight": 106773204, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 200578, -"txId": "8d3e233a1963eab7a60c1b76e12109b8d76f57b7731a1b27149eac9636271705", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "vDFbsr1r8xsC3gEgcKLKJqhvuN2ZwXo97vMNYwy1qjWYD4tLa", -"amount": "3600000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-08-26T06:34:36.175202Z", -"method": "Release", -"blockHeight": 105713445, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 194793, -"txId": "ffdcfdecab57ef5a336dca99ff25d7bb904670e8213b9d39714176a69ac07a20", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "2mkuq8zdiWa6tSZm8Mw26idnUaQMarPiBgAb18Q7YLqwsXMZMY", -"amount": "4000000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-08-11T06:02:20.1855375Z", -"method": "Release", -"blockHeight": 103152308, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 191683, -"txId": "5e526ba00b4b77b3522a4f89679f5f1d8c43ec0a3dafb12f231e4ca7111b2890", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "cVVn1XKgjbm13KDb2ySXte83SWNRcsqSHDpL2Zc85JLiQyW3h", -"amount": "5000000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-08-03T08:58:44Z", -"method": "Release", -"blockHeight": 101826640, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 175648, -"txId": "734353787e97e0780294a830e92fccc63a2ad335ba2cdb98dccb969deb01aaa1", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "2FaKMniuPNwzNNFKDD2s8ERkQukeQoPy1uPvn1oyiq6Jq77q6H", -"amount": "2000000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": "Test", -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-07-08T12:53:28Z", -"method": "Release", -"blockHeight": 97442853, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 175627, -"txId": "c00317c45083aead98bf7be9d07eb9026c391795fa66a749dca13c9dae01ddf5", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "2FaKMniuPNwzNNFKDD2s8ERkQukeQoPy1uPvn1oyiq6Jq77q6H", -"amount": "10.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": "Test", -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-07-08T12:33:28.2520247Z", -"method": "Release", -"blockHeight": 97440471, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 123672, -"txId": "eb206e04f9fe278114c5b60107a832ddc17362f2c6a06fd434f6dd9da6e5ef77", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "2ExtaRkjDiFhkGH8hwLZYVpRAnXe7awa25C61KVWy47uwnRw4s", -"amount": "500000.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": "Transfer for swap", -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-05-05T13:15:28Z", -"method": "Release", -"blockHeight": 86556949, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 84071, -"txId": "23397524494e9843cda4ef60b788c63c2c1c0896e9db4f151cb44e7f177afcd8", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "UVVvmaXbWtM44nTviqiz7cKNS2tcizdrauKTciovQ7ujK9gtS", -"amount": "153257.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-02-07T11:15:00.1988963Z", -"method": "Release", -"blockHeight": 71653382, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}, -{ -"id": 83941, -"txId": "ed921b52e9bbee58b0f34bd7e9c97d7fbb0b912e0057438be6b1490f18859306", -"from": "25CuX2FXDvhaj7etTpezDQDunk5xGhytxE68yTYJJfMkQwvj5p", -"to": "21dtMzbgXEiPs3XWWCQorgodAUdAHYM4RLiFBaBREyk6p45eCW", -"amount": "100.00000000", -"symbol": "ELF", -"action": "Transferred", -"isCrossChain": "no", -"relatedChainId": "AELF", -"memo": null, -"txFee": { - "ELF": 0.24635 -}, -"time": "2022-02-07T08:53:52.1769321Z", -"method": "Release", -"blockHeight": 71636567, -"addressFrom": "2R1rgEKkG84XGtbx6fvxExzChaXEyJrfSnvMpuKCGrUFoR5SKz", -"addressTo": "XyRN9VNabpBiVUFeX2t7ZUR2b3tWV7U31exufJ2AUepVb5t56" -}] \ No newline at end of file From 9913a17de3df52b709e462afbb48c8df2053f5c2 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 4 Jul 2023 12:00:54 +0800 Subject: [PATCH 248/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20joint=20debug=20f?= =?UTF-8?q?ix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/SendTransactions/index.tsx | 6 ++--- .../app/web/pages/Token/Custom/index.tsx | 11 +++++---- .../app/web/pages/Token/Manage/index.less | 1 - .../app/web/pages/Token/Manage/index.tsx | 24 +++++++++++++------ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 88e01e3fea..78ca5dc464 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -106,7 +106,7 @@ export default function SendTransactions() { try { const tokenDetail = await request.token.fetchTokenItemBySearch({ params: { - token, + symbol: token, chainId, }, }); @@ -129,14 +129,14 @@ export default function SendTransactions() { return; } const params = JSON.parse(txPayload[transactionInfoId]); - getTokenDecimals(params.symbol, payload?.chainId); + getTokenDecimals(params?.paramsOption?.symbol, payload?.chainId); setTxParams(params); const _isManagerSynced = await checkManagerSyncState(payload?.chainId); setIsManagerSynced(_isManagerSynced); if (_isManagerSynced) { getFee(params); } else { - setErrMsg('the manager has not been synchronized yet'); // TODO + setErrMsg('Synchronizing on-chain account information...'); } }, [checkManagerSyncState, getFee, getTokenDecimals, payload?.chainId, transactionInfoId]); diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx index 417e6e8b8a..fd198abed3 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx @@ -1,6 +1,6 @@ import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { Button, Input } from 'antd'; +import { Button, Input, message } from 'antd'; import CustomSvg from 'components/CustomSvg'; import TitleWrapper from 'components/TitleWrapper'; import { useCallback, useMemo, useState } from 'react'; @@ -15,6 +15,7 @@ import { useDebounceCallback } from '@portkey-wallet/hooks'; import { transNetworkText } from '@portkey-wallet/utils/activity'; import { ChainId } from '@portkey-wallet/types'; import { request } from '@portkey-wallet/api/api-did'; +import { handleErrorMessage } from '@portkey-wallet/utils'; import './index.less'; export default function CustomToken() { @@ -101,11 +102,13 @@ export default function CustomToken() { isDisplay: !curToken?.isDisplay, }, }); - setLoading(false); navigate('/add-token'); - } catch (error) { - setLoading(false); + } catch (error: any) { + const err = handleErrorMessage(error, 'add custom token error'); + message.error(err); console.log('add custom token error', error); + } finally { + setLoading(false); } } }, [curToken?.id, curToken?.isDefault, curToken?.isDisplay, navigate, setLoading]); diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.less b/packages/web-extension-did/app/web/pages/Token/Manage/index.less index 98ea7eae75..fa5c9e766d 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.less +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.less @@ -141,7 +141,6 @@ .add-button { width: fit-content; - margin-bottom: 24px; background-color: transparent; color: @font-9; border: none; diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx index ead2e141cc..995fd5876d 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx @@ -6,7 +6,7 @@ import CustomSvg from 'components/CustomSvg'; import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; import DropdownSearch from 'components/DropdownSearch'; import { useTranslation } from 'react-i18next'; -import { useAppDispatch, useCommonState, useTokenInfo, useUserInfo } from 'store/Provider/hooks'; +import { useAppDispatch, useCommonState, useLoading, useTokenInfo, useUserInfo } from 'store/Provider/hooks'; import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import { useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { transNetworkText } from '@portkey-wallet/utils/activity'; @@ -16,6 +16,7 @@ import clsx from 'clsx'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { request } from '@portkey-wallet/api/api-did'; import { useDebounceCallback } from '@portkey-wallet/hooks'; +import { handleErrorMessage } from '@portkey-wallet/utils'; import './index.less'; export default function AddToken() { @@ -27,6 +28,7 @@ export default function AddToken() { const appDispatch = useAppDispatch(); const chainIdArray = useChainIdList(); const isMainnet = useIsMainnet(); + const { setLoading } = useLoading(); const [tokenShowList, setTokenShowList] = useState(tokenDataShowInMarket); useEffect(() => { @@ -36,8 +38,8 @@ export default function AddToken() { }, [filterWord, tokenDataShowInMarket]); useEffect(() => { - passwordSeed && appDispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray })); - }, [passwordSeed, appDispatch, chainIdArray]); + !filterWord && passwordSeed && appDispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray })); + }, [passwordSeed, appDispatch, chainIdArray, filterWord]); const handleAddCustomToken = useCallback(() => { setFilterWord(''); @@ -48,20 +50,27 @@ export default function AddToken() { async (keyword: string) => { try { if (!keyword) return; + setLoading(true); const res = await request.token.fetchTokenListBySearch({ params: { symbol: keyword, chainIds: chainIdArray, }, }); - // TODO transfer data structure, includes isAdded, userTokenId - setTokenShowList(res?.data || []); + const _target = (res || []).map((item: any) => ({ + ...item, + isAdded: item.isDisplay, + userTokenId: item.id, + })); + setTokenShowList(_target); } catch (error) { setTokenShowList([]); console.log('filter search error', error); + } finally { + setLoading(false); } }, - [chainIdArray], + [chainIdArray, setLoading], ); const searchDebounce = useDebounceCallback(handleSearch, [filterWord], 500); @@ -96,7 +105,8 @@ export default function AddToken() { }, 1000); message.success('success'); } catch (error: any) { - message.error(error?.message || 'handle display error'); + const err = handleErrorMessage(error, 'handle display error'); + message.error(err); console.log('=== userToken display', error); } }, From 8f25b0cda38e13ca49b171227c998949e17c50c7 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 4 Jul 2023 13:50:20 +0800 Subject: [PATCH 249/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lottieFiles/globalLoading.json} | 0 .../lottieFiles}/loading.json | 0 .../js/components/CommonToast/index.tsx | 2 +- .../js/components/Loading/index.tsx | 2 +- .../components/TransactionOverlay/index.tsx | 104 ++++++++++++++---- .../TransactionOverlay/styles/index.ts | 20 +++- .../mobile-app-did/js/pages/Token/index.ts | 2 + .../store/store-ca/tokenManagement/action.ts | 1 + .../store/store-ca/tokenManagement/slice.ts | 3 - 9 files changed, 103 insertions(+), 31 deletions(-) rename packages/mobile-app-did/js/{components/Loading/data.json => assets/lottieFiles/globalLoading.json} (100%) rename packages/mobile-app-did/js/{components/CommonToast => assets/lottieFiles}/loading.json (100%) diff --git a/packages/mobile-app-did/js/components/Loading/data.json b/packages/mobile-app-did/js/assets/lottieFiles/globalLoading.json similarity index 100% rename from packages/mobile-app-did/js/components/Loading/data.json rename to packages/mobile-app-did/js/assets/lottieFiles/globalLoading.json diff --git a/packages/mobile-app-did/js/components/CommonToast/loading.json b/packages/mobile-app-did/js/assets/lottieFiles/loading.json similarity index 100% rename from packages/mobile-app-did/js/components/CommonToast/loading.json rename to packages/mobile-app-did/js/assets/lottieFiles/loading.json diff --git a/packages/mobile-app-did/js/components/CommonToast/index.tsx b/packages/mobile-app-did/js/components/CommonToast/index.tsx index e464f18a90..921c131ad8 100644 --- a/packages/mobile-app-did/js/components/CommonToast/index.tsx +++ b/packages/mobile-app-did/js/components/CommonToast/index.tsx @@ -64,7 +64,7 @@ const icons: any = { success: , fail: , warning: , - loading: , + loading: , } as const; const show = (...args: TostProps) => { diff --git a/packages/mobile-app-did/js/components/Loading/index.tsx b/packages/mobile-app-did/js/components/Loading/index.tsx index b71d163bb3..f4653beeb5 100644 --- a/packages/mobile-app-did/js/components/Loading/index.tsx +++ b/packages/mobile-app-did/js/components/Loading/index.tsx @@ -24,7 +24,7 @@ type LoadingPositionType = 'center' | 'bottom'; function LoadingBody({ text }: { text?: string; position?: LoadingPositionType; iconType: IconType }) { return ( - + {text} ); diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 8e6b19687f..294a6a02e5 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -25,6 +25,7 @@ import DappInfoSection from '../DappInfoSection'; import TransactionDataSection from '../TransactionDataSection'; import { ELF_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; import { styles, transferGroupStyle } from './styles/index'; +import Lottie from 'lottie-react-native'; type TransactionModalPropsType = { dappInfo: DappStoreItem; @@ -46,15 +47,18 @@ const ConnectModal = (props: TransactionModalPropsType) => { const chainInfo = useCurrentChain(transactionInfo.chainId); const [, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); - const isCAContract = useMemo( - () => chainInfo?.caContractAddress === transactionInfo?.contractAddress, - [chainInfo?.caContractAddress, transactionInfo?.contractAddress], - ); + const [decimal, setDecimal] = useState('0'); + const [isFetchingDecimal, setIsFetchingDecimal] = useState(false); const [fee, setFee] = useState(''); const [isFetchingFee, setIsFetchingFee] = useState(true); const [noEnoughFee, setNoEnoughFee] = useState(false); + const isCAContract = useMemo( + () => chainInfo?.caContractAddress === transactionInfo?.contractAddress, + [chainInfo?.caContractAddress, transactionInfo?.contractAddress], + ); + const isTransfer = useMemo(() => transactionInfo.method.toLowerCase() === 'transfer', [transactionInfo.method]); const buttonList = useMemo(() => { @@ -100,9 +104,19 @@ const ConnectModal = (props: TransactionModalPropsType) => { <> {isTransfer && ( <> - - {`${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`} - + + {isFetchingDecimal && ( + + )} + + {isFetchingDecimal ? symbol : `${formatAmountShow(divDecimals(amount, decimals), 8)} ${symbol}`} + + {isMainnet && ( {`${formatAmountInUsdShow(amount, decimals, symbol)}`} )} @@ -140,10 +154,20 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Transaction Fee')} - {`${formatAmountShow( - divDecimals(fee, ELF_DECIMAL), - 8, - )} ELF`} + + + {isFetchingFee && ( + + )} + + {isFetchingFee ? 'ELF' : `${formatAmountShow(divDecimals(fee, ELF_DECIMAL), 8)} ELF`} + + {isMainnet && ( @@ -163,10 +187,22 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Total')} - {`${formatAmountShow( - divDecimals(ZERO.plus(amount).plus(fee), decimals), - 8, - )} ${symbol}`} + + + {isFetchingFee && ( + + )} + + {isFetchingFee + ? 'ELF' + : `${formatAmountShow(divDecimals(ZERO.plus(amount).plus(fee), decimals), 8)} ${symbol}`} + + {isMainnet && ( @@ -185,10 +221,19 @@ const ConnectModal = (props: TransactionModalPropsType) => { {t('Total')} - {`${formatAmountShow( - divDecimals(ZERO.plus(fee), ELF_DECIMAL), - 8, - )} ELF`} + + {isFetchingFee && ( + + )} + + {isFetchingFee ? 'ELF' : `${formatAmountShow(divDecimals(ZERO.plus(fee), ELF_DECIMAL), 8)} ELF`} + + {isMainnet && ( @@ -202,10 +247,21 @@ const ConnectModal = (props: TransactionModalPropsType) => { )} - {`${formatAmountShow( - divDecimals(ZERO.plus(amount), decimals), - 8, - )} ${symbol}`} + + {isFetchingDecimal && ( + + )} + + {isFetchingDecimal + ? symbol + : `${formatAmountShow(divDecimals(ZERO.plus(amount), decimals), 8)} ${symbol}`} + + {isMainnet && ( @@ -226,6 +282,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, [ fee, formatAmountInUsdShow, + isFetchingDecimal, + isFetchingFee, isMainnet, isTransfer, noEnoughFee, diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts index 16f1e1d7b5..ca5c6149ea 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/styles/index.ts @@ -1,4 +1,4 @@ -import { screenHeight } from '@portkey-wallet/utils/mobile/device'; +import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import fonts from 'assets/theme/fonts'; @@ -25,7 +25,8 @@ export const styles = StyleSheet.create({ }, scrollSection: { paddingLeft: pTd(20), - width: '100%', + paddingRight: pTd(20), + width: screenWidth, height: screenHeight / 2, }, blank: { @@ -43,7 +44,20 @@ export const transferGroupStyle = StyleSheet.create({ marginTop: pTd(4), fontSize: pTd(28), textAlign: 'center', - width: pTd(335), + }, + tokenWrap: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + loadingIcon: { + width: pTd(20), + marginRight: pTd(8), + }, + smallLoadingIcon: { + width: pTd(12), + marginRight: pTd(4), }, tokenUSD: { color: defaultColors.font3, diff --git a/packages/mobile-app-did/js/pages/Token/index.ts b/packages/mobile-app-did/js/pages/Token/index.ts index 7f596a4f3c..9eb08d984a 100644 --- a/packages/mobile-app-did/js/pages/Token/index.ts +++ b/packages/mobile-app-did/js/pages/Token/index.ts @@ -1,9 +1,11 @@ import ManageTokenList from './ManageTokenList'; import TokenDetail from './TokenDetail/index'; +import CustomToken from './CustomToken/index'; const stackNav = [ { name: 'ManageTokenList', component: ManageTokenList }, { name: 'TokenDetail', component: TokenDetail }, + { name: 'CustomToken', component: CustomToken }, ] as const; export default stackNav; diff --git a/packages/store/store-ca/tokenManagement/action.ts b/packages/store/store-ca/tokenManagement/action.ts index 8dd58724c1..2129172ba4 100644 --- a/packages/store/store-ca/tokenManagement/action.ts +++ b/packages/store/store-ca/tokenManagement/action.ts @@ -12,6 +12,7 @@ export const fetchAllTokenListAsync = createAsyncThunk( 'tokenManagement/fetchAllTokenListAsync', async ({ keyword = '', chainIdArray }: { keyword?: string; chainIdArray?: string[] }) => { const response = await fetchAllTokenList({ keyword, chainIdArray: chainIdArray || [] }); + return { list: response.items, totalRecordCount: response.totalRecordCount }; }, ); diff --git a/packages/store/store-ca/tokenManagement/slice.ts b/packages/store/store-ca/tokenManagement/slice.ts index 2146236922..c95f60bcf8 100644 --- a/packages/store/store-ca/tokenManagement/slice.ts +++ b/packages/store/store-ca/tokenManagement/slice.ts @@ -49,9 +49,6 @@ export const tokenManagementSlice = createSlice({ })); state.tokenDataShowInMarket = tmpToken; - // state.tokenDataShowInMarket = [...state.tokenDataShowInMarket, ...tmpToken]; - // state.skipCount = tmpToken.length; - // state.totalRecordCount = totalRecordCount; state.isFetching = false; }) .addCase(fetchAllTokenListAsync.rejected, state => { From 492475872fac5443a05208b4503be40118e6e16c Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 4 Jul 2023 15:58:43 +0800 Subject: [PATCH 250/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20scan=20?= =?UTF-8?q?word?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx | 4 +++- packages/mobile-app-did/js/pages/Login/components/QRCode.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index cccaf3eec7..95ebfb5d16 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -54,7 +54,9 @@ export default function ScanLogin() { if (targetClientId) { const isQRCodeExist = await checkQRCodeExist(targetClientId); if (isQRCodeExist) { - // TODO: add Toast + CommonToast.warn('The QR code has already been scanned by another device.'); + setLoading(false); + return; } } diff --git a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx index 1793e0ca46..3651f0c150 100644 --- a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx @@ -143,7 +143,7 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi {isScanQRCode && ( - Waiting for authorization... + Waiting for authorization.... )} From 727266ea97579f33302703f7999d27cf7bd34075 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 4 Jul 2023 18:36:52 +0800 Subject: [PATCH 251/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20custom=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/FormItem/index.tsx | 32 +++ .../js/pages/Token/CustomToken/index.tsx | 182 +++++++++++++++ .../components/FilterToken/index.tsx | 109 +++++++++ .../components/PopularToken/index.tsx | 60 +++++ .../components/TokenItem/index.tsx | 91 ++++++++ .../js/pages/Token/ManageTokenList/index.tsx | 210 ++++++------------ 6 files changed, 538 insertions(+), 146 deletions(-) create mode 100644 packages/mobile-app-did/js/components/FormItem/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx diff --git a/packages/mobile-app-did/js/components/FormItem/index.tsx b/packages/mobile-app-did/js/components/FormItem/index.tsx new file mode 100644 index 0000000000..3a638c16aa --- /dev/null +++ b/packages/mobile-app-did/js/components/FormItem/index.tsx @@ -0,0 +1,32 @@ +import { defaultColors } from 'assets/theme'; +import { TextM } from 'components/CommonText'; +import React, { ReactNode } from 'react'; +import { View, StyleSheet } from 'react-native'; +import { pTd } from 'utils/unit'; + +export type FormItemType = { + title: string; + children: ReactNode; +}; + +export default function FormItem(props: FormItemType) { + const { title, children } = props; + + return ( + <> + {title} + {children} + + ); +} + +const styles = StyleSheet.create({ + titleStyle: { + paddingLeft: pTd(8), + marginBottom: pTd(8), + color: defaultColors.font3, + }, + childrenWrap: { + width: '100%', + }, +}); diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx new file mode 100644 index 0000000000..60819ea6f1 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -0,0 +1,182 @@ +import PageContainer from 'components/PageContainer'; +import CommonInput from 'components/CommonInput'; +import { StyleSheet, View } from 'react-native'; +import gStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; +import React, { useCallback, useState } from 'react'; +import { TextM } from 'components/CommonText'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { ChainId } from '@portkey-wallet/types'; +import FormItem from 'components/FormItem'; +import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import SelectChain from 'components/SelectChain'; +import CommonButton from 'components/CommonButton'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { request } from '@portkey-wallet/api/api-did'; +import { useDebounceCallback } from '@portkey-wallet/hooks'; +import Loading from 'components/Loading'; +import navigationService from 'utils/navigationService'; + +interface CustomTokenProps { + route?: any; +} + +const CustomToken: React.FC = () => { + const { t } = useLanguage(); + + const originChainId = useOriginChainId(); + const { chainList = [], currentNetwork } = useCurrentWallet(); + + const [keyword, setKeyword] = useState(''); + const [tokenItem, setTokenItem] = useState<{ + symbol: string; + chainId: ChainId; + decimal: string; + id: string; + isDefault?: boolean; + isDisplay?: boolean; + }>({ + symbol: '', + chainId: originChainId, + decimal: '--', + id: '', + }); + const [btnDisable, setBtnDisable] = useState(true); + const [errorMessage, setErrorMessage] = useState(''); + + const fetchTokenItem = useCallback(async () => { + Loading.show(); + try { + const res = await request.token.fetchTokenItemBySearch({ + params: { + symbol: tokenItem.symbol, + chainId: tokenItem.chainId, + }, + }); + const { symbol, decimals, id } = res; + + if (symbol && decimals && id) { + setTokenItem(pre => ({ ...pre, ...res })); + setKeyword(symbol); + setBtnDisable(true); + } + } catch (error) { + setBtnDisable(true); + console.log('filter search error', error); + } finally { + Loading.hide(); + } + }, [tokenItem.chainId, tokenItem.symbol]); + + const fetchTokenItemDebounce = useDebounceCallback(fetchTokenItem, [tokenItem], 500); + + const onValueChange = useCallback( + (v: string) => { + setKeyword(v.trim()); + fetchTokenItemDebounce(); + }, + [fetchTokenItemDebounce], + ); + + const addToken = useCallback(async () => { + if (tokenItem?.isDefault || tokenItem?.isDisplay) { + setErrorMessage('This token has already been added.'); + } else { + try { + Loading.show(); + await request.token.displayUserToken({ + resourceUrl: `${tokenItem?.id}/display`, + params: { + isDisplay: true, + }, + }); + navigationService.goBack(); + } catch (error: any) { + console.log('add custom token error', error); + } finally { + Loading.hide(); + } + } + }, [tokenItem?.id, tokenItem?.isDefault, tokenItem?.isDisplay]); + + return ( + + + {t( + 'To add a token, you need to select the network that it belongs to and enter its symbol for automatic recognition.', + )} + + + setTokenItem(pre => ({ ...pre, chainId: _chainId }))} + /> + + + + + + {tokenItem.decimal} + + + + + {t('Add')} + + + + ); +}; + +export default CustomToken; + +export const pageStyles = StyleSheet.create({ + pageWrap: { + flex: 1, + backgroundColor: defaultColors.bg4, + ...gStyles.paddingArg(24, 20), + }, + tips: { + color: defaultColors.font3, + marginBottom: pTd(24), + }, + list: { + flex: 1, + }, + noResult: { + marginTop: pTd(40), + textAlign: 'center', + color: defaultColors.font7, + }, + btnContainer: { + position: 'absolute', + bottom: 0, + width: screenWidth, + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + tokenDecimal: { + lineHeight: pTd(56), + backgroundColor: defaultColors.bg7, + opacity: 0.3, + overflow: 'hidden', + borderRadius: pTd(6), + paddingLeft: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx new file mode 100644 index 0000000000..1a76e458e9 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx @@ -0,0 +1,109 @@ +import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; +import { StyleSheet } from 'react-native'; +import gStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; +import React, { useCallback } from 'react'; +import { FlatList } from 'react-native'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import TokenItem from '../TokenItem'; +import { TextL, TextM } from 'components/CommonText'; +import CommonButton from 'components/CommonButton'; +import navigationService from 'utils/navigationService'; +import Svg from 'components/Svg'; + +enum TipsEnum { + NO_RESULT = 'There is no search result.', + TRY = "Can't find your token? Please try below.", +} + +interface IFilterTokenSectionProps { + tokenList: any[]; + onHandleTokenItem: (item: any, added: boolean) => void; +} + +const FilterTokenSection: React.FC = (props: IFilterTokenSectionProps) => { + const { tokenList, onHandleTokenItem } = props; + + const { t } = useLanguage(); + + const { currentNetwork } = useWallet(); + + const CustomTokenTips = useCallback( + (v: TipsEnum) => ( + <> + {v} + navigationService.navigate('CustomToken')}> + + {t('Custom Token')} + + + ), + [t], + ); + + return ( + CustomTokenTips(TipsEnum.NO_RESULT)} + ListFooterComponent={() => (tokenList?.length > 0 ? CustomTokenTips(TipsEnum.TRY) : null)} + renderItem={({ item }: { item: TokenItemShowType }) => ( + onHandleTokenItem(item, !item?.isAdded)} + /> + )} + keyExtractor={(item: TokenItemShowType) => item?.id || item?.symbol} + /> + ); +}; + +export default FilterTokenSection; + +export const pageStyles = StyleSheet.create({ + pageWrap: { + flex: 1, + ...gStyles.paddingArg(0), + }, + inputWrap: { + backgroundColor: defaultColors.bg5, + ...gStyles.paddingArg(0, 16, 16), + }, + list: { + flex: 1, + }, +}); + +export const customTokenTipsStyle = StyleSheet.create({ + tips: { + color: defaultColors.font7, + marginTop: pTd(96), + textAlign: 'center', + }, + addButtonWrap: { + marginTop: pTd(24), + width: '100%', + height: pTd(44), + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + }, + addButton: { + borderRadius: pTd(6), + height: pTd(44), + }, + addText: { + marginLeft: pTd(8), + color: defaultColors.font2, + }, + try: { + marginTop: pTd(32), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx new file mode 100644 index 0000000000..bad7bb5339 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx @@ -0,0 +1,60 @@ +import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; +import { StyleSheet } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import React from 'react'; +import { FlatList } from 'react-native'; +import { TextL } from 'components/CommonText'; +import { pTd } from 'utils/unit'; +import { useLanguage } from 'i18n/hooks'; +import { useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import TokenItem from '../TokenItem'; +import fonts from 'assets/theme/fonts'; + +interface IPopularTokenSectionProps { + tokenDataShowInMarket: any[]; + onHandleTokenItem: (item: any, added: boolean) => void; +} + +const PopularTokenSection: React.FC = (props: IPopularTokenSectionProps) => { + const { tokenDataShowInMarket, onHandleTokenItem } = props; + + const { t } = useLanguage(); + + const { currentNetwork } = useWallet(); + + console.log('tokenDataShowInMarket', tokenDataShowInMarket); + + return ( + {t('Popular Assets')}} + data={tokenDataShowInMarket || []} + renderItem={({ item }: { item: TokenItemShowType }) => ( + onHandleTokenItem(item, !item?.isAdded)} + /> + )} + keyExtractor={(item: TokenItemShowType) => item?.id || item?.symbol} + /> + ); +}; + +export default PopularTokenSection; + +export const pageStyles = StyleSheet.create({ + list: { + flex: 1, + }, + header: { + ...fonts.mediumFont, + paddingLeft: pTd(16), + paddingTop: pTd(16), + }, + noResult: { + marginTop: pTd(40), + textAlign: 'center', + color: defaultColors.font7, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx new file mode 100644 index 0000000000..2d2edb32ae --- /dev/null +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx @@ -0,0 +1,91 @@ +import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; +import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import React from 'react'; +import { TextL, TextS } from 'components/CommonText'; +import { pTd } from 'utils/unit'; +import Svg from 'components/Svg'; +import CommonSwitch from 'components/CommonSwitch'; +import CommonAvatar from 'components/CommonAvatar'; +import { formatChainInfoToShow } from '@portkey-wallet/utils'; +import { FontStyles } from 'assets/theme/styles'; +import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; +import { NetworkType } from '@portkey-wallet/types'; + +type TokenItemProps = { + networkType: NetworkType; + item: TokenItemShowType; + onHandleToken: (item: TokenItemShowType, type: 'add' | 'delete') => void; +}; + +const TokenItem = ({ networkType, item, onHandleToken }: TokenItemProps) => { + const symbolImages = useSymbolImages(); + return ( + + + + + + + {item.symbol} + + + {`${formatChainInfoToShow(item.chainId, networkType)}`} + + + + {item.isDefault ? ( + + ) : ( + { + onHandleToken(item, item.isAdded ? 'delete' : 'add'); + }}> + + + + + )} + + + ); +}; + +export default TokenItem; + +const itemStyle = StyleSheet.create({ + wrap: { + height: pTd(72), + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + left: { + marginLeft: pTd(16), + }, + right: { + height: pTd(72), + marginLeft: pTd(16), + paddingRight: pTd(16), + flex: 1, + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + borderBottomColor: defaultColors.border6, + borderBottomWidth: StyleSheet.hairlineWidth, + }, + addedStyle: { + marginRight: pTd(14), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index b457447aaf..b8c7f4bd86 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -1,91 +1,34 @@ import PageContainer from 'components/PageContainer'; -import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; import CommonInput from 'components/CommonInput'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useEffect, useState } from 'react'; -import { FlatList } from 'react-native'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import CommonToast from 'components/CommonToast'; -import { TextL, TextS } from 'components/CommonText'; -import { pTd } from 'utils/unit'; -import Svg from 'components/Svg'; -import CommonSwitch from 'components/CommonSwitch'; -import CommonAvatar from 'components/CommonAvatar'; import { useLanguage } from 'i18n/hooks'; -import NoData from 'components/NoData'; import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import useDebounce from 'hooks/useDebounce'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { request } from '@portkey-wallet/api/api-did'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; -import { useCaAddresses, useCaAddressInfoList, useChainIdList, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { useCaAddresses, useCaAddressInfoList, useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { fetchTokenListAsync } from '@portkey-wallet/store/store-ca/assets/slice'; import Loading from 'components/Loading'; -import { formatChainInfoToShow } from '@portkey-wallet/utils'; -import { FontStyles } from 'assets/theme/styles'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; -import { NetworkType } from '@portkey-wallet/types'; +import FilterTokenSection from './components/FilterToken'; +import PopularTokenSection from './components/PopularToken'; +import { pTd } from 'utils/unit'; +import navigationService from 'utils/navigationService'; +import Svg from 'components/Svg'; interface ManageTokenListProps { route?: any; } - -type ItemProps = { - networkType: NetworkType; - item: TokenItemShowType; - onHandleToken: (item: TokenItemShowType, type: 'add' | 'delete') => void; -}; - -const Item = ({ networkType, item, onHandleToken }: ItemProps) => { - const symbolImages = useSymbolImages(); - return ( - - - - - - - {item.symbol} - - - {`${formatChainInfoToShow(item.chainId, networkType)}`} - - - - {item.isDefault ? ( - - ) : ( - { - onHandleToken(item, item.isAdded ? 'delete' : 'add'); - }}> - - - - - )} - - - ); -}; const ManageTokenList: React.FC = () => { const { t } = useLanguage(); + const timerRef = useRef(null); - const currentNetworkInfo = useCurrentNetworkInfo(); - const { currentNetwork } = useWallet(); - - const chainList = useChainIdList(); + const chainIdList = useChainIdList(); const dispatch = useAppCommonDispatch(); const caAddressArray = useCaAddresses(); @@ -94,22 +37,16 @@ const ManageTokenList: React.FC = () => { const { tokenDataShowInMarket } = useAppCASelector(state => state.tokenManagement); const [keyword, setKeyword] = useState(''); + const [filterTokenList, setFilterTokenList] = useState([]); const debounceWord = useDebounce(keyword, 500); - useEffect(() => { - if (tokenDataShowInMarket.length) return; - dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainList })); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const onHandleTokenItem = useCallback( async (item: TokenItemShowType, isAdded: boolean) => { // TODO Loading.show(); await request.token .displayUserToken({ - baseURL: currentNetworkInfo.apiUrl, resourceUrl: `${item.userTokenId}/display`, params: { isDisplay: isAdded, @@ -117,8 +54,8 @@ const ManageTokenList: React.FC = () => { }) .then(res => { console.log(res); - setTimeout(() => { - dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainList })); + timerRef.current = setTimeout(() => { + dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); Loading.hide(); @@ -130,18 +67,60 @@ const ManageTokenList: React.FC = () => { CommonToast.fail('Fail'); }); }, - [caAddressArray, caAddressInfos, chainList, currentNetworkInfo.apiUrl, debounceWord, dispatch], + [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch], ); + const fetchSearchedTokenList = useCallback(async () => { + try { + if (!debounceWord) return; + const res = await request.token.fetchTokenListBySearch({ + params: { + symbol: debounceWord, + chainIds: chainIdList, + }, + }); + console.log('res', res); + setFilterTokenList(res?.data || []); + } catch (error) { + console.log('filter search error', error); + } + }, [chainIdList, debounceWord]); + + useEffect(() => { + if (tokenDataShowInMarket.length) return; + dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { - dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainList })); - }, [chainList, debounceWord, dispatch]); + if (debounceWord) { + // get filter token + fetchSearchedTokenList(); + } else { + dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); + } + }, [chainIdList, debounceWord, dispatch, fetchSearchedTokenList]); + + useEffect( + () => () => { + if (timerRef.current) clearInterval(timerRef.current); + }, + [], + ); return ( } + rightDom={ + { + navigationService.navigate('CustomToken'); + }}> + + + } containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> @@ -153,20 +132,12 @@ const ManageTokenList: React.FC = () => { }} /> - {!!keyword && !tokenDataShowInMarket.length && } - ( - onHandleTokenItem(item, !item?.isAdded)} - /> - )} - keyExtractor={(item: TokenItemShowType) => item?.id || item?.symbol} - /> - {/* {isLoading && } */} + + {debounceWord ? ( + + ) : ( + + )} ); }; @@ -185,57 +156,4 @@ export const pageStyles = StyleSheet.create({ list: { flex: 1, }, - noResult: { - marginTop: pTd(40), - textAlign: 'center', - color: defaultColors.font7, - }, -}); - -const itemStyle = StyleSheet.create({ - wrap: { - height: pTd(72), - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - left: { - marginLeft: pTd(16), - }, - right: { - height: pTd(72), - marginLeft: pTd(16), - paddingRight: pTd(16), - flex: 1, - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - borderBottomColor: defaultColors.border6, - borderBottomWidth: StyleSheet.hairlineWidth, - }, - addedStyle: { - marginRight: pTd(14), - }, - tokenName: { - flex: 1, - }, - balanceWrap: { - flex: 1, - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'flex-end', - }, - token: { - color: defaultColors.font5, - lineHeight: pTd(22), - overflow: 'hidden', - }, - dollar: { - marginTop: pTd(2), - lineHeight: pTd(16), - color: defaultColors.font7, - }, }); From 076ae3affd865a1d56ffff369516c4552f299a0e Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 5 Jul 2023 10:57:56 +0800 Subject: [PATCH 252/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20QRCode?= =?UTF-8?q?=20time=20to=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/misc.ts | 11 ++++++----- .../mobile-app-did/js/pages/Login/ScanLogin/index.tsx | 6 +++--- .../js/pages/Login/components/QRCode.tsx | 4 ++-- packages/types/types-ca/qrcode.ts | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/hooks/hooks-ca/misc.ts b/packages/hooks/hooks-ca/misc.ts index 69c7e3b8ba..8916bef339 100644 --- a/packages/hooks/hooks-ca/misc.ts +++ b/packages/hooks/hooks-ca/misc.ts @@ -7,6 +7,7 @@ import { DefaultCountry, getCountryCodeIndex } from '@portkey-wallet/constants/c import { CountryItem } from '@portkey-wallet/types/types-ca/country'; import signalrDid from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; +import { checkQRCodeExist } from '@portkey-wallet/api/api-did/message/utils'; export const useMisc = () => useAppCASelector(state => state.misc); @@ -95,6 +96,11 @@ export const useIsScanQRCode = (clientId: string | undefined) => { const registerSignalr = useCallback(async (clientId: string) => { try { + const { remove } = signalrDid.onScanLogin(() => { + setIsScanQRCode(true); + cleanSignalrRef.current(); + }); + signalrDidRemoveRef.current = remove; await signalrDid.doOpen({ url: `${request.defaultConfig.baseURL}/ca`, clientId, @@ -102,11 +108,6 @@ export const useIsScanQRCode = (clientId: string | undefined) => { if (!isActiveRef.current) { throw new Error('isActiveRef.current is false'); } - const { remove } = signalrDid.onScanLogin(() => { - setIsScanQRCode(true); - cleanSignalrRef.current(); - }); - signalrDidRemoveRef.current = remove; } catch (error) { console.log('registerSignalr: error', error); } diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index 95ebfb5d16..259308791e 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -26,13 +26,13 @@ const ScrollViewProps = { disabled: true }; export default function ScanLogin() { const { data } = useRouterParams<{ data?: LoginQRData }>(); - const { address: managerAddress, extraData: qrExtraData, deviceType, time } = data || {}; + const { address: managerAddress, extraData: qrExtraData, deviceType, id } = data || {}; const { caHash, address } = useCurrentWalletInfo(); const [loading, setLoading] = useState(); const getCurrentCAContract = useGetCurrentCAContract(); - const targetClientId = useMemo(() => (time ? `${managerAddress}_${time}` : undefined), [managerAddress, time]); + const targetClientId = useMemo(() => (id ? `${managerAddress}_${id}` : undefined), [managerAddress, id]); useEffectOnce(() => { if (!targetClientId) return; @@ -53,7 +53,7 @@ export default function ScanLogin() { setLoading(true); if (targetClientId) { const isQRCodeExist = await checkQRCodeExist(targetClientId); - if (isQRCodeExist) { + if (!isQRCodeExist) { CommonToast.warn('The QR code has already been scanned by another device.'); setLoading(false); return; diff --git a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx index 3651f0c150..e18788a85f 100644 --- a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx @@ -115,7 +115,7 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi type: 'login', address: newWallet.address, netWorkType: currentNetwork, - time: Math.floor(Date.now() / 1000), + id: Math.floor(Date.now() / 1000), extraData: { deviceInfo: getDeviceInfo(), version: DEVICE_INFO_VERSION, @@ -125,7 +125,7 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi [currentNetwork, getDeviceInfo, newWallet], ); const qrDataStr = useMemo(() => JSON.stringify(qrData), [qrData]); - const clientId = useMemo(() => (qrData.time ? `${qrData.address}_${qrData.time}` : undefined), [qrData]); + const clientId = useMemo(() => (qrData.id ? `${qrData.address}_${qrData.id}` : undefined), [qrData]); const isScanQRCode = useIsScanQRCode(clientId); return ( diff --git a/packages/types/types-ca/qrcode.ts b/packages/types/types-ca/qrcode.ts index 1c05af07b1..5385db347b 100644 --- a/packages/types/types-ca/qrcode.ts +++ b/packages/types/types-ca/qrcode.ts @@ -13,7 +13,7 @@ export interface LoginQRData extends QRData { type: 'login'; extraData?: QRExtraDataType; deviceType?: DeviceType; // 0.0.1 - time?: number; + id?: number; } export interface SendTokenQRDataType extends QRData { From 68f6d6901ba4bdd0f229da8bc922a2904bf751b5 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 11:03:33 +0800 Subject: [PATCH 253/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20checkManagerSync?= =?UTF-8?q?State?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/send.ts | 1 + .../BrowserTab/components/HttpModal/index.tsx | 9 +-- .../components/TransactionOverlay/index.tsx | 59 +++++++++++++++---- .../js/pages/My/WalletSecurity/Dapp/index.tsx | 3 - .../js/pages/Send/SendHome/index.tsx | 20 ++++++- .../js/pages/Send/SendHome/style.ts | 3 + 6 files changed, 76 insertions(+), 19 deletions(-) diff --git a/packages/constants/constants-ca/send.ts b/packages/constants/constants-ca/send.ts index cad95f14bd..f335d74bb5 100644 --- a/packages/constants/constants-ca/send.ts +++ b/packages/constants/constants-ca/send.ts @@ -8,6 +8,7 @@ export enum TransactionError { NFT_NOT_ENOUGH = 'Insufficient quantity', FEE_NOT_ENOUGH = 'Insufficient funds for transaction fee', CROSS_NOT_ENOUGH = 'Insufficient funds for cross chain transaction fee', + SYNCHRONIZING = 'Synchronizing on-chain account information...', } export const AddressErrorArray = Object.values(AddressError); diff --git a/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx index b4d6701fba..dc2bea65b3 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx @@ -34,15 +34,16 @@ export default function HttpModal(props: HttpModalPropsType) { return ( - You are accessing an insecure http connection. Please be mindful of protecting your personal information and - account security. + {t( + 'You are accessing an insecure site. Please be cautious about safeguarding your personal information and account security.', + )} - {t('Disable notification')} + {t('Disable notifications')} setIsShowHttpModal(false)}> - {t('Get it')} + {t('Continue')} diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 294a6a02e5..f3f468f4eb 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -26,6 +26,13 @@ import TransactionDataSection from '../TransactionDataSection'; import { ELF_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; import { styles, transferGroupStyle } from './styles/index'; import Lottie from 'lottie-react-native'; +import { useCheckManagerSyncState } from 'hooks/wallet'; +import { request } from '@portkey-wallet/api/api-did'; + +enum ErrorText { + ESTIMATE_ERROR = 'Failed to estimate transaction fee', + SYNCHRONIZING = 'Synchronizing on-chain account information...', +} type TransactionModalPropsType = { dappInfo: DappStoreItem; @@ -41,18 +48,19 @@ const ConnectModal = (props: TransactionModalPropsType) => { const { walletName } = useWallet(); const wallet = useCurrentWalletInfo(); + const checkManagerSyncState = useCheckManagerSyncState(); const amountInUsdShow = useAmountInUsdShow(); const chainInfo = useCurrentChain(transactionInfo.chainId); const [, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); - const [decimal, setDecimal] = useState('0'); + const [tokenDecimal, setTokenDecimal] = useState('0'); const [isFetchingDecimal, setIsFetchingDecimal] = useState(false); const [fee, setFee] = useState(''); const [isFetchingFee, setIsFetchingFee] = useState(true); - const [noEnoughFee, setNoEnoughFee] = useState(false); + const [errorText, setErrorText] = useState(''); const isCAContract = useMemo( () => chainInfo?.caContractAddress === transactionInfo?.contractAddress, @@ -98,7 +106,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { const transferContent = useMemo(() => { const { symbol, amount } = transactionInfo?.params?.paramsOption || {}; - const decimals = symbol === 'ELF' ? 8 : 0; + const decimals = symbol === 'ELF' ? 8 : tokenDecimal; return ( <> @@ -276,18 +284,19 @@ const ConnectModal = (props: TransactionModalPropsType) => { )} - {noEnoughFee && Failed to estimate transaction fee} + {!!errorText && {errorText}} ); }, [ + errorText, fee, formatAmountInUsdShow, isFetchingDecimal, isFetchingFee, isMainnet, isTransfer, - noEnoughFee, t, + tokenDecimal, transactionInfo.chainId, transactionInfo?.params?.paramsOption, wallet, @@ -326,14 +335,14 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, }); - if (!TransactionFee && !TransactionFee?.ELF) setNoEnoughFee(true); + if (!TransactionFee && !TransactionFee?.ELF) setErrorText(ErrorText.ESTIMATE_ERROR); setFee(TransactionFee?.ELF || '0'); + setIsFetchingFee(false); } catch (e) { setFee('0'); - setNoEnoughFee(true); - console.log('get fee error', e); + setErrorText(ErrorText.ESTIMATE_ERROR); setIsFetchingFee(false); } }, [ @@ -346,9 +355,39 @@ const ConnectModal = (props: TransactionModalPropsType) => { wallet.caHash, ]); + // get decimals + const getDecimals = useCallback(async () => { + setIsFetchingDecimal(true); + try { + const res = await request.token.fetchTokenItemBySearch({ + params: { + symbol: transactionInfo?.params?.paramsOption?.symbol, + chainId: transactionInfo.chainId, + }, + }); + const { symbol, decimals } = res; + + if (symbol && decimals) { + setTokenDecimal(decimals); + } + } catch (error) { + console.log('filter search error', error); + } finally { + setIsFetchingDecimal(false); + } + }, [transactionInfo.chainId, transactionInfo?.params?.paramsOption?.symbol]); + useEffect(() => { - getFee(); - }, [getFee]); + (async () => { + // checkIsManagerSynced + const _isManagerSynced = await checkManagerSyncState(transactionInfo.chainId); + + if (!_isManagerSynced) return setErrorText('Synchronizing on-chain account information...'); + + getFee(); + getDecimals(); + })(); + }, [checkManagerSyncState, getDecimals, getFee, transactionInfo.chainId]); useEffect(() => { const symbol = transactionInfo?.params?.paramsOption?.symbol; diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx index 9061fd6c8d..b2fa1ffee0 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -47,9 +47,6 @@ const DeviceList: React.FC = () => { - - {item?.name || getHost(item.origin)} - {item?.origin || getHost(item.origin)} diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index f27b24c2c1..8232f3c3f5 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -44,6 +44,7 @@ import { } from '@portkey-wallet/constants/constants-ca/send'; import { getAddressChainId, isSameAddresses } from '@portkey-wallet/utils'; import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; +import { useCheckManagerSyncState } from 'hooks/wallet'; const SendHome: React.FC = () => { const { t } = useLanguage(); @@ -73,6 +74,8 @@ const SendHome: React.FC = () => { const [isLoading] = useState(false); const [errorMessage, setErrorMessage] = useState([]); + const checkManagerSyncState = useCheckManagerSyncState(); + useEffect(() => { setSelectedToContact(toInfo); }, [toInfo]); @@ -84,6 +87,11 @@ const SendHome: React.FC = () => { const account = getManagerAccount(pin); if (!account) return; + const _isManagerSynced = await checkManagerSyncState(chainInfo.chainId); + if (!_isManagerSynced) { + return setErrorMessage([TransactionError.SYNCHRONIZING]); + } + const contract = await getContractBasic({ contractAddress: chainInfo.caContractAddress, rpcUrl: chainInfo?.endPoint, @@ -124,6 +132,7 @@ const SendHome: React.FC = () => { }, [ chainInfo, + checkManagerSyncState, debounceSendNumber, pin, selectedAssets.decimals, @@ -265,8 +274,9 @@ const SendHome: React.FC = () => { const previewDisable = useMemo(() => { if (!selectedToContact?.address) return true; if (sendNumber === '0' || !sendNumber) return true; + if (errorMessage?.length > 0) return true; return false; - }, [selectedToContact?.address, sendNumber]); + }, [selectedToContact?.address, sendNumber, errorMessage]); const checkCanNext = useCallback(() => { const suffix = getAddressChainId(selectedToContact.address, chainInfo?.chainId || 'AELF'); @@ -494,7 +504,13 @@ const SendHome: React.FC = () => { )} {TransactionErrorArray.filter(ele => errorMessage.includes(ele)).map(err => ( - + {t(err)} ))} diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/style.ts b/packages/mobile-app-did/js/pages/Send/SendHome/style.ts index af30076fe4..f6999d379c 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/style.ts +++ b/packages/mobile-app-did/js/pages/Send/SendHome/style.ts @@ -38,6 +38,9 @@ export const styles = StyleSheet.create({ marginLeft: pTd(26), paddingLeft: pTd(8), }, + warnMessage: { + color: defaultColors.font6, + }, nftErrorMessage: { marginLeft: 0, paddingLeft: 0, From 9564b417f186580499d87bba27e2b9fb0af0b1ca Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 11:04:00 +0800 Subject: [PATCH 254/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20custom=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/components/CustomHeader/index.tsx | 4 +++- .../js/components/CustomHeader/style/index.style.ts | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/components/CustomHeader/index.tsx b/packages/mobile-app-did/js/components/CustomHeader/index.tsx index 55fd8a70d6..52f270a777 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/index.tsx +++ b/packages/mobile-app-did/js/components/CustomHeader/index.tsx @@ -15,6 +15,7 @@ import { useHardwareBackPress } from '@portkey-wallet/hooks/mobile'; export type CustomHeaderProps = { themeType?: SafeAreaColorMapKeyUnit; noLeftDom?: boolean; + noCenterDom?: boolean; leftDom?: ReactNode; titleDom?: ReactNode | string; rightDom?: ReactNode; @@ -32,6 +33,7 @@ const CustomHeader: React.FC = props => { const { noLeftDom = false, + noCenterDom = false, leftDom = null, titleDom = 'title', rightDom = null, @@ -132,7 +134,7 @@ const CustomHeader: React.FC = props => { return ( {letElement} - {centerElement} + {!noCenterDom && {centerElement}} {rightElement} ); diff --git a/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts b/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts index 4be6f3dc7b..db3d7c7046 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts +++ b/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts @@ -20,7 +20,7 @@ export const blueStyles = StyleSheet.create({ display: 'flex', justifyContent: 'center', alignItems: 'flex-start', - width: pTd(80), + flex: 1, }, centerWrap: { flex: 1, @@ -33,7 +33,7 @@ export const blueStyles = StyleSheet.create({ fontWeight: 'bold', }, rightDomWrap: { - width: pTd(80), + flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'flex-end', @@ -58,7 +58,7 @@ export const whitStyles = StyleSheet.create({ display: 'flex', justifyContent: 'center', alignItems: 'flex-start', - width: pTd(80), + flex: 1, }, centerWrap: { flex: 1, @@ -71,7 +71,7 @@ export const whitStyles = StyleSheet.create({ fontWeight: 'bold', }, rightDomWrap: { - width: pTd(80), + flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'flex-end', From bfa5213574cbf857e8b6f75da37800624429e0a5 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 11:04:37 +0800 Subject: [PATCH 255/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/login.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/hooks/login.ts b/packages/mobile-app-did/js/hooks/login.ts index ea883a5aa9..44833fc5a8 100644 --- a/packages/mobile-app-did/js/hooks/login.ts +++ b/packages/mobile-app-did/js/hooks/login.ts @@ -105,7 +105,7 @@ export function useOnManagerAddressAndQueryResult() { verifierInfo?: VerifierInfo; guardiansApproved?: GuardiansApproved; }) => { - showLoading && Loading.show({ text: t('Working on recovery...') }); + showLoading && Loading.show({ text: t('Initiating social recovery...') }); await sleep(500); const isRecovery = managerInfo.verificationType === VerificationType.communityRecovery; try { From c4a77352e0128a68c65afe99f8b534cf1574b388 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 11:05:00 +0800 Subject: [PATCH 256/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20http?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TabsDrawer/TabsDrawerContent.tsx | 31 +++++++++++++++++-- .../components/TextWithProtocolIcon/index.tsx | 14 ++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 3a86a30e7b..d1356c1480 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -7,7 +7,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { StyleSheet, ScrollView, View } from 'react-native'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca/index'; import { pTd } from 'utils/unit'; -import Svg from 'components/Svg'; import { defaultColors } from 'assets/theme'; import { useLanguage } from 'i18n/hooks'; import { FontStyles } from 'assets/theme/styles'; @@ -29,6 +28,8 @@ import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { BrowserContext, IBrowserTab } from './context'; import { useHardwareBackPress } from '@portkey-wallet/hooks/mobile'; +import Svg from 'components/Svg'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); @@ -102,6 +103,11 @@ const TabsDrawerContent: React.FC = () => { }), [], ); + + const activeItem = useMemo(() => { + return tabs?.find(ele => ele.id === activeTabId); + }, [activeTabId, tabs]); + useHardwareBackPress( useMemo(() => { if (isDrawerOpen) { @@ -112,13 +118,25 @@ const TabsDrawerContent: React.FC = () => { } }, [backToSearchPage, isDrawerOpen]), ); + return ( + + + + } rightDom={rightDom} - leftCallback={backToSearchPage} notHandleHardwareBackPress safeAreaColor={['blue', 'white']} containerStyles={styles.container} @@ -187,6 +205,15 @@ const styles = StyleSheet.create({ ...fonts.mediumFont, lineHeight: pTd(24), }, + leftWrap: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + paddingLeft: pTd(16), + }, + backIcon: { + marginRight: pTd(4), + }, cancelButton: { paddingLeft: pTd(12), lineHeight: pTd(36), diff --git a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx index 08f3ade79a..38ab564cd1 100644 --- a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx +++ b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx @@ -1,4 +1,4 @@ -import { isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; +import { getProtocolAndHost, isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; import { defaultColors } from 'assets/theme'; import { TextM } from 'components/CommonText'; @@ -28,7 +28,7 @@ export default function TextWithProtocolIcon({ }: ITextWithProtocolIconProps) { const isDanger = isDangerousLink(url); - const fontSizeObj: any = { + const textStyleObj: any = { fontSize: textFontSize, }; @@ -52,8 +52,11 @@ export default function TextWithProtocolIcon({ return ( {type === 'iconLeft' && ProtocolIcon} - - {title || url} + + {title || getProtocolAndHost(url)} {type === 'iconRight' && ProtocolIcon} @@ -76,4 +79,7 @@ const styles = StyleSheet.create({ marginRight: pTd(4), marginLeft: pTd(4), }, + headerTextColor: { + color: defaultColors.bg1, + }, }); From 46acb82b285c37c59db8384f2f9bd6aafe836c26 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 5 Jul 2023 11:32:11 +0800 Subject: [PATCH 257/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20preventScr?= =?UTF-8?q?eenCapture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Login/components/QRCode.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx index e18788a85f..c8db89caba 100644 --- a/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/QRCode.tsx @@ -27,6 +27,7 @@ import CommonQRCodeStyled from 'components/CommonQRCodeStyled'; import { useCheckManager } from 'hooks/useLogOut'; import Lottie from 'lottie-react-native'; import { useIsScanQRCode } from '@portkey-wallet/hooks/hooks-ca/misc'; +import { usePreventScreenCapture } from 'expo-screen-capture'; // When wallet does not exist, DEFAULT_WALLET is populated as the default data const DEFAULT_WALLET: LoginQRData = { @@ -51,6 +52,7 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi const checkManager = useCheckManager(); const caWalletInfo = useIntervalQueryCAInfoByAddress(currentNetwork, newWallet?.address, checkManager); const isFocused = useIsFocused(); + usePreventScreenCapture('LoginQRCode'); useEffect(() => { if (!isFocused) return; From 50c664910355b2b3416356ea831ad05e25cc641e Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Wed, 5 Jul 2023 11:38:54 +0800 Subject: [PATCH 258/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20perf=20scan=20lo?= =?UTF-8?q?gin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/types-ca/qrcode.ts | 1 + .../app/web/hooks/usePortkeyUIConfig.ts | 71 +++++ .../components/ScanCard/index.less | 37 ++- .../components/ScanCard/index.tsx | 42 ++- .../app/web/store/Provider/Updater.tsx | 4 + .../app/web/store/Provider/index.tsx | 24 +- packages/web-extension-did/package.json | 1 + yarn.lock | 246 +++++++++++++++++- 8 files changed, 391 insertions(+), 35 deletions(-) create mode 100644 packages/web-extension-did/app/web/hooks/usePortkeyUIConfig.ts diff --git a/packages/types/types-ca/qrcode.ts b/packages/types/types-ca/qrcode.ts index b98cb1a63a..5433feb245 100644 --- a/packages/types/types-ca/qrcode.ts +++ b/packages/types/types-ca/qrcode.ts @@ -13,6 +13,7 @@ export interface LoginQRData extends QRData { type: 'login'; extraData?: QRExtraDataType; deviceType?: DeviceType; // 0.0.1 + id: string; } export interface SendTokenQRDataType extends QRData { diff --git a/packages/web-extension-did/app/web/hooks/usePortkeyUIConfig.ts b/packages/web-extension-did/app/web/hooks/usePortkeyUIConfig.ts new file mode 100644 index 0000000000..0f96aff546 --- /dev/null +++ b/packages/web-extension-did/app/web/hooks/usePortkeyUIConfig.ts @@ -0,0 +1,71 @@ +import { useCurrentNetworkInfo, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; +import { ConfigProvider, ReCaptchaResponseType } from '@portkey/did-ui-react'; +import { localStorage } from 'redux-persist-webextension-storage'; +import { useCallback, useMemo } from 'react'; +import { useLoading } from 'store/Provider/hooks'; +import { reCAPTCHAAction, socialLoginAction } from 'utils/lib/serviceWorkerAction'; +import { ISocialLogin } from '@portkey-wallet/types/types-ca/wallet'; +import { sleep } from '@portkey-wallet/utils'; + +const usePortkeyUIConfig = () => { + const currentNetwork = useCurrentNetworkInfo(); + const networkList = useNetworkList(); + const { setLoading } = useLoading(); + const customReCaptchaHandler: () => Promise<{ + type: ReCaptchaResponseType; + message?: any; + }> = useCallback(async () => { + const reCaptcha = await reCAPTCHAAction(); + if (reCaptcha.error) return { type: 'error', message: reCaptcha.message }; + return { type: 'success', message: reCaptcha.response }; + }, []); + + const socialLoginHandler = useCallback( + async (v: ISocialLogin) => { + await sleep(10); + setLoading(true); + const result: any = await socialLoginAction(v, currentNetwork.networkType); + + if (result.error) { + setLoading(false); + return { error: Error(result.message) }; + } + return { + data: { ...result.data, accessToken: result.data.access_token }, + error: result.error, + }; + }, + + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentNetwork.networkType], + ); + useMemo(() => { + ConfigProvider.setGlobalConfig({ + storageMethod: localStorage, + graphQLUrl: currentNetwork.graphqlUrl, + socketUrl: `${currentNetwork.apiUrl}/ca`, + socialLogin: { + Google: { + clientId: '', + customLoginHandler: () => socialLoginHandler('Google'), + }, + Apple: { + clientId: '', + customLoginHandler: () => socialLoginHandler('Apple'), + }, + }, + requestDefaults: { + baseURL: currentNetwork.apiUrl, + }, + network: { + defaultNetwork: currentNetwork.networkType, + networkList: networkList, + }, + reCaptchaConfig: { + customReCaptchaHandler, + }, + }); + }, [currentNetwork, customReCaptchaHandler, networkList, socialLoginHandler]); +}; + +export default usePortkeyUIConfig; diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.less b/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.less index c06f1f42d2..131ba9c198 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.less +++ b/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.less @@ -1,16 +1,29 @@ @import '../../../../assets/theme/color.less'; -.register-start-card.scan-card-wrapper { - text-align: center; - .title { - padding-top: 80px; - padding-bottom: 8px; - & + p { - line-height: 20px; - color: @font-13; - font-size: 14px; - margin-bottom: 48px; - z-index: 10; - position: relative; +@import '../../../../assets/theme/constants.less'; + +.scan-card { + width: 406px; + height: 474px; + padding: 0 20px 24px; + border-radius: 6px; + background-color: @bg-11; + box-shadow: @box-shadow5; + position: relative; + display: flex; + flex-direction: column; + .scan-card-inner { + flex: 1; + .scan-title { + padding-top: 80px; + padding-bottom: 8px; + .back-icon-wrapper { + right: -16px; + } + } + .pc-icon { + width: 80px; + height: 80px; + border-radius: 8px; } } } diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx index 376ef5e519..b74a788fcb 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx @@ -1,5 +1,4 @@ import { WalletInfoType } from '@portkey-wallet/types/wallet'; -import CustomSvg from 'components/CustomSvg'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router'; import AElf from 'aelf-sdk'; @@ -12,11 +11,14 @@ import { setWalletInfoAction } from 'store/reducers/loginCache/actions'; import { getDeviceInfo } from 'utils/device'; import { DEVICE_TYPE } from 'constants/index'; import { DEVICE_INFO_VERSION } from '@portkey-wallet/constants/constants-ca/device'; -import QRCodeCommon from 'pages/components/QRCodeCommon'; +import { ScanBase, CustomSvg } from '@portkey/did-ui-react'; import { setCAInfoType, setOriginChainId } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useCheckManager } from 'hooks/useLogout'; import { message } from 'antd'; import './index.less'; +import didSignalr from '@portkey-wallet/socket/socket-did'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { randomId } from '@portkey-wallet/utils'; export default function ScanCard() { const navigate = useNavigate(); @@ -27,6 +29,9 @@ export default function ScanCard() { const checkManager = useCheckManager(); const { passwordSeed: pin } = useUserInfo(); const caWallet = useIntervalQueryCAInfoByAddress(currentNetwork, newWallet?.address, checkManager); + const [isWaitingAuth, setIsWaitingAuth] = useState(); + const networkItem = useCurrentNetworkInfo(); + const generateKeystore = useCallback(() => { try { const wallet = walletInfo?.address ? walletInfo : AElf.wallet.createNewWallet(); @@ -51,6 +56,7 @@ export default function ScanCard() { type: 'login', address: newWallet.address, netWorkType: currentNetwork, + id: randomId(), chainType: 'aelf', extraData: { deviceInfo, @@ -60,6 +66,23 @@ export default function ScanCard() { return JSON.stringify(data); }, [currentNetwork, deviceInfo, newWallet]); + // Listen whether the user is authorized + useEffect(() => { + try { + const data: LoginQRData = JSON.parse(qrData); + if (!data?.id) return; + const clientId = `${data.address}_${data.id}`; + didSignalr.onScanLogin(() => { + setIsWaitingAuth(true); + }); + didSignalr.doOpen({ url: `${networkItem.apiUrl}/ca`, clientId }).catch((error) => { + console.warn('Socket:', error); + }); + } catch (error) { + console.warn('Socket:', error); + } + }, [networkItem.apiUrl, qrData]); + useEffect(() => { const { caInfo, originChainId } = caWallet || {}; if (caInfo && newWallet && originChainId) { @@ -84,13 +107,14 @@ export default function ScanCard() { }, [caWallet, dispatch, navigate, newWallet, pin]); return ( -
    -

    - Scan code to log in - navigate('/register/start')} /> -

    -

    Please use the portkey Dapp to scan the QR code

    -
    {qrData && }
    +
    + } + onBack={() => navigate('/register/start')} + qrData={qrData} + />
    ); } diff --git a/packages/web-extension-did/app/web/store/Provider/Updater.tsx b/packages/web-extension-did/app/web/store/Provider/Updater.tsx index 81404a4e3e..de7eb9305c 100644 --- a/packages/web-extension-did/app/web/store/Provider/Updater.tsx +++ b/packages/web-extension-did/app/web/store/Provider/Updater.tsx @@ -18,6 +18,7 @@ import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; import { useLocation } from 'react-router'; import { useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; +import usePortkeyUIConfig from 'hooks/usePortkeyUIConfig'; keepAliveOnPages({}); request.setExceptionManager(exceptionManager); @@ -46,6 +47,9 @@ export default function Updater() { useMemo(() => { request.set('baseURL', apiUrl); }, [apiUrl]); + + usePortkeyUIConfig(); + useCaInfoOnChain(); useActiveLockStatus(); useEffect(() => { diff --git a/packages/web-extension-did/app/web/store/Provider/index.tsx b/packages/web-extension-did/app/web/store/Provider/index.tsx index 42f0ac222d..836778a05c 100644 --- a/packages/web-extension-did/app/web/store/Provider/index.tsx +++ b/packages/web-extension-did/app/web/store/Provider/index.tsx @@ -13,6 +13,8 @@ import Updater from './Updater'; import * as Sentry from '@sentry/react'; import { Integrations } from '@sentry/tracing'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; +import { PortkeyConfigProvider } from '@portkey/did-ui-react'; +import '@portkey/did-ui-react/dist/assets/index.css'; let childrenNode: any = undefined; @@ -57,16 +59,18 @@ export default function ContextProviders({ return ( - - - - - - - {children} - - - + + + + + + + + {children} + + + + ); } diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 97dc060833..40a8de51a4 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -20,6 +20,7 @@ "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", "@portkey/providers": "1.0.1-alpha.0", + "@portkey/did-ui-react": "1.0.0-alpha.6", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index ecc68eb9d3..4e3c1c06f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3018,6 +3018,18 @@ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.6.tgz#d21ace437cc919cdd8f1640302fa8851e65e75c0" integrity sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg== +"@floating-ui/core@^1.3.1": + version "1.3.1" + resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz#4d795b649cc3b1cbb760d191c80dcb4353c9a366" + integrity sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g== + +"@floating-ui/dom@^1.0.6": + version "1.4.3" + resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.3.tgz#0854a3297ea03894932381f3ea1403fab3a6e602" + integrity sha512-nB/68NyaQlcdY22L+Fgd1HERQ7UGv7XFN+tPxwrEfQL4nKtAP/jIZnZtpUlXbtV+VEGHh6W/63Gy2C5biWI3sA== + dependencies: + "@floating-ui/core" "^1.3.1" + "@floating-ui/dom@^1.2.6": version "1.2.9" resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.9.tgz#b9ed1c15d30963419a6736f1b7feb350dd49c603" @@ -4701,6 +4713,13 @@ dependencies: nanoid "^3.3.3" +"@matt-block/react-recaptcha-v2@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@matt-block/react-recaptcha-v2/-/react-recaptcha-v2-2.0.1.tgz#4f3c6b540a5216e6154ca08d2243314a07ffc197" + integrity sha512-nQ1DjdjmfeG5dcKwqprfgBMdBO1MYlFcB4LtfMDsw8kmuxVuRsiVlAHsmARirmGutJ9zKQpvcYZqy2HbIoAH5w== + dependencies: + nanoid "^3.3.4" + "@metamask/detect-provider@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@metamask/detect-provider/-/detect-provider-1.2.0.tgz#3667a7531f2a682e3c3a43eaf3a1958bdb42a696" @@ -5184,6 +5203,16 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@portkey/accounts@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/accounts/-/accounts-1.0.0-alpha.6.tgz#eddde767b6140fb27dc214e0eb79d9020c189fdd" + integrity sha512-+Zc8a0umPfrJYL7NvDTFzJ3xoUkgODqsduXfn8IF5qWRGd8dky22kGYug9oOK0S9fPBe3Phr116ra8iV98Kx1g== + dependencies: + "@portkey/types" "^1.0.0-alpha.6" + "@portkey/utils" "^1.0.0-alpha.6" + "@portkey/validator" "^1.0.0-alpha.6" + aelf-sdk "^3.2.44" + "@portkey/chain@^1.0.1-alpha.0": version "1.0.1-alpha.0" resolved "https://registry.npmjs.org/@portkey/chain/-/chain-1.0.1-alpha.0.tgz#fa3140d3159d3d465249b4fa7f8e9584fd533dea" @@ -5203,6 +5232,53 @@ "@portkey/utils" "^1.0.0-alpha.5" aelf-sdk "^3.2.44" +"@portkey/contracts@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/contracts/-/contracts-1.0.0-alpha.6.tgz#79e763b988a1492cfffd019d0b39e55f09b4c947" + integrity sha512-X7sRX6vCqQL9e7ZmAOz3efzLPOp9foJZRkncPEZnAXayQNSK5+DSIKCC9lUSXqDON3pRmnVayg0KMOR0dSN9jA== + dependencies: + "@portkey/provider-types" "^1.0.1-alpha.0" + "@portkey/types" "^1.0.0-alpha.6" + "@portkey/utils" "^1.0.0-alpha.6" + aelf-sdk "^3.2.44" + +"@portkey/did-ui-react@1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/did-ui-react/-/did-ui-react-1.0.0-alpha.6.tgz#96aa51a9da9268204cb42a3b0054dda6a4921f3f" + integrity sha512-yw6nxqRB2Z4bfFbOkTM3nwL710wo35wz2D1NbZ1vmbg0wgTV4TAPU2XAPZG+QqWDynYUmM3J22L592LkZghseQ== + dependencies: + "@matt-block/react-recaptcha-v2" "^2.0.0" + "@portkey/did" "^1.0.0-alpha.6" + "@portkey/socket" "^1.0.0-alpha.6" + "@rc-component/portal" "1.0.2" + aelf-sdk "^3.2.40" + antd "4.24.4" + antd-mobile "5.26.0" + bignumber.js "^9.1.0" + clsx "^1.2.1" + lottie-web "5.9.6" + query-string "^6.14.1" + react-i18next "^11.18.6" + react-qrcode-logo "2.9.0" + react-use "^17.4.0" + reactjs-social-login "^2.6.2" + uuid "^8.3.2" + +"@portkey/did@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/did/-/did-1.0.0-alpha.6.tgz#d22a6b64f59748d0e2398d27e75839f6d8587c87" + integrity sha512-vDNM7EyPDXZZv27Bo1bNRBzrCOKK+eG5tMSoSmxr962GqcWr3xAprtAmdoHnejIzEUatBaD+GV1pS3s4I8rfqA== + dependencies: + "@portkey/accounts" "^1.0.0-alpha.6" + "@portkey/contracts" "^1.0.0-alpha.6" + "@portkey/graphql" "^1.0.0-alpha.6" + "@portkey/request" "^1.0.0-alpha.6" + "@portkey/services" "^1.0.0-alpha.6" + "@portkey/types" "^1.0.0-alpha.6" + "@portkey/utils" "^1.0.0-alpha.6" + aelf-sdk "^3.2.44" + react "^18.2.0" + "@portkey/extension-provider@1.0.1-alpha.0": version "1.0.1-alpha.0" resolved "https://registry.npmjs.org/@portkey/extension-provider/-/extension-provider-1.0.1-alpha.0.tgz#3f106032eb21209c6fe7b7921879e07c0fbb1e61" @@ -5213,6 +5289,16 @@ "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" +"@portkey/graphql@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/graphql/-/graphql-1.0.0-alpha.6.tgz#bbf9cdb6f460d54e036eadbf9d9876dc5e5633f3" + integrity sha512-O5144dCMaSkP5HV8sA8Pfh8fIMwsE+EFYQXX3NpPVBgSThEDDYU3a1M+AwpC1e5f//ANMnOAIPc4QT7lZgYvpw== + dependencies: + "@apollo/client" "^3.7.3" + "@portkey/types" "^1.0.0-alpha.6" + graphql "^16.6.0" + subscriptions-transport-ws "^0.11.0" + "@portkey/mobile-provider@1.0.1-alpha.0": version "1.0.1-alpha.0" resolved "https://registry.npmjs.org/@portkey/mobile-provider/-/mobile-provider-1.0.1-alpha.0.tgz#b059d3bd317e010358f031e71f778a86c26f9d60" @@ -5245,6 +5331,25 @@ lodash "^4.17.21" readable-stream "^4.4.0" +"@portkey/request@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/request/-/request-1.0.0-alpha.6.tgz#67d724d7d2363ad828c1ae323d550991b0b30828" + integrity sha512-MctgldYxpEo5TvX/hR7fa60LvvgNEwXTX+77dZ1UlTJtVwVUEZ5Tpjusks0Ao3jVt/WVe7STToq9pcAr1IGT0Q== + dependencies: + "@portkey/types" "^1.0.0-alpha.6" + query-string "^6.14.1" + +"@portkey/services@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/services/-/services-1.0.0-alpha.6.tgz#c0dab0f3edef4678c9ee20ffce8b6711bed29dd6" + integrity sha512-nKdyY6BpHKcWfSfZd3yQ8Scsz5kblu2GDNhT6dV7WRamxE6P95GY2Z8gWPE1Vv8DavK5e3Io4GS9EN/kxBt+MA== + dependencies: + "@portkey/graphql" "^1.0.0-alpha.6" + "@portkey/request" "^1.0.0-alpha.6" + "@portkey/types" "^1.0.0-alpha.6" + aelf-sdk "^3.2.44" + query-string "^7.1.1" + "@portkey/socket@1.0.0-alpha.2": version "1.0.0-alpha.2" resolved "https://registry.npmjs.org/@portkey/socket/-/socket-1.0.0-alpha.2.tgz#fc243b61c03d2d5c0856190478af566841c00c0e" @@ -5253,6 +5358,14 @@ "@abp/signalr" "^7.0.0" "@portkey/utils" "^1.0.0-alpha.2" +"@portkey/socket@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/socket/-/socket-1.0.0-alpha.6.tgz#cbf61c5cadf49218c56c20fad6fae56522cc32b0" + integrity sha512-FP29M/aYU84v2owQZUQiFCmFeIxd0lBI+s5ysCaP88kqHqmhgqCr32hw31kNlC7KIcMvBCtza99Dj13ChSYl7g== + dependencies: + "@abp/signalr" "^7.0.0" + "@portkey/utils" "^1.0.0-alpha.6" + "@portkey/types@1.0.0-alpha.4": version "1.0.0-alpha.4" resolved "https://registry.npmjs.org/@portkey/types/-/types-1.0.0-alpha.4.tgz#2f22a75a1af6df31db15fce1c473ca9e1f07f127" @@ -5267,6 +5380,13 @@ dependencies: "@types/elliptic" "^6.4.14" +"@portkey/types@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/types/-/types-1.0.0-alpha.6.tgz#e15f90432c785ea500f62e0c83995a185bdd162b" + integrity sha512-JCIKVWZqO5POy1cIaxIJcsIWyjT1q1eb/uMxkNhzenBQP58nXfPerbu2zx6FsA3XdwoqtzibJBYOSddA0Jr3Wg== + dependencies: + "@types/elliptic" "^6.4.14" + "@portkey/utils@^1.0.0-alpha.2": version "1.0.0-alpha.2" resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.2.tgz#719184830e520976c35db44ed131bcd95923df0b" @@ -5281,6 +5401,18 @@ dependencies: aelf-sdk "^3.2.44" +"@portkey/utils@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.6.tgz#935a959761f22f79c97f8fc1ee359fc215cf7879" + integrity sha512-akvB4Z5OO1j7J1UcklblaJa4qlUzUNS7AivHQgdRVVdA0xpn9JamDtWUsLJk3iPG3WTRwN52O5zyoDHjZs7reA== + dependencies: + aelf-sdk "^3.2.44" + +"@portkey/validator@^1.0.0-alpha.6": + version "1.0.0-alpha.6" + resolved "https://registry.npmjs.org/@portkey/validator/-/validator-1.0.0-alpha.6.tgz#2e106cb66a620c1052b7713513f1245a4652b15e" + integrity sha512-/WxtzawTDp56/uP88QarEnKKECR9Ep8eBbpBFSaXWrXyMDZU4uPndvVSJN4+5EKNFB2P0FG8Tx2Ldl8Pn5F1Xw== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -5341,6 +5473,15 @@ dependencies: "@babel/runtime" "^7.18.0" +"@rc-component/portal@1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.0.2.tgz#c14cdd21e7da42c0db417ce01c624df170349ec6" + integrity sha512-fEfbp5PcE+63zukZFLxqtPz7JSX4G5s6IUIqodLeoVeZ9I0mN1OGjOFDDX/BOpGxdoviH7xchIHxYxXJn2ReRg== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.16.0" + "@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.2": version "1.0.3" resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.0.3.tgz#3aa2c229a7a20ac2412d864e8977e6377973416e" @@ -5694,6 +5835,14 @@ "@react-spring/shared" "~9.6.1" "@react-spring/types" "~9.6.1" +"@react-spring/animated@~9.7.3": + version "9.7.3" + resolved "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz#4211b1a6d48da0ff474a125e93c0f460ff816e0f" + integrity sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw== + dependencies: + "@react-spring/shared" "~9.7.3" + "@react-spring/types" "~9.7.3" + "@react-spring/core@~9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.6.1.tgz#ebe07c20682b360b06af116ea24e2b609e778c10" @@ -5704,6 +5853,15 @@ "@react-spring/shared" "~9.6.1" "@react-spring/types" "~9.6.1" +"@react-spring/core@~9.7.3": + version "9.7.3" + resolved "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz#60056bcb397f2c4f371c6c9a5f882db77ae90095" + integrity sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ== + dependencies: + "@react-spring/animated" "~9.7.3" + "@react-spring/shared" "~9.7.3" + "@react-spring/types" "~9.7.3" + "@react-spring/rafz@~9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.6.1.tgz#d71aafb92b78b24e4ff84639f52745afc285c38d" @@ -5717,11 +5875,33 @@ "@react-spring/rafz" "~9.6.1" "@react-spring/types" "~9.6.1" +"@react-spring/shared@~9.7.3": + version "9.7.3" + resolved "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz#4cf29797847c689912aec4e62e34c99a4d5d9e53" + integrity sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA== + dependencies: + "@react-spring/types" "~9.7.3" + "@react-spring/types@~9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.6.1.tgz#913d3a68c5cbc1124fdb18eff919432f7b6abdde" integrity sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q== +"@react-spring/types@~9.7.3": + version "9.7.3" + resolved "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz#ea78fd447cbc2612c1f5d55852e3c331e8172a0b" + integrity sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw== + +"@react-spring/web@^9.5.5": + version "9.7.3" + resolved "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz#d9f4e17fec259f1d65495a19502ada4f5b57fa3d" + integrity sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg== + dependencies: + "@react-spring/animated" "~9.7.3" + "@react-spring/core" "~9.7.3" + "@react-spring/shared" "~9.7.3" + "@react-spring/types" "~9.7.3" + "@react-spring/web@~9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.6.1.tgz#3e4c03b724d2b545dc2fa2649eb6109318ab9178" @@ -7974,6 +8154,22 @@ ahooks-v3-count@^1.0.0: resolved "https://registry.yarnpkg.com/ahooks-v3-count/-/ahooks-v3-count-1.0.0.tgz#ddeb392e009ad6e748905b3cbf63a9fd8262ca80" integrity sha512-V7uUvAwnimu6eh/PED4mCDjE7tokeZQLKlxg9lCTMPhN+NjsSbtdacByVlR1oluXQzD3MOw55wylDmQo4+S9ZQ== +ahooks@^3.7.2: + version "3.7.8" + resolved "https://registry.npmjs.org/ahooks/-/ahooks-3.7.8.tgz#3fa3c491cd153e884a32b0c4192fc72cf84c4332" + integrity sha512-e/NMlQWoCjaUtncNFIZk3FG1ImSkV/JhScQSkTqnftakRwdfZWSw6zzoWSG9OMYqPNs2MguDYBUFFC6THelWXA== + dependencies: + "@babel/runtime" "^7.21.0" + "@types/js-cookie" "^2.x.x" + ahooks-v3-count "^1.0.0" + dayjs "^1.9.1" + intersection-observer "^0.12.0" + js-cookie "^2.x.x" + lodash "^4.17.21" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + tslib "^2.4.1" + ahooks@^3.7.6: version "3.7.7" resolved "https://registry.yarnpkg.com/ahooks/-/ahooks-3.7.7.tgz#b046f8a23a6e6d6695a37ba44f5f4331cf09f1eb" @@ -8174,6 +8370,28 @@ antd-mobile-v5-count@^1.0.1: resolved "https://registry.yarnpkg.com/antd-mobile-v5-count/-/antd-mobile-v5-count-1.0.1.tgz#85f20c46d1635c24e856bcf5ad55e8c98e44a523" integrity sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA== +antd-mobile@5.26.0: + version "5.26.0" + resolved "https://registry.npmjs.org/antd-mobile/-/antd-mobile-5.26.0.tgz#338282126c2054fa8b84777f81db112238939261" + integrity sha512-syhaSpgiR+SG+s868R0nS+vj3pORVrM2fp9ZPLQD5yHlpxdXZ9/OY82+ttNtuazz2r37PxOOQ/0ggQds5t9LrA== + dependencies: + "@floating-ui/dom" "^1.0.6" + "@react-spring/web" "^9.5.5" + "@use-gesture/react" "10.2.20" + ahooks "^3.7.2" + antd-mobile-icons "^0.3.0" + antd-mobile-v5-count "^1.0.1" + big.js "^6.2.1" + classnames "^2.3.2" + dayjs "^1.11.6" + lodash "^4.17.21" + rc-field-form "~1.27.3" + react-is "^17.0.2" + runes "^0.4.3" + staged-components "^1.1.3" + tslib "^2.4.1" + use-sync-external-store "^1.2.0" + antd-mobile@^5.26.0: version "5.30.0" resolved "https://registry.yarnpkg.com/antd-mobile/-/antd-mobile-5.30.0.tgz#e7540617bbaf9cbe0a1ab69b9811a1906b8a85b8" @@ -8197,7 +8415,7 @@ antd-mobile@^5.26.0: tslib "^2.5.0" use-sync-external-store "^1.2.0" -antd@^4.21.7: +antd@4.24.4, antd@^4.21.7: version "4.24.4" resolved "https://registry.yarnpkg.com/antd/-/antd-4.24.4.tgz#33df11a101ecfe62ed4bc8a492e7f83e9b83e5e0" integrity sha512-XKZYMCKTQV+z0kZkAodvWMph9EhnNLon4JrWOoi2rjBzsYKKOVNEceXi3TZy/wdF6GcawjBduL3gd6NtWcTe4A== @@ -9955,6 +10173,11 @@ big.js@^5.2.2: resolved "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +big.js@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f" + integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ== + bigint-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" @@ -19251,7 +19474,7 @@ lottie-react-native@^5.1.5: invariant "^2.2.2" react-native-safe-modules "^1.0.3" -lottie-web@^5.9.6: +lottie-web@5.9.6, lottie-web@^5.9.6: version "5.9.6" resolved "https://registry.npmmirror.com/lottie-web/-/lottie-web-5.9.6.tgz#62ae68563355d3e04aa75d53dec3dd4bea0e57c9" integrity sha512-JFs7KsHwflugH5qIXBpB4905yC1Sub2MZWtl/elvO/QC6qj1ApqbUZJyjzJseJUtVpgiDaXQLjBlIJGS7UUUXA== @@ -22610,6 +22833,16 @@ query-string@6.13.5: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +query-string@^6.14.1: + version "6.14.1" + resolved "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + query-string@^7.0.0, query-string@^7.1.1: version "7.1.1" resolved "https://registry.npmmirror.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" @@ -22817,7 +23050,7 @@ rc-field-form@~1.27.0: async-validator "^4.1.0" rc-util "^5.8.0" -rc-field-form@~1.27.4: +rc-field-form@~1.27.3, rc-field-form@~1.27.4: version "1.27.4" resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.4.tgz#53600714af5b28c226c70d34867a8c52ccd64d44" integrity sha512-PQColQnZimGKArnOh8V2907+VzDCXcqtFvHgevDLtqWc/P7YASb/FqntSmdS8q3VND5SHX3Y1vgMIzY22/f/0Q== @@ -23321,7 +23554,7 @@ react-is@^16.12.0, react-is@^16.13.0, react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-is@^17.0.1: +react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== @@ -24656,6 +24889,11 @@ runes2@^1.1.2: resolved "https://registry.yarnpkg.com/runes2/-/runes2-1.1.2.tgz#408a4cdd31077ca62f52a9aa5bd22d6d16ae6a41" integrity sha512-v6XIdRpUKdFLNhgF2AC9XvntZsDzxyTpVlpQ8HD592XD6vHiW8jEcHFnTV5ztUjWJC5cGOcdi9YKIwxWVh0f9w== +runes@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/runes/-/runes-0.4.3.tgz#32f7738844bc767b65cc68171528e3373c7bb355" + integrity sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg== + rx-lite-aggregates@^4.0.8: version "4.0.8" resolved "https://registry.npmmirror.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" From f3336168163ada2488e948749aea20585e7ccf11 Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 5 Jul 2023 15:57:50 +0800 Subject: [PATCH 259/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20payment=20?= =?UTF-8?q?hook=20ut=20and=20delete=20useIpInfo=20hook=20ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.test.ts | 143 +++++++++++++++++++++- packages/hooks/hooks-ca/useIpInfo.test.ts | 31 ----- packages/hooks/hooks-ca/useIpInfo.ts | 24 ---- 3 files changed, 142 insertions(+), 56 deletions(-) delete mode 100644 packages/hooks/hooks-ca/useIpInfo.test.ts delete mode 100644 packages/hooks/hooks-ca/useIpInfo.ts diff --git a/packages/hooks/hooks-ca/payment.test.ts b/packages/hooks/hooks-ca/payment.test.ts index ba092cdd93..2384825c8c 100644 --- a/packages/hooks/hooks-ca/payment.test.ts +++ b/packages/hooks/hooks-ca/payment.test.ts @@ -1,16 +1,32 @@ -import { usePayment, useGetAchTokenInfo } from './payment'; +import { usePayment, useGetAchTokenInfo, useSellTransfer } from './payment'; import { renderHookWithProvider } from '../../../test/utils/render'; import { setupStore } from '../../../test/utils/setup'; import { PaymentState } from '../../../test/data/paymentState'; import { GuardianState } from '../../../test/data/guardianState'; import { getAchToken } from '@portkey-wallet/api/api-did/payment/util'; import { useAppCommonDispatch } from '../index'; +import * as networkHook from './network'; +import { renderHook } from '@testing-library/react'; +import { ACH_MERCHANT_NAME } from '@portkey-wallet/constants/constants-ca/payment'; +import { randomId } from '@portkey-wallet/utils'; +import signalrSell from '@portkey-wallet/socket/socket-sell'; +import { request } from '@portkey-wallet/api/api-did'; jest.mock('@portkey-wallet/api/api-did/payment/util'); jest.mock('../index'); jest.mocked(useAppCommonDispatch).mockReturnValue(async (call: () => void) => { return call; }); +jest.mock('@portkey-wallet/utils'); +jest.mock('@portkey-wallet/socket/socket-sell'); +jest.mock('@portkey-wallet/api/api-did'); + +beforeAll(() => { + jest.useFakeTimers(); +}); +beforeEach(() => { + jest.restoreAllMocks(); +}); describe('usePayment', () => { test('get payment data successfully', () => { @@ -85,3 +101,128 @@ describe('useGetAchTokenInfo', () => { expect(res).toHaveProperty('token', achToken.accessToken); }); }); + +describe('useSellTransfer', () => { + test('testnet, and halfway return', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(false); + + const { result } = renderHook(() => useSellTransfer()); + + const res = await result.current({ merchantName: ACH_MERCHANT_NAME, orderId: '', paymentSellTransfer: jest.fn() }); + expect(res).toBeUndefined(); + }); + test('merchantName !== ACH_MERCHANT_NAME, and halfway return', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(true); + + const { result } = renderHook(() => useSellTransfer()); + + const res = await result.current({ + merchantName: (ACH_MERCHANT_NAME + 'test') as any, + orderId: '', + paymentSellTransfer: jest.fn(), + }); + expect(res).toBeUndefined(); + }); + test('mainnet and signalrSellResult === null, throw TIMEOUT error', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(true); + jest.mocked(randomId).mockReturnValue('test_id'); + jest.mocked(signalrSell.doOpen).mockImplementation(jest.fn()); + const mockSignalrSell = jest.fn((_params, callback) => { + callback(null); // mock callback behavior + return { + remove: jest.fn(), // return mock remove function + }; + }); + jest.mocked(signalrSell.onAchTxAddressReceived).mockImplementation(mockSignalrSell); + jest.mocked(signalrSell.requestAchTxAddress).mockImplementation(jest.fn()); + // jest.spyOn(window.Promise, 'race').mockResolvedValue(null); + + // renderHook + const { result } = renderHook(() => useSellTransfer()); + + const mockPaymentSellTransfer = jest.fn().mockResolvedValue({ + transactionId: 'transactionId', + }); + + try { + await result.current({ + merchantName: ACH_MERCHANT_NAME, + orderId: '', + paymentSellTransfer: mockPaymentSellTransfer, + }); + } catch (error) { + expect(error).toEqual({ code: 'TIMEOUT', message: 'Transaction failed.' }); + } + }); + test('mainnet, signalrSellResult and !transactionId, throw NO_TX_HASH error ', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(true); + jest.mocked(randomId).mockReturnValue('test_id'); + jest.mocked(signalrSell.doOpen).mockImplementation(jest.fn()); + const mockSignalrSell = jest.fn((params, callback) => { + const data = { params }; + callback(data); // mock callback behavior + return { + remove: jest.fn(), // return mock remove function + }; + }); + jest.mocked(signalrSell.onAchTxAddressReceived).mockImplementation(mockSignalrSell); + jest.mocked(signalrSell.requestAchTxAddress).mockImplementation(jest.fn()); + jest.mocked(signalrSell.stop).mockImplementation(jest.fn()); + jest.spyOn(global, 'setTimeout'); + jest.runAllTimers(); + jest.mocked(request.payment.updateAlchemyOrderTxHash).mockResolvedValue({}); + + // renderHook + const { result } = renderHook(() => useSellTransfer()); + + const mockPaymentSellTransfer = jest.fn().mockResolvedValue({ + transactionId: '', + }); + try { + await result.current({ + merchantName: ACH_MERCHANT_NAME, + orderId: '', + paymentSellTransfer: mockPaymentSellTransfer, + }); + } catch (error) { + // expect + expect(error).toEqual({ + code: 'NO_TX_HASH', + message: 'Transaction failed. Please contact the team for assistance.', + }); + } + }); + test('mainnet, signalrSellResult and transactionId, update order tx hash', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(true); + jest.mocked(randomId).mockReturnValue('test_id'); + jest.mocked(signalrSell.doOpen).mockImplementation(jest.fn()); + const mockSignalrSell = jest.fn((params, callback) => { + const data = { params }; + callback(data); // mock callback behavior + return { + remove: jest.fn(), // return mock remove function + }; + }); + jest.mocked(signalrSell.onAchTxAddressReceived).mockImplementation(mockSignalrSell); + jest.mocked(signalrSell.requestAchTxAddress).mockImplementation(jest.fn()); + jest.mocked(signalrSell.stop).mockImplementation(jest.fn()); + jest.spyOn(global, 'setTimeout'); + jest.runAllTimers(); + jest.mocked(request.payment.updateAlchemyOrderTxHash).mockResolvedValue({}); + + // renderHook + const { result } = renderHook(() => useSellTransfer()); + + const mockPaymentSellTransfer = jest.fn().mockResolvedValue({ + transactionId: 'transactionId', + }); + await result.current({ + merchantName: ACH_MERCHANT_NAME, + orderId: '', + paymentSellTransfer: mockPaymentSellTransfer, + }); + + // expect + expect(request.payment.updateAlchemyOrderTxHash).toBeCalled(); + }); +}); diff --git a/packages/hooks/hooks-ca/useIpInfo.test.ts b/packages/hooks/hooks-ca/useIpInfo.test.ts deleted file mode 100644 index 1d367f2650..0000000000 --- a/packages/hooks/hooks-ca/useIpInfo.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { renderHook, act } from '@testing-library/react'; -import useIpInfo from './useIpInfo'; -import { request } from '@portkey-wallet/api/api-did'; - -jest.mock('@portkey-wallet/api/api-did'); - -describe('useIpInfo', () => { - test('complete data, and return successfully', async () => { - expect.assertions(3); - - jest - .mocked(request.verify.getCountry) - .mockResolvedValue({ code: 'HK', country: 'Hong Kong SAR China', iso: '852' }); - - const res = await act(async () => renderHook(() => useIpInfo())); - - expect(res.result.current).toHaveProperty('code', 'HK'); - expect(res.result.current).toHaveProperty('country', 'Hong Kong SAR China'); - expect(res.result.current).toHaveProperty('iso', '852'); - }); - test('mock reject, and catch error', async () => { - jest.mocked(request.verify.getCountry).mockRejectedValue({ code: 500, message: 'server error' }); - - try { - await act(async () => renderHook(() => useIpInfo())); - } catch (e) { - expect(e).toHaveProperty('code', 500); - expect(e).toHaveProperty('message', 'server error'); - } - }); -}); diff --git a/packages/hooks/hooks-ca/useIpInfo.ts b/packages/hooks/hooks-ca/useIpInfo.ts deleted file mode 100644 index f032c21fac..0000000000 --- a/packages/hooks/hooks-ca/useIpInfo.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useState, useCallback, useEffect } from 'react'; -import { CountryItem } from '@portkey-wallet/types/types-ca/country'; -import { usePhoneCountryCode } from './misc'; - -const useIpInfo = () => { - const [ipInfo, setIpInfo] = useState(); - const { localPhoneCountryCode } = usePhoneCountryCode(); - const getIpInfo = useCallback(async () => { - try { - setIpInfo(localPhoneCountryCode); - } catch (error) { - console.log(error, 'useIpInfo'); - } - }, [localPhoneCountryCode]); - - useEffect(() => { - getIpInfo(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ipInfo; -}; - -export default useIpInfo; From 77f42c01d5bc2fa86d5351df044c5046da840280 Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 5 Jul 2023 16:24:44 +0800 Subject: [PATCH 260/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20assets=20h?= =?UTF-8?q?ook=20ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/assets.test.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 packages/hooks/hooks-ca/assets.test.ts diff --git a/packages/hooks/hooks-ca/assets.test.ts b/packages/hooks/hooks-ca/assets.test.ts new file mode 100644 index 0000000000..efd1e96de0 --- /dev/null +++ b/packages/hooks/hooks-ca/assets.test.ts @@ -0,0 +1,21 @@ +import { AssetsState } from '../../../test/data/assetsState'; +import { renderHookWithProvider } from '../../../test/utils/render'; +import { setupStore } from '../../../test/utils/setup'; +import { useAssets } from './assets'; + +describe('useAssets', () => { + test('get assets data successfully', () => { + const { result } = renderHookWithProvider(useAssets, setupStore(AssetsState)); + + expect(result.current).toHaveProperty('accountAssets'); + expect(result.current).toHaveProperty('accountBalance'); + expect(result.current).toHaveProperty('accountNFT'); + expect(result.current).toHaveProperty('accountToken'); + expect(result.current).toHaveProperty('tokenPrices'); + }); + test('failed to get assets data', () => { + const { result } = renderHookWithProvider(useAssets, setupStore({})); + + expect(result.current).toBeUndefined(); + }); +}); From ad2047fecaa54e0a9365fabcbd851474b4c41e10 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 17:09:35 +0800 Subject: [PATCH 261/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20custom=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Token/CustomToken/index.tsx | 26 +++++-- .../components/TokenItem/index.tsx | 4 +- .../js/pages/Token/ManageTokenList/index.tsx | 73 ++++++++++++------- packages/mobile-app-did/js/store/config.ts | 2 + 4 files changed, 69 insertions(+), 36 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 60819ea6f1..7da7544ea1 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -17,6 +17,9 @@ import { request } from '@portkey-wallet/api/api-did'; import { useDebounceCallback } from '@portkey-wallet/hooks'; import Loading from 'components/Loading'; import navigationService from 'utils/navigationService'; +import { sleep } from '@portkey-wallet/utils'; +import CommonToast from 'components/CommonToast'; +import { FontStyles } from 'assets/theme/styles'; interface CustomTokenProps { route?: any; @@ -32,14 +35,14 @@ const CustomToken: React.FC = () => { const [tokenItem, setTokenItem] = useState<{ symbol: string; chainId: ChainId; - decimal: string; + decimals: string; id: string; isDefault?: boolean; isDisplay?: boolean; }>({ symbol: '', chainId: originChainId, - decimal: '--', + decimals: '--', id: '', }); const [btnDisable, setBtnDisable] = useState(true); @@ -47,10 +50,13 @@ const CustomToken: React.FC = () => { const fetchTokenItem = useCallback(async () => { Loading.show(); + setBtnDisable(false); + setTokenItem(pre => ({ ...pre, decimals: '--', symbol: '' })); + try { const res = await request.token.fetchTokenItemBySearch({ params: { - symbol: tokenItem.symbol, + symbol: keyword, chainId: tokenItem.chainId, }, }); @@ -59,15 +65,14 @@ const CustomToken: React.FC = () => { if (symbol && decimals && id) { setTokenItem(pre => ({ ...pre, ...res })); setKeyword(symbol); - setBtnDisable(true); + setBtnDisable(false); } } catch (error) { setBtnDisable(true); - console.log('filter search error', error); } finally { Loading.hide(); } - }, [tokenItem.chainId, tokenItem.symbol]); + }, [keyword, tokenItem.chainId]); const fetchTokenItemDebounce = useDebounceCallback(fetchTokenItem, [tokenItem], 500); @@ -91,6 +96,9 @@ const CustomToken: React.FC = () => { isDisplay: true, }, }); + Loading.hide(); + CommonToast.success('success'); + await sleep(500); navigationService.goBack(); } catch (error: any) { console.log('add custom token error', error); @@ -98,7 +106,7 @@ const CustomToken: React.FC = () => { Loading.hide(); } } - }, [tokenItem?.id, tokenItem?.isDefault, tokenItem?.isDisplay]); + }, [tokenItem]); return ( = () => { /> - {tokenItem.decimal} + + {tokenItem.decimals} + diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx index 2d2edb32ae..a1e89e6169 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx @@ -16,7 +16,7 @@ import { NetworkType } from '@portkey-wallet/types'; type TokenItemProps = { networkType: NetworkType; item: TokenItemShowType; - onHandleToken: (item: TokenItemShowType, type: 'add' | 'delete') => void; + onHandleToken: (item: TokenItemShowType, isDisplay: boolean) => void; }; const TokenItem = ({ networkType, item, onHandleToken }: TokenItemProps) => { @@ -48,7 +48,7 @@ const TokenItem = ({ networkType, item, onHandleToken }: TokenItemProps) => { ) : ( { - onHandleToken(item, item.isAdded ? 'delete' : 'add'); + onHandleToken(item, !!item.isAdded); }}> diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index b8c7f4bd86..20d44e334d 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -20,6 +20,7 @@ import PopularTokenSection from './components/PopularToken'; import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; +import { useFocusEffect } from '@react-navigation/native'; interface ManageTokenListProps { route?: any; @@ -37,54 +38,72 @@ const ManageTokenList: React.FC = () => { const { tokenDataShowInMarket } = useAppCASelector(state => state.tokenManagement); const [keyword, setKeyword] = useState(''); - const [filterTokenList, setFilterTokenList] = useState([]); + const [filterTokenList, setFilterTokenList] = useState([]); const debounceWord = useDebounce(keyword, 500); + const fetchSearchedTokenList = useCallback(async () => { + try { + if (!debounceWord) return; + Loading.show(); + const list = await request.token.fetchTokenListBySearch({ + params: { + symbol: debounceWord, + chainIds: chainIdList, + }, + }); + + const tmpToken: TokenItemShowType[] = list.map((item: any) => ({ + ...item, + isAdded: item.isDisplay, + userTokenId: item.id, + })); + setFilterTokenList(tmpToken); + Loading.hide(); + } catch (error) { + console.log('filter search error', error); + Loading.hide(); + } + }, [chainIdList, debounceWord]); + const onHandleTokenItem = useCallback( - async (item: TokenItemShowType, isAdded: boolean) => { - // TODO + async (item: TokenItemShowType, isDisplay: boolean) => { Loading.show(); + await request.token .displayUserToken({ resourceUrl: `${item.userTokenId}/display`, params: { - isDisplay: isAdded, + isDisplay, }, }) - .then(res => { - console.log(res); - timerRef.current = setTimeout(() => { - dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); + .then(() => { + timerRef.current = setTimeout(async () => { dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); + if (debounceWord) { + await fetchSearchedTokenList(); + } else { + await dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); + } Loading.hide(); - CommonToast.success('Success'); - }, 1000); + }, 800); }) .catch(err => { console.log(err); + Loading.hide(); CommonToast.fail('Fail'); }); }, - [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch], + [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch, fetchSearchedTokenList], ); - const fetchSearchedTokenList = useCallback(async () => { - try { - if (!debounceWord) return; - const res = await request.token.fetchTokenListBySearch({ - params: { - symbol: debounceWord, - chainIds: chainIdList, - }, - }); - console.log('res', res); - setFilterTokenList(res?.data || []); - } catch (error) { - console.log('filter search error', error); - } - }, [chainIdList, debounceWord]); + useFocusEffect( + useCallback(() => { + fetchSearchedTokenList(); + dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); + }, [chainIdList, dispatch, fetchSearchedTokenList]), + ); useEffect(() => { if (tokenDataShowInMarket.length) return; @@ -95,12 +114,14 @@ const ManageTokenList: React.FC = () => { useEffect(() => { if (debounceWord) { // get filter token + setFilterTokenList([]); fetchSearchedTokenList(); } else { dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); } }, [chainIdList, debounceWord, dispatch, fetchSearchedTokenList]); + // clear timer useEffect( () => () => { if (timerRef.current) clearInterval(timerRef.current); diff --git a/packages/mobile-app-did/js/store/config.ts b/packages/mobile-app-did/js/store/config.ts index 30d2d5ec6e..9bd0286fad 100644 --- a/packages/mobile-app-did/js/store/config.ts +++ b/packages/mobile-app-did/js/store/config.ts @@ -17,6 +17,7 @@ import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; import assetsSlice from '@portkey-wallet/store/store-ca/assets/slice'; +import tokenManagementSlice from '@portkey-wallet/store/store-ca/tokenManagement/slice'; interface ThunkOptions { extraArgument: E; @@ -41,6 +42,7 @@ const reduxPersistConfig = { contactSlice.name, miscSlice.name, tokenBalanceSlice.name, + tokenManagementSlice.name, settingsSlice.name, chainSlice.name, recentSlice.name, From af9aa5bed6c8118c2fa5a135beb6d94379d21c5e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 17:12:45 +0800 Subject: [PATCH 262/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/android/app/build.gradle | 2 +- packages/mobile-app-did/ios/Portkey/Info.plist | 2 +- .../mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/android/app/build.gradle b/packages/mobile-app-did/android/app/build.gradle index 0b20f653f8..ccd163b063 100644 --- a/packages/mobile-app-did/android/app/build.gradle +++ b/packages/mobile-app-did/android/app/build.gradle @@ -168,7 +168,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 231 - versionName "1.3.1" + versionName "1.3.2" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/packages/mobile-app-did/ios/Portkey/Info.plist b/packages/mobile-app-did/ios/Portkey/Info.plist index ec19d210e0..8dbd017cc4 100644 --- a/packages/mobile-app-did/ios/Portkey/Info.plist +++ b/packages/mobile-app-did/ios/Portkey/Info.plist @@ -32,7 +32,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.1 + 1.3.2 CFBundleSignature ???? CFBundleVersion diff --git a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx index d577c1fa7f..1c08327463 100644 --- a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx @@ -29,7 +29,7 @@ const AboutUs = () => { Portkey - {`V${Application.nativeApplicationVersion} beta`} + {`V ${Application.nativeApplicationVersion}`} {socialMediaList.map((item, index) => ( From 2480fe6b90b5f025970ab121171e7167c5f82db7 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 5 Jul 2023 17:29:26 +0800 Subject: [PATCH 263/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20checkQR?= =?UTF-8?q?CodeExist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/misc.ts | 1 - packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/hooks/hooks-ca/misc.ts b/packages/hooks/hooks-ca/misc.ts index 8916bef339..f94083f547 100644 --- a/packages/hooks/hooks-ca/misc.ts +++ b/packages/hooks/hooks-ca/misc.ts @@ -7,7 +7,6 @@ import { DefaultCountry, getCountryCodeIndex } from '@portkey-wallet/constants/c import { CountryItem } from '@portkey-wallet/types/types-ca/country'; import signalrDid from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; -import { checkQRCodeExist } from '@portkey-wallet/api/api-did/message/utils'; export const useMisc = () => useAppCASelector(state => state.misc); diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index 259308791e..ff84b16108 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -53,7 +53,7 @@ export default function ScanLogin() { setLoading(true); if (targetClientId) { const isQRCodeExist = await checkQRCodeExist(targetClientId); - if (!isQRCodeExist) { + if (isQRCodeExist === true) { CommonToast.warn('The QR code has already been scanned by another device.'); setLoading(false); return; From 565193e83f1abe9de95fb55e8f631e7f34de7c29 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 17:56:29 +0800 Subject: [PATCH 264/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20fetch=20toke?= =?UTF-8?q?n=20list=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/Loading/index.tsx | 5 +++ .../js/pages/Token/CustomToken/index.tsx | 2 +- .../js/pages/Token/ManageTokenList/index.tsx | 42 +++++++++---------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/mobile-app-did/js/components/Loading/index.tsx b/packages/mobile-app-did/js/components/Loading/index.tsx index f4653beeb5..53adbe1429 100644 --- a/packages/mobile-app-did/js/components/Loading/index.tsx +++ b/packages/mobile-app-did/js/components/Loading/index.tsx @@ -52,6 +52,11 @@ export default class Loading extends React.Component { // }, duration); } + static showOnce(options?: ShowOptionsType) { + if (elements.length) return; + Loading.show(options); + } + static hide() { timer && clearTimeout(timer); timer = null; diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 7da7544ea1..fcaea476f6 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -74,7 +74,7 @@ const CustomToken: React.FC = () => { } }, [keyword, tokenItem.chainId]); - const fetchTokenItemDebounce = useDebounceCallback(fetchTokenItem, [tokenItem], 500); + const fetchTokenItemDebounce = useDebounceCallback(fetchTokenItem, [tokenItem], 800); const onValueChange = useCallback( (v: string) => { diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index 20d44e334d..08852ab03c 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -40,12 +40,12 @@ const ManageTokenList: React.FC = () => { const [keyword, setKeyword] = useState(''); const [filterTokenList, setFilterTokenList] = useState([]); - const debounceWord = useDebounce(keyword, 500); + const debounceWord = useDebounce(keyword, 800); const fetchSearchedTokenList = useCallback(async () => { try { if (!debounceWord) return; - Loading.show(); + Loading.showOnce(); const list = await request.token.fetchTokenListBySearch({ params: { symbol: debounceWord, @@ -68,32 +68,30 @@ const ManageTokenList: React.FC = () => { const onHandleTokenItem = useCallback( async (item: TokenItemShowType, isDisplay: boolean) => { - Loading.show(); + Loading.showOnce(); - await request.token - .displayUserToken({ + try { + await request.token.displayUserToken({ resourceUrl: `${item.userTokenId}/display`, params: { isDisplay, }, - }) - .then(() => { - timerRef.current = setTimeout(async () => { - dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); - if (debounceWord) { - await fetchSearchedTokenList(); - } else { - await dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); - } - Loading.hide(); - CommonToast.success('Success'); - }, 800); - }) - .catch(err => { - console.log(err); - Loading.hide(); - CommonToast.fail('Fail'); }); + timerRef.current = setTimeout(async () => { + dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); + if (debounceWord) { + await fetchSearchedTokenList(); + } else { + await dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); + } + Loading.hide(); + CommonToast.success('Success'); + }, 800); + } catch (err) { + console.log(err); + Loading.hide(); + CommonToast.fail('Fail'); + } }, [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch, fetchSearchedTokenList], ); From 4720a3de4415d4cd2d1325e98a017cd59dd342cf Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 5 Jul 2023 18:31:16 +0800 Subject: [PATCH 265/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20update=20versioin?= =?UTF-8?q?=20&=20adjust=20display=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Token/Manage/index.tsx | 41 +++++++++++-------- .../Wallet/components/AboutUsBody/index.tsx | 2 +- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx index 995fd5876d..ad3ccd8e0c 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx @@ -16,7 +16,7 @@ import clsx from 'clsx'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { request } from '@portkey-wallet/api/api-did'; import { useDebounceCallback } from '@portkey-wallet/hooks'; -import { handleErrorMessage } from '@portkey-wallet/utils'; +import { handleErrorMessage, sleep } from '@portkey-wallet/utils'; import './index.less'; export default function AddToken() { @@ -50,7 +50,6 @@ export default function AddToken() { async (keyword: string) => { try { if (!keyword) return; - setLoading(true); const res = await request.token.fetchTokenListBySearch({ params: { symbol: keyword, @@ -66,14 +65,20 @@ export default function AddToken() { } catch (error) { setTokenShowList([]); console.log('filter search error', error); - } finally { - setLoading(false); } }, - [chainIdArray, setLoading], + [chainIdArray], ); - const searchDebounce = useDebounceCallback(handleSearch, [filterWord], 500); + const searchDebounce = useDebounceCallback( + async (params) => { + setLoading(true); + await handleSearch(params); + setLoading(false); + }, + [filterWord], + 500, + ); const rightElement = useMemo( () => ( @@ -90,31 +95,31 @@ export default function AddToken() { const handleUserTokenDisplay = useCallback( async (item: TokenItemShowType) => { try { + setLoading(true); await request.token.displayUserToken({ resourceUrl: `${item.userTokenId}/display`, params: { isDisplay: !item.isAdded, }, }); - setTimeout(() => { - if (!filterWord) { - appDispatch(fetchAllTokenListAsync({ chainIdArray })); - } else { - handleSearch(filterWord); - } - }, 1000); + sleep(800); + if (!filterWord) { + await appDispatch(fetchAllTokenListAsync({ chainIdArray })); + } else { + await handleSearch(filterWord); + } message.success('success'); } catch (error: any) { const err = handleErrorMessage(error, 'handle display error'); message.error(err); console.log('=== userToken display', error); + } finally { + setLoading(false); } }, - [appDispatch, chainIdArray, filterWord, handleSearch], + [appDispatch, chainIdArray, filterWord, handleSearch, setLoading], ); - const displayToken = useDebounceCallback(handleUserTokenDisplay, [], 500); - const renderTokenItemBtn = useCallback( (item: TokenItemShowType) => { const { isDefault = false, isAdded = true } = item; @@ -130,13 +135,13 @@ export default function AddToken() { ); }, - [displayToken, t], + [handleUserTokenDisplay, t], ); const renderTokenItem = useCallback( diff --git a/packages/web-extension-did/app/web/pages/Wallet/components/AboutUsBody/index.tsx b/packages/web-extension-did/app/web/pages/Wallet/components/AboutUsBody/index.tsx index 6787198ce2..ec7bcc482a 100644 --- a/packages/web-extension-did/app/web/pages/Wallet/components/AboutUsBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Wallet/components/AboutUsBody/index.tsx @@ -38,7 +38,7 @@ export default function AboutUsBody() {
    {t('Portkey')} - {`${process.env.SDK_VERSION?.toUpperCase()} beta`} + {`${process.env.SDK_VERSION?.toUpperCase()}`}
    From 18eebc73c92da47b2edf5d299316d20ab85ddbd6 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 19:22:58 +0800 Subject: [PATCH 266/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20delete=20useles?= =?UTF-8?q?s=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/components/TabsOverlay/index.tsx | 2 +- .../Token/ManageTokenList/components/PopularToken/index.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index d4cd3a6553..f256522438 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -12,7 +12,7 @@ import { screenWidth } from '@portkey-wallet/utils/mobile/device'; import { FontStyles } from 'assets/theme/styles'; import { setStringAsync } from 'expo-clipboard'; import CommonToast from 'components/CommonToast'; -import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { isIOS } from '@rneui/base'; import { useAppCASelector } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx index bad7bb5339..cbb562a483 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx @@ -22,8 +22,6 @@ const PopularTokenSection: React.FC = (props: IPopula const { currentNetwork } = useWallet(); - console.log('tokenDataShowInMarket', tokenDataShowInMarket); - return ( Date: Wed, 5 Jul 2023 19:23:52 +0800 Subject: [PATCH 267/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20custom=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/My/WalletHome/AboutUs/index.tsx | 2 +- .../js/pages/Token/CustomToken/index.tsx | 21 +++++++++++++------ .../components/FilterToken/index.tsx | 2 ++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx index 1c08327463..4e9f190b09 100644 --- a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx @@ -29,7 +29,7 @@ const AboutUs = () => { Portkey - {`V ${Application.nativeApplicationVersion}`} + {`V${Application.nativeApplicationVersion}`} {socialMediaList.map((item, index) => ( diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index fcaea476f6..717077f59d 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -49,8 +49,10 @@ const CustomToken: React.FC = () => { const [errorMessage, setErrorMessage] = useState(''); const fetchTokenItem = useCallback(async () => { + if (!keyword) return; + Loading.show(); - setBtnDisable(false); + setBtnDisable(true); setTokenItem(pre => ({ ...pre, decimals: '--', symbol: '' })); try { @@ -76,7 +78,7 @@ const CustomToken: React.FC = () => { const fetchTokenItemDebounce = useDebounceCallback(fetchTokenItem, [tokenItem], 800); - const onValueChange = useCallback( + const onKeywordChange = useCallback( (v: string) => { setKeyword(v.trim()); fetchTokenItemDebounce(); @@ -84,6 +86,15 @@ const CustomToken: React.FC = () => { [fetchTokenItemDebounce], ); + const onChainChange = useCallback( + (_chainId: ChainId) => { + setBtnDisable(true); + setTokenItem(pre => ({ ...pre, chainId: _chainId })); + fetchTokenItemDebounce(); + }, + [fetchTokenItemDebounce], + ); + const addToken = useCallback(async () => { if (tokenItem?.isDefault || tokenItem?.isDisplay) { setErrorMessage('This token has already been added.'); @@ -102,8 +113,6 @@ const CustomToken: React.FC = () => { navigationService.goBack(); } catch (error: any) { console.log('add custom token error', error); - } finally { - Loading.hide(); } } }, [tokenItem]); @@ -124,7 +133,7 @@ const CustomToken: React.FC = () => { currentNetwork={currentNetwork} chainId={tokenItem.chainId || originChainId} chainList={chainList} - onChainPress={(_chainId: ChainId) => setTokenItem(pre => ({ ...pre, chainId: _chainId }))} + onChainPress={onChainChange} /> @@ -135,7 +144,7 @@ const CustomToken: React.FC = () => { value={keyword} theme={'white-bg'} placeholder={t('Enter Symbol')} - onChangeText={onValueChange} + onChangeText={onKeywordChange} errorMessage={errorMessage} /> diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx index 1a76e458e9..5da427e0bd 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx @@ -96,6 +96,8 @@ export const customTokenTipsStyle = StyleSheet.create({ alignItems: 'center', }, addButton: { + paddingLeft: pTd(16), + paddingRight: pTd(16), borderRadius: pTd(6), height: pTd(44), }, From 716ebf261d0b7b353de4c59255ad2ccf788ed356 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 19:26:02 +0800 Subject: [PATCH 268/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20dapp=20synced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dapp/components/TransactionOverlay/index.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index f3f468f4eb..020ce5638d 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -309,6 +309,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { const account = getManagerAccount(pin); if (!account) return; + const _isManagerSynced = await checkManagerSyncState(transactionInfo.chainId); + if (!_isManagerSynced) return setErrorText('Synchronizing on-chain account information...'); + const contract = await getContractBasic({ contractAddress: chainInfo.caContractAddress, rpcUrl: chainInfo?.endPoint, @@ -347,8 +350,10 @@ const ConnectModal = (props: TransactionModalPropsType) => { } }, [ chainInfo, + checkManagerSyncState, isCAContract, pin, + transactionInfo.chainId, transactionInfo.contractAddress, transactionInfo.method, transactionInfo.params?.paramsOption, @@ -378,15 +383,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, [transactionInfo.chainId, transactionInfo?.params?.paramsOption?.symbol]); useEffect(() => { - (async () => { - // checkIsManagerSynced - const _isManagerSynced = await checkManagerSyncState(transactionInfo.chainId); - - if (!_isManagerSynced) return setErrorText('Synchronizing on-chain account information...'); - - getFee(); - getDecimals(); - })(); + getFee(); + getDecimals(); }, [checkManagerSyncState, getDecimals, getFee, transactionInfo.chainId]); useEffect(() => { From 31919031f892f9733dd4736442823effe3a67eb6 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 5 Jul 2023 20:20:56 +0800 Subject: [PATCH 269/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20=5FisMa?= =?UTF-8?q?nagerSynced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TransactionOverlay/index.tsx | 4 +-- .../js/pages/Send/SendHome/index.tsx | 26 +++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 020ce5638d..b06270174b 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -86,11 +86,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { onSign?.(); OverlayModal.hide(); }, - disabled: isFetchingFee, - loading: isFetchingFee, }, ]; - }, [isFetchingFee, onReject, onSign, t]); + }, [onReject, onSign, t]); const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index 8232f3c3f5..c9ef49bddb 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -87,11 +87,6 @@ const SendHome: React.FC = () => { const account = getManagerAccount(pin); if (!account) return; - const _isManagerSynced = await checkManagerSyncState(chainInfo.chainId); - if (!_isManagerSynced) { - return setErrorMessage([TransactionError.SYNCHRONIZING]); - } - const contract = await getContractBasic({ contractAddress: chainInfo.caContractAddress, rpcUrl: chainInfo?.endPoint, @@ -132,7 +127,6 @@ const SendHome: React.FC = () => { }, [ chainInfo, - checkManagerSyncState, debounceSendNumber, pin, selectedAssets.decimals, @@ -145,6 +139,12 @@ const SendHome: React.FC = () => { ); const onPressMax = useCallback(async () => { + // check is SYNCHRONIZING + const _isManagerSynced = await checkManagerSyncState(chainInfo?.chainId || 'AELF'); + if (_isManagerSynced) { + return setErrorMessage([TransactionError.SYNCHRONIZING]); + } + // balance 0 if (divDecimals(selectedAssets.balance, selectedAssets.decimals).isEqualTo(0)) return setSendNumber('0'); @@ -181,6 +181,8 @@ const SendHome: React.FC = () => { } Loading.hide(); }, [ + chainInfo?.chainId, + checkManagerSyncState, getTransactionFee, selectedAssets.balance, selectedAssets.chainId, @@ -274,9 +276,8 @@ const SendHome: React.FC = () => { const previewDisable = useMemo(() => { if (!selectedToContact?.address) return true; if (sendNumber === '0' || !sendNumber) return true; - if (errorMessage?.length > 0) return true; return false; - }, [selectedToContact?.address, sendNumber, errorMessage]); + }, [selectedToContact?.address, sendNumber]); const checkCanNext = useCallback(() => { const suffix = getAddressChainId(selectedToContact.address, chainInfo?.chainId || 'AELF'); @@ -329,6 +330,13 @@ const SendHome: React.FC = () => { let fee; setErrorMessage([]); + // check is SYNCHRONIZING + const _isManagerSynced = await checkManagerSyncState(chainInfo?.chainId || 'AELF'); + if (!_isManagerSynced) { + setErrorMessage([TransactionError.SYNCHRONIZING]); + return { status: false }; + } + const sendBigNumber = timesDecimals(sendNumber, selectedAssets.decimals || '0'); const assetBalanceBigNumber = ZERO.plus(selectedAssets.balance); const isCross = isCrossChain(selectedToContact.address, assetInfo.chainId); @@ -382,6 +390,8 @@ const SendHome: React.FC = () => { }, [ assetInfo.chainId, assetInfo.symbol, + chainInfo?.chainId, + checkManagerSyncState, getTransactionFee, selectedAssets.balance, selectedAssets.decimals, From 809c3be3cf24d2cef9bf96736575ef3db5609c98 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 6 Jul 2023 07:30:50 +0800 Subject: [PATCH 270/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20cmsState?= =?UTF-8?q?=20mock=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.test.ts | 0 test/data/cmsState.ts | 129 ++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 packages/hooks/hooks-ca/cms.test.ts create mode 100644 test/data/cmsState.ts diff --git a/packages/hooks/hooks-ca/cms.test.ts b/packages/hooks/hooks-ca/cms.test.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/data/cmsState.ts b/test/data/cmsState.ts new file mode 100644 index 0000000000..4b75efeb61 --- /dev/null +++ b/test/data/cmsState.ts @@ -0,0 +1,129 @@ +import { CMSState } from '@portkey-wallet/store/store-ca/cms/types'; + +export const CmsState: { cms: CMSState } = { + cms: { + socialMediaListNetMap: { + TESTNET: [ + { + index: 1, + link: 'https://twitter.com/Portkey_DID', + svgUrl: { filename_disk: '6e8441f7-2260-44fa-9673-94b0352c1889.svg' }, + title: 'Follow us on Twitter', + }, + { + index: 1, + link: 'https://t.me/Portkey_Official_Group', + svgUrl: { filename_disk: '54aefae4-1559-425d-af8b-c9a2752a9e4c.svg' }, + title: 'Join us on Telegram', + }, + { + index: 2, + link: 'https://discord.com/invite/EUBq3rHQhr', + svgUrl: { filename_disk: '22f51bce-6836-4815-b37b-88933254c7c2.svg' }, + title: 'Join us on Discord', + }, + ], + MAIN: [ + { + index: 1, + link: 'https://twitter.com/Portkey_DID', + svgUrl: { filename_disk: '7fd9e50a-961a-46d4-a4b3-f8bcc66a2777.svg' }, + title: 'Follow us on Twitter', + }, + { + index: 2, + link: 'https://discord.com/invite/EUBq3rHQhr', + svgUrl: { filename_disk: 'fd871c3e-155a-4e2a-92bd-e21b24f92d3a.svg' }, + title: 'Join us on Discord', + }, + { + index: 3, + link: 'https://t.me/Portkey_Official_Group', + svgUrl: { filename_disk: 'd77dca95-7cae-455b-891f-85e6785779e6.svg' }, + title: 'Join us on Telegram', + }, + ], + }, + discoverGroupListNetMap: { + TESTNET: [ + { + id: '1', + index: 1, + title: 'GAME', + items: [ + { + id: '1', + index: 1, + title: 'GAME111wdsadasdsadgfsgfsfaddaddasdasdasasdadasdsadasdsdsfdafdaadfdas', + description: 'https://www.baidu.com', + url: 'https://www.baidu.com', + imgUrl: { filename_disk: '19f90253-7d3c-4fca-8fd0-780d40818698.png' }, + }, + { + id: '5', + index: 1, + title: '不删档bingogame', + description: '不删档bingogame', + url: 'https://bingogame-test.portkey.finance', + imgUrl: { filename_disk: '41c87959-be1a-44ce-8ff2-fd891e8232a4.png' }, + }, + { + id: '8', + index: 1, + title: 'http', + description: '啊大赛盛大', + url: 'http://192.168.11.160:3000', + }, + { + id: '7', + index: 6, + title: '自己的local', + description: 'http://192.168.10.139:3000/', + url: 'http://192.168.10.139:3000/', + }, + { + id: '2', + index: 12, + title: 'Game2', + description: 'Game2Game2Game2Game2Game2Game2Game2Game2', + url: '', + imgUrl: { filename_disk: '91a87175-7270-4eb5-b38d-b516ca6676e5.png' }, + }, + ], + }, + { + id: '2', + index: 1, + title: 'Dapps', + items: [ + { + id: '3', + index: 1, + title: 'Dapp1', + description: 'http://192.168.11.79:3000', + url: 'http://192.168.11.79:3000', + imgUrl: { filename_disk: 'ddd16436-0939-4676-a01f-86ea4c1d187f.png' }, + }, + { + id: '6', + index: 3, + title: 'bingogamel33', + description: 'http://192.168.66.240:3000/', + url: 'http://192.168.66.240:3000/', + imgUrl: { filename_disk: '8bfde757-c2e9-43e8-8bb5-ff18beb7d5ed.png' }, + }, + { + id: '4', + index: 6, + title: 'Dapp2', + description: 'Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2', + url: 'Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2', + imgUrl: { filename_disk: '58b740f5-304f-4c6a-90c3-98d928132dd3.png' }, + }, + ], + }, + ], + }, + tabMenuListNetMap: {}, + }, +}; From 85d2965546f6f7a72c37bdaf96db4ee834fb2b5a Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 6 Jul 2023 08:27:40 +0800 Subject: [PATCH 271/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20check=20ma?= =?UTF-8?q?nage=20info=20on=20buy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constants/constants-ca/payment/index.ts | 15 +++++++ .../app/web/i18n/languages/en.json | 8 +++- .../app/web/pages/Buy/Preview/index.tsx | 13 +++++-- .../pages/Buy/components/BuyFrom/index.tsx | 7 +++- .../pages/Buy/components/SellFrom/index.tsx | 6 ++- .../app/web/pages/Buy/const.ts | 19 --------- .../app/web/pages/Buy/index.less | 6 +++ .../app/web/pages/Buy/index.tsx | 39 +++++++++++++++---- .../app/web/types/payment.ts | 18 +++++++++ 9 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 packages/web-extension-did/app/web/types/payment.ts diff --git a/packages/constants/constants-ca/payment/index.ts b/packages/constants/constants-ca/payment/index.ts index bbdc8aa449..014c162bf9 100644 --- a/packages/constants/constants-ca/payment/index.ts +++ b/packages/constants/constants-ca/payment/index.ts @@ -32,3 +32,18 @@ export const ACH_MERCHANT_NAME = 'Alchemy'; export const FaucetUrl = 'https://testnet-faucet.aelf.io/'; export const SELL_SOCKET_TIMEOUT = 20 * 1000; + +export const buySoonText = 'On-ramp is currently not supported. It will be launched in the coming weeks.'; + +export const sellSoonText = 'Off-ramp is currently not supported. It will be launched in the coming weeks.'; + +export const serviceUnavailableText = 'Sorry, the service you are using is temporarily unavailable.'; + +export const soonText = 'On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.'; + +export const disclaimer = + 'AlchemyPay is a fiat-to-crypto platform independently operated by a third-party entity. Portkey shall not be held liable for any losses or damages suffered as a result of using AlchemyPay services.'; + +export const InsufficientFundsText = 'Insufficient funds'; + +export const SynchronizingChainText = 'Synchronizing on-chain account information...'; diff --git a/packages/web-extension-did/app/web/i18n/languages/en.json b/packages/web-extension-did/app/web/i18n/languages/en.json index a1d59d9d33..8ae5765336 100644 --- a/packages/web-extension-did/app/web/i18n/languages/en.json +++ b/packages/web-extension-did/app/web/i18n/languages/en.json @@ -419,5 +419,11 @@ "This guardian is the only login account and cannot be removed": "This guardian is the only login account and cannot be removed", "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian": "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian", "Creating address on the chain...": "Creating address on the chain...", - "Initiating social recovery...": "Initiating social recovery..." + "On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.": "On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.", + "On-ramp is currently not supported. It will be launched in the coming weeks.": "On-ramp is currently not supported. It will be launched in the coming weeks.", + "Off-ramp is currently not supported. It will be launched in the coming weeks.": "Off-ramp is currently not supported. It will be launched in the coming weeks.", + "Initiating social recovery...": "Initiating social recovery...", + "Sorry, the service you are using is temporarily unavailable.": "Sorry, the service you are using is temporarily unavailable.", + "Synchronizing on-chain account information...": "Synchronizing on-chain account information...", + "AlchemyPay is a fiat-to-crypto platform independently operated by a third-party entity. Portkey shall not be held liable for any losses or damages suffered as a result of using AlchemyPay services.": "AlchemyPay is a fiat-to-crypto platform independently operated by a third-party entity. Portkey shall not be held liable for any losses or damages suffered as a result of using AlchemyPay services." } diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index 3fc3cf583a..e65fe0fa03 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -4,12 +4,17 @@ import { Button, message } from 'antd'; import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import { useLocation, useNavigate } from 'react-router'; -import { disclaimer, initPreviewData, MAX_UPDATE_TIME, serviceUnavailableText } from '../const'; +import { initPreviewData, MAX_UPDATE_TIME } from '../const'; import { getAchSignature, getOrderQuote, getPaymentOrderNo } from '@portkey-wallet/api/api-did/payment/util'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; import { useCommonState, useLoading } from 'store/Provider/hooks'; import PromptFrame from 'pages/components/PromptFrame'; -import { ACH_MERCHANT_NAME, TransDirectEnum } from '@portkey-wallet/constants/constants-ca/payment'; +import { + ACH_MERCHANT_NAME, + TransDirectEnum, + disclaimer, + serviceUnavailableText, +} from '@portkey-wallet/constants/constants-ca/payment'; import clsx from 'clsx'; import { useGetAchTokenInfo } from '@portkey-wallet/hooks/hooks-ca/payment'; import paymentApi from '@portkey-wallet/api/api-did/payment'; @@ -172,11 +177,11 @@ export default function Preview() { content: ( <>
    Disclaimer
    - {disclaimer} + {t(disclaimer)} ), }); - }, []); + }, [t]); const handleBack = useCallback(() => { navigate('/buy', { state: state }); diff --git a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx index 2449ef600d..9c5cfc4478 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx @@ -3,6 +3,7 @@ import CurrencyInput from '../CurrencyInput'; import TokenInput, { ICurToken } from '../TokenInput'; import { IKeyDownParams } from 'types'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; +import { useTranslation } from 'react-i18next'; export interface IBuyOrSellFromProps { currencyVal: string; @@ -18,6 +19,7 @@ export interface IBuyOrSellFromProps { curToken: ICurToken; errMsg: string; + warningMsg: string; side: PaymentTypeEnum; } @@ -35,8 +37,10 @@ export default function BuyFrom({ curToken, errMsg, + warningMsg, side, }: IBuyOrSellFromProps) { + const { t } = useTranslation(); return ( <>
    @@ -50,7 +54,8 @@ export default function BuyFrom({ curFiat={curFiat} onSelect={handleCurrencySelect} /> - {!!errMsg &&
    {errMsg}
    } + {!!errMsg &&
    {t(errMsg)}
    } + {!!warningMsg &&
    {t(warningMsg)}
    }
    {`I will receive≈`}
    diff --git a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx index f2dbadbf3b..26a30288a3 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx @@ -1,6 +1,7 @@ import CurrencyInput from '../CurrencyInput'; import TokenInput from '../TokenInput'; import { IBuyOrSellFromProps } from '../BuyFrom'; +import { useTranslation } from 'react-i18next'; export default function SellFrom({ currencyVal, @@ -16,8 +17,10 @@ export default function SellFrom({ curToken, errMsg, + warningMsg, side, }: IBuyOrSellFromProps) { + const { t } = useTranslation(); const tokenChange = (val: string) => { const arr = val.split('.'); // No more than eight digits after the decimal point @@ -41,7 +44,8 @@ export default function SellFrom({ curToken={curToken} onSelect={handleTokenSelect} /> - {!!errMsg &&
    {errMsg}
    } + {!!errMsg &&
    {t(errMsg)}
    } + {!!warningMsg &&
    {t(warningMsg)}
    }
    {`I will receive≈`}
    diff --git a/packages/web-extension-did/app/web/pages/Buy/const.ts b/packages/web-extension-did/app/web/pages/Buy/const.ts index 9e987bef25..a2f46b794c 100644 --- a/packages/web-extension-did/app/web/pages/Buy/const.ts +++ b/packages/web-extension-did/app/web/pages/Buy/const.ts @@ -3,16 +3,6 @@ import { ChainId } from '@portkey-wallet/types'; import { ICurToken } from './components/TokenInput'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; -export const buySoonText = 'On-ramp is currently not supported. It will be launched in the coming weeks.'; -export const sellSoonText = 'Off-ramp is currently not supported. It will be launched in the coming weeks.'; - -export const serviceUnavailableText = 'Sorry, the service you are using is temporarily unavailable.'; - -export const soonText = 'On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.'; - -export const disclaimer = - 'AlchemyPay is a fiat-to-crypto platform independently operated by a third-party entity. Portkey shall not be held liable for any losses or damages suffered as a result of using AlchemyPay services. '; - export enum DrawerType { token, currency, @@ -62,15 +52,6 @@ export const initValueSave: { isShowErrMsg: false, }; -export interface IFetchOrderQuote { - crypto: string; - network: string; - fiat: string; - country: string; - amount: string; - side: string; -} - export const initPreviewData = { crypto: 'ELF', network: 'ELF', // TODO 'AELF' diff --git a/packages/web-extension-did/app/web/pages/Buy/index.less b/packages/web-extension-did/app/web/pages/Buy/index.less index 8eb8a1464c..58b0ea6f0a 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.less +++ b/packages/web-extension-did/app/web/pages/Buy/index.less @@ -99,6 +99,12 @@ line-height: 16px; color: @font-14; } + .warning-text { + margin-top: 4px; + font-size: 12px; + line-height: 16px; + color: @font-8; + } } .buy-target { width: 100%; diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 1f59ef068c..64e4134229 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -16,9 +16,6 @@ import { initValueSave, MAX_UPDATE_TIME, PartialFiatType, - buySoonText, - sellSoonText, - serviceUnavailableText, } from './const'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { useCommonState, useLoading } from 'store/Provider/hooks'; @@ -39,6 +36,14 @@ import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import BigNumber from 'bignumber.js'; import CustomTipModal from 'pages/components/CustomModal'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useCheckManagerSyncState } from 'hooks/wallet'; +import { + buySoonText, + InsufficientFundsText, + sellSoonText, + serviceUnavailableText, + SynchronizingChainText, +} from '@portkey-wallet/constants/constants-ca/payment'; export default function Buy() { const { t } = useTranslation(); @@ -49,6 +54,7 @@ export default function Buy() { const updateTimerRef = useRef(); const valueSaveRef = useRef({ ...initValueSave }); const [errMsg, setErrMsg] = useState(''); + const [warningMsg, setWarningMsg] = useState(''); const [page, setPage] = useState(PaymentTypeEnum.BUY); const [rate, setRate] = useState(''); const [amount, setAmount] = useState(initCurrency); @@ -58,6 +64,7 @@ export default function Buy() { const [curFiat, setCurFiat] = useState(initFiat); const [rateUpdateTime, setRateUpdateTime] = useState(MAX_UPDATE_TIME); const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); + const checkManagerSyncState = useCheckManagerSyncState(); const disabled = useMemo(() => !!errMsg || !amount, [errMsg, amount]); const showRateText = useMemo( @@ -176,6 +183,7 @@ export default function Buy() { setReceiveCase({ fiatQuantity, rampFee, cryptoQuantity }); setRate(cryptoPrice); setErrMsg(''); + setWarningMsg(''); valueSaveRef.current.isShowErrMsg = false; if (!updateTimerRef.current) { resetTimer(); @@ -238,6 +246,7 @@ export default function Buy() { if (min && max) { if (!isValidValue({ amount, min, max })) { setErrMsgCase(); + setWarningMsg(''); stopInterval(); } else { await updateReceive(); @@ -252,6 +261,7 @@ export default function Buy() { const { min, max } = valueSaveRef.current; if (max && min && !isValidValue({ amount: v, min, max })) { setErrMsgCase(); + setWarningMsg(''); stopInterval(); return; } @@ -276,13 +286,13 @@ export default function Buy() { // Compatible with the situation where the function is turned off when the user is on the page. if (side === PaymentTypeEnum.BUY && !isBuySectionShow) { CustomTipModal({ - content: buySoonText, + content: t(buySoonText), }); return; } if (side === PaymentTypeEnum.SELL && !isSellSectionShow) { CustomTipModal({ - content: sellSoonText, + content: t(sellSoonText), }); return; } @@ -300,6 +310,7 @@ export default function Buy() { } setCurFiat(initFiat); + setWarningMsg(''); setErrMsg(''); valueSaveRef.current.isShowErrMsg = false; setReceive(''); @@ -314,7 +325,7 @@ export default function Buy() { setLoading(false); } }, - [isBuySectionShow, isSellSectionShow, refreshBuyButton, setLoading, stopInterval, updateCrypto], + [isBuySectionShow, isSellSectionShow, refreshBuyButton, setLoading, stopInterval, t, updateCrypto], ); const handleSelect = useCallback( @@ -353,7 +364,7 @@ export default function Buy() { const setInsufficientFundsMsg = useCallback(() => { stopInterval(); - setErrMsg('Insufficient funds'); + setErrMsg(InsufficientFundsText); valueSaveRef.current.isShowErrMsg = true; setReceive(''); @@ -373,6 +384,16 @@ export default function Buy() { return navigate('/'); } + const _isManagerSynced = await checkManagerSyncState('AELF'); + + if (!_isManagerSynced) { + setLoading(false); + setWarningMsg(SynchronizingChainText); + return; + } else { + setWarningMsg(''); + } + if (side === PaymentTypeEnum.SELL) { if (!currentChain) return setLoading(false); // search balance from contract @@ -411,6 +432,7 @@ export default function Buy() { }); }, [ accountTokenList, + checkManagerSyncState, currentChain, currentNetwork.walletType, navigate, @@ -478,6 +500,7 @@ export default function Buy() { handleTokenSelect={(v) => handleSelect(v, DrawerType.token)} curToken={curToken} errMsg={errMsg} + warningMsg={warningMsg} side={PaymentTypeEnum.BUY} /> )} @@ -494,6 +517,7 @@ export default function Buy() { handleCurrencySelect={(v) => handleSelect(v, DrawerType.currency)} curFiat={curFiat} errMsg={errMsg} + warningMsg={warningMsg} side={PaymentTypeEnum.SELL} /> )} @@ -524,6 +548,7 @@ export default function Buy() { receive, renderRate, t, + warningMsg, ], ); diff --git a/packages/web-extension-did/app/web/types/payment.ts b/packages/web-extension-did/app/web/types/payment.ts new file mode 100644 index 0000000000..37f4b22991 --- /dev/null +++ b/packages/web-extension-did/app/web/types/payment.ts @@ -0,0 +1,18 @@ +import { FiatType } from '@portkey-wallet/store/store-ca/payment/type'; +import { ChainId } from '@portkey/provider-types'; + +export type PartialFiatType = Partial; + +export type TokenType = { + symbol: string; + chainId: ChainId; +}; + +export interface IFetchOrderQuote { + crypto: string; + network: string; + fiat: string; + country: string; + amount: string; + side: string; +} From 5d401285ca6b430b61097008d42ecfaa6683bf44 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 6 Jul 2023 10:11:43 +0800 Subject: [PATCH 272/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20reset=20token=20?= =?UTF-8?q?=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/index.ts | 2 ++ packages/store/store-ca/tokenManagement/slice.ts | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/hooks/hooks-ca/index.ts b/packages/hooks/hooks-ca/index.ts index 9f833d2b59..5162ac9edb 100644 --- a/packages/hooks/hooks-ca/index.ts +++ b/packages/hooks/hooks-ca/index.ts @@ -8,12 +8,14 @@ import { resetActivity } from '@portkey-wallet/store/store-ca/activity/slice'; import { resetContact } from '@portkey-wallet/store/store-ca/contact/actions'; import { resetGuardiansState } from '@portkey-wallet/store/store-ca/guardians/actions'; import { resetPayment } from '@portkey-wallet/store/store-ca/payment/actions'; +import { resetTokenManagement } from '@portkey-wallet/store/store-ca/tokenManagement/slice'; export const useAppCASelector: TypedUseSelectorHook = useSelector; export function useResetStore() { const dispatch = useAppCommonDispatch(); return useCallback(() => { + dispatch(resetTokenManagement()); dispatch(resetAssets()); dispatch(resetRecent()); dispatch(resetActivity()); diff --git a/packages/store/store-ca/tokenManagement/slice.ts b/packages/store/store-ca/tokenManagement/slice.ts index c95f60bcf8..73528213d7 100644 --- a/packages/store/store-ca/tokenManagement/slice.ts +++ b/packages/store/store-ca/tokenManagement/slice.ts @@ -4,7 +4,6 @@ import { fetchAllTokenListAsync, getSymbolImagesAsync } from './action'; import { TokenItemShowType } from '@portkey-wallet/types/types-eoa/token'; const initialState: TokenState = { - // addedTokenData: {}, tokenDataShowInMarket: [], isFetching: false, skipCount: 0, @@ -23,9 +22,9 @@ export const tokenManagementSlice = createSlice({ state.tokenDataShowInMarket = []; }, resetToken: state => { - // state.addedTokenData = {}; state.tokenDataShowInMarket = []; }, + resetTokenManagement: () => initialState, }, extraReducers: builder => { builder @@ -64,6 +63,6 @@ export const tokenManagementSlice = createSlice({ }, }); -export const { clearMarketToken, resetToken } = tokenManagementSlice.actions; +export const { clearMarketToken, resetToken, resetTokenManagement } = tokenManagementSlice.actions; export default tokenManagementSlice; From 16940d8eab712fffff63b2ac4e5e8c6b38bb4337 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 6 Jul 2023 10:12:15 +0800 Subject: [PATCH 273/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20sell=20Syn?= =?UTF-8?q?chronizing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/CommonInput/index.tsx | 3 +- .../Buy/BuyHome/components/SellForm/index.tsx | 29 ++++++++++++++++++- packages/mobile-app-did/js/types/common.ts | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/CommonInput/index.tsx b/packages/mobile-app-did/js/components/CommonInput/index.tsx index 79ab7ae611..78a2fcf582 100644 --- a/packages/mobile-app-did/js/components/CommonInput/index.tsx +++ b/packages/mobile-app-did/js/components/CommonInput/index.tsx @@ -20,6 +20,7 @@ const CommonInput = forwardRef(function CommonInput(props: CommonInputProps, for labelStyle, rightIconContainerStyle, leftIconContainerStyle, + errorStyle, ...inputProps } = props; @@ -54,7 +55,7 @@ const CommonInput = forwardRef(function CommonInput(props: CommonInputProps, for labelStyle={[generalStyles.labelStyle, labelStyle]} rightIconContainerStyle={[generalStyles.rightIconContainerStyle, rightIconContainerStyle]} leftIconContainerStyle={leftIconContainerStyle} - errorStyle={[generalStyles.errorStyle]} + errorStyle={[generalStyles.errorStyle, errorStyle]} placeholder={placeholder || t('Please enter')} placeholderTextColor={defaultColors.font7} disabledInputStyle={[generalStyles.disabledInputStyle]} diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index 7cd4180635..eeec427d7b 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -38,10 +38,12 @@ import BigNumber from 'bignumber.js'; import { PaymentLimitType, PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; import CommonToast from 'components/CommonToast'; +import { useCheckManagerSyncState } from 'hooks/wallet'; export default function SellForm() { const { sellFiatList: fiatList } = usePayment(); const { refreshBuyButton } = useBuyButtonShow(); + const checkManagerSyncState = useCheckManagerSyncState(); const [fiat, setFiat] = useState( fiatList.find(item => item.currency === 'USD' && item.country === 'US'), @@ -180,6 +182,18 @@ export default function SellForm() { } try { + Loading.show(); + const _isManagerSynced = await checkManagerSyncState(chainId); + if (!_isManagerSynced) { + setAmountLocalError({ + ...INIT_HAS_ERROR, + isWarning: true, + errorMsg: 'Synchronizing on-chain account information...', + }); + Loading.hide(); + return; + } + if (ZERO.plus(amount).isLessThanOrEqualTo(DEFAULT_FEE)) { throw new Error('Insufficient funds'); } @@ -222,7 +236,19 @@ export default function SellForm() { receiveAmount: _receiveAmount, rate: _rate, }); - }, [amount, rate, receiveAmount, aelfToken, chainInfo, pin, refreshBuyButton, fiat, token, wallet]); + }, [ + amount, + rate, + receiveAmount, + aelfToken, + chainInfo, + pin, + fiat, + token, + refreshBuyButton, + checkManagerSyncState, + wallet, + ]); return ( @@ -252,6 +278,7 @@ export default function SellForm() { autoCorrect={false} keyboardType="decimal-pad" onChangeText={onAmountInput} + errorStyle={amountError.isWarning && FontStyles.font6} errorMessage={amountError.isError ? amountError.errorMsg : ''} /> diff --git a/packages/mobile-app-did/js/types/common.ts b/packages/mobile-app-did/js/types/common.ts index bcf94aba4a..0979e6fadb 100644 --- a/packages/mobile-app-did/js/types/common.ts +++ b/packages/mobile-app-did/js/types/common.ts @@ -4,6 +4,7 @@ import type { ParsedQuery } from 'query-string'; export type ErrorType = { errorMsg: string; isError: boolean; + isWarning?: boolean; }; export type SchemeParsedUrl = { From 4c780503504ed6f86fa29bc0bfd5c9823a5ffa48 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 6 Jul 2023 10:15:38 +0800 Subject: [PATCH 274/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20check?= =?UTF-8?q?=20manage=20info?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/index.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 64e4134229..2631c1b842 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -384,18 +384,19 @@ export default function Buy() { return navigate('/'); } - const _isManagerSynced = await checkManagerSyncState('AELF'); - - if (!_isManagerSynced) { - setLoading(false); - setWarningMsg(SynchronizingChainText); - return; - } else { - setWarningMsg(''); - } - if (side === PaymentTypeEnum.SELL) { if (!currentChain) return setLoading(false); + + const _isManagerSynced = await checkManagerSyncState('AELF'); + + if (!_isManagerSynced) { + setLoading(false); + setWarningMsg(SynchronizingChainText); + return; + } else { + setWarningMsg(''); + } + // search balance from contract const result = await getBalance({ rpcUrl: currentChain.endPoint, From 6d479a8a939022aaf56f1f637d1bf5005a6c0314 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 6 Jul 2023 10:39:13 +0800 Subject: [PATCH 275/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20checkQR?= =?UTF-8?q?Code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/message/index.ts | 2 +- packages/mobile-app-did/js/assets/theme/index.ts | 2 +- .../mobile-app-did/js/pages/Login/ScanLogin/index.tsx | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/api/api-did/message/index.ts b/packages/api/api-did/message/index.ts index 26984dbc29..cb01d4a85a 100644 --- a/packages/api/api-did/message/index.ts +++ b/packages/api/api-did/message/index.ts @@ -1,5 +1,5 @@ export default { sendScanLoginSuccess: '/api/app/message/scanLoginSuccess', sendScanLogin: '/api/app/message/scanLogin', - checkQRCodeExist: '/api/app/qrcode/exist', + checkQRCodeExist: '/api/app/qrcode', }; diff --git a/packages/mobile-app-did/js/assets/theme/index.ts b/packages/mobile-app-did/js/assets/theme/index.ts index 2440a57cb9..326fa82c54 100644 --- a/packages/mobile-app-did/js/assets/theme/index.ts +++ b/packages/mobile-app-did/js/assets/theme/index.ts @@ -21,7 +21,7 @@ export const defaultColors = { bg12: '#BDD2FB', bg13: '#0075FF', bg14: '#CEDDFC', - bg15: 'rgba(104,170,253,.2)', + bg15: 'rgba(104, 170, 253, .2)', bg16: '#C5CBD5', font1: '#464B53', diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index ff84b16108..73227e643c 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -49,17 +49,21 @@ export default function ScanLogin() { const onLogin = useCallback(async () => { if (!caHash || loading || !managerAddress) return; + setLoading(true); try { - setLoading(true); if (targetClientId) { const isQRCodeExist = await checkQRCodeExist(targetClientId); - if (isQRCodeExist === true) { + if (isQRCodeExist === false) { CommonToast.warn('The QR code has already been scanned by another device.'); setLoading(false); return; } } + } catch (error) { + console.log(error); + } + try { const deviceInfo = getDeviceInfoFromQR(qrExtraData, deviceType); const contract = await getCurrentCAContract(); const extraData = await extraDataEncode(deviceInfo || {}, true); From 4d043af388fe8dd5473d3afddfba961dc7451683 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 6 Jul 2023 12:07:29 +0800 Subject: [PATCH 276/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20UI=20&=20origin?= =?UTF-8?q?=20chainId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Send/components/AmountInput/TokenInput.tsx | 1 + .../app/web/pages/SendTransactions/index.tsx | 1 + .../web-extension-did/app/web/pages/Token/Custom/index.tsx | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx index 43f8da44ed..e5a235f540 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx @@ -125,6 +125,7 @@ export default function TokenInput({ if (_isManagerSynced) { setAmount(maxAmount); onChange({ amount: maxAmount, balance }); + setErrorMsg(''); } else { setErrorMsg('Synchronizing on-chain account information...'); } diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 78ca5dc464..33f346e6e9 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -135,6 +135,7 @@ export default function SendTransactions() { setIsManagerSynced(_isManagerSynced); if (_isManagerSynced) { getFee(params); + setErrMsg(''); } else { setErrMsg('Synchronizing on-chain account information...'); } diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx index fd198abed3..5b0b775e84 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx @@ -1,5 +1,5 @@ import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; -import { useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { useChainIdList, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { Button, Input, message } from 'antd'; import CustomSvg from 'components/CustomSvg'; import TitleWrapper from 'components/TitleWrapper'; @@ -24,7 +24,8 @@ export default function CustomToken() { const { t } = useTranslation(); const [errorMsg, setErrorMsg] = useState(''); const [curToken, setCurToken] = useState({}); - const [curChainId, setCurChain] = useState('AELF'); + const originChainId = useOriginChainId(); + const [curChainId, setCurChain] = useState(originChainId); const [value, setValue] = useState(''); const { isPrompt } = useCommonState(); const chainList = useChainIdList(); From abbff20952f6e8735ac3cf473f8baaf5b1790e11 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 6 Jul 2023 14:49:28 +0800 Subject: [PATCH 277/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20extension=20Opera?= =?UTF-8?q?tionTypeEnum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/GuardianItems/index.tsx | 7 +++---- .../app/web/pages/VerifierAccount/index.tsx | 11 +++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx index 963660a7ab..8d3ad296be 100644 --- a/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx +++ b/packages/web-extension-did/app/web/pages/GuardianApproval/components/GuardianItems/index.tsx @@ -50,8 +50,6 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount const operationType: OperationTypeEnum = useMemo(() => { switch (query) { - case 'login': - return OperationTypeEnum.communityRecovery; case 'guardians/add': return OperationTypeEnum.addGuardian; case 'guardians/edit': @@ -59,12 +57,13 @@ export default function GuardianItems({ disabled, item, isExpired, loginAccount case 'guardians/del': return OperationTypeEnum.deleteGuardian; default: - if (query?.indexOf('removeManage') !== -1) { + if (query && query?.indexOf('removeManage') !== -1) { return OperationTypeEnum.removeOtherManager; } - return OperationTypeEnum.unknown; + return OperationTypeEnum.communityRecovery; } }, [query]); + console.log('operationType====', operationType); const guardianSendCode = useCallback( async (item: UserGuardianItem) => { diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 8c07e45edc..63f2a16b49 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -30,7 +30,13 @@ export default function VerifierAccount() { const navigate = useNavigate(); const dispatch = useAppDispatch(); const { state } = useLocationState< - 'register' | 'login' | 'guardians/add' | 'guardians/edit' | 'guardians/del' | 'guardians/setLoginAccount' + | 'register' + | 'login' + | 'guardians/add' + | 'guardians/edit' + | 'guardians/del' + | 'guardians/setLoginAccount' + | 'removeManage' >(); const { isNotLessThan768 } = useCommonState(); const { walletInfo } = useCurrentWallet(); @@ -202,8 +208,9 @@ export default function VerifierAccount() { case 'guardians/del': return OperationTypeEnum.deleteGuardian; case 'guardians/setLoginAccount': + return OperationTypeEnum.setLoginAccount; default: - if (state?.indexOf('removeManage') !== -1) { + if (state && state?.indexOf('removeManage') !== -1) { return OperationTypeEnum.removeOtherManager; } else { return OperationTypeEnum.unknown; From d5220ac1b7ee4756715ec09e285fdd791bb0a379 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 6 Jul 2023 14:57:07 +0800 Subject: [PATCH 278/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20logout=20disconne?= =?UTF-8?q?ct=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/controllers/SWEventController.ts | 2 +- packages/web-extension-did/app/web/hooks/useLogout.ts | 2 -- .../app/web/pages/components/PermissionCheck/index.tsx | 2 ++ .../app/web/serviceWorker/ServiceWorkerInstantiate.ts | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.ts b/packages/web-extension-did/app/web/controllers/SWEventController.ts index 2c9b4897d0..25f7551069 100644 --- a/packages/web-extension-did/app/web/controllers/SWEventController.ts +++ b/packages/web-extension-did/app/web/controllers/SWEventController.ts @@ -171,7 +171,7 @@ export default class SWEventController { case resetDapp.toString(): case resetDappList.toString(): { await InternalMessage.payload(NotificationEvents.DISCONNECTED, { - data: { message: 'user logout', code: ResponseCode.USER_DENIED }, + data: { message: 'user logout', code: ResponseCode.USER_DENIED, origin: '*' }, }).send(); break; } diff --git a/packages/web-extension-did/app/web/hooks/useLogout.ts b/packages/web-extension-did/app/web/hooks/useLogout.ts index 249876c1e1..e3fd899980 100644 --- a/packages/web-extension-did/app/web/hooks/useLogout.ts +++ b/packages/web-extension-did/app/web/hooks/useLogout.ts @@ -23,7 +23,6 @@ import { useResetStore } from '@portkey-wallet/hooks/hooks-ca'; import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import { useNavigate } from 'react-router'; -import { clearLocalStorage } from 'utils/storage/chromeStorage'; import { getWalletInfo, isCurrentCaHash } from 'store/utils/getStore'; import { resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; @@ -46,7 +45,6 @@ export default function useLogOut() { dispatch(resetSettings()); dispatch(resetNetwork()); dispatch(resetLoginInfoAction()); - clearLocalStorage(); } dispatch(resetDappList(currentNetwork)); diff --git a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx index d1e63ba843..2a71e822d0 100644 --- a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx @@ -111,7 +111,9 @@ export default function PermissionCheck({ }, [currentNetwork, getPassword, pageType, walletInfo?.caInfo]); useEffect(() => { + if (location.pathname.includes('/test')) return; if (locked && !noCheckRegister && !isRegisterPage) return navigate('/unlock'); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isRegisterPage, locked, navigate, noCheckRegister]); useEffectOnce(() => { diff --git a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts index e6e86bc8b7..d903a0a951 100644 --- a/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts +++ b/packages/web-extension-did/app/web/serviceWorker/ServiceWorkerInstantiate.ts @@ -100,9 +100,6 @@ export default class ServiceWorkerInstantiate { // reset lockout timer console.log(message, 'LocalStream.watch message'); if (message.type === InternalMessageTypes.ACTIVE_LOCK_STATUS) return sendResponse(errorHandler(0)); - - const registerRes = await this.permissionController.checkCurrentNetworkOtherwiseRegister(message.type); - if (registerRes.error !== 0) return sendResponse(registerRes); // process events if (SWEventController.check(message.type, message.payload?.data)) { const payload = message.payload; @@ -112,6 +109,9 @@ export default class ServiceWorkerInstantiate { sendResponse(errorHandler(0)); return; } + const registerRes = await this.permissionController.checkCurrentNetworkOtherwiseRegister(message.type); + if (registerRes.error !== 0) return sendResponse(registerRes); + await ServiceWorkerInstantiate.checkTimingLock(); const isLocked = await this.permissionController.checkIsLockOtherwiseUnlock(message.type); if (isLocked.error !== 0) return sendResponse(isLocked); From 2642a15c3d590e893194d9c6537e30311ce22911 Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Thu, 6 Jul 2023 16:25:32 +0800 Subject: [PATCH 279/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20update=20ui?= =?UTF-8?q?=20sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/package.json | 2 +- yarn.lock | 132 ++++++++++++------------ 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 40a8de51a4..b8500e4682 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -20,7 +20,7 @@ "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", "@portkey/providers": "1.0.1-alpha.0", - "@portkey/did-ui-react": "1.0.0-alpha.6", + "@portkey/did-ui-react": "1.0.0-alpha.8", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index 4e3c1c06f4..3f98247abb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5203,14 +5203,14 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/accounts@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/accounts/-/accounts-1.0.0-alpha.6.tgz#eddde767b6140fb27dc214e0eb79d9020c189fdd" - integrity sha512-+Zc8a0umPfrJYL7NvDTFzJ3xoUkgODqsduXfn8IF5qWRGd8dky22kGYug9oOK0S9fPBe3Phr116ra8iV98Kx1g== - dependencies: - "@portkey/types" "^1.0.0-alpha.6" - "@portkey/utils" "^1.0.0-alpha.6" - "@portkey/validator" "^1.0.0-alpha.6" +"@portkey/accounts@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/accounts/-/accounts-1.0.0-alpha.8.tgz#101190172e8c4c6f44d2dca220c91b4f00bbd1fc" + integrity sha512-oduw84UzaPNsbQtNJgHMoMx0FUl4b86IGjd0jEAY0M9z3gag2M8fjYZDIbkqMcpIOXMG0tMTDhrQzQSjFhvjjQ== + dependencies: + "@portkey/types" "^1.0.0-alpha.8" + "@portkey/utils" "^1.0.0-alpha.8" + "@portkey/validator" "^1.0.0-alpha.8" aelf-sdk "^3.2.44" "@portkey/chain@^1.0.1-alpha.0": @@ -5232,24 +5232,24 @@ "@portkey/utils" "^1.0.0-alpha.5" aelf-sdk "^3.2.44" -"@portkey/contracts@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/contracts/-/contracts-1.0.0-alpha.6.tgz#79e763b988a1492cfffd019d0b39e55f09b4c947" - integrity sha512-X7sRX6vCqQL9e7ZmAOz3efzLPOp9foJZRkncPEZnAXayQNSK5+DSIKCC9lUSXqDON3pRmnVayg0KMOR0dSN9jA== +"@portkey/contracts@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/contracts/-/contracts-1.0.0-alpha.8.tgz#2d2faab25005acae50418a162e8f520b211d8e17" + integrity sha512-sNZtrQmC+ZLaOK2ZGwpbC1nlmWwVj775oWT5V/EsudBKrnldcCoZMshBFJyNj0EVoGzXjqwLFfHDP8rC4LyRkQ== dependencies: "@portkey/provider-types" "^1.0.1-alpha.0" - "@portkey/types" "^1.0.0-alpha.6" - "@portkey/utils" "^1.0.0-alpha.6" + "@portkey/types" "^1.0.0-alpha.8" + "@portkey/utils" "^1.0.0-alpha.8" aelf-sdk "^3.2.44" -"@portkey/did-ui-react@1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/did-ui-react/-/did-ui-react-1.0.0-alpha.6.tgz#96aa51a9da9268204cb42a3b0054dda6a4921f3f" - integrity sha512-yw6nxqRB2Z4bfFbOkTM3nwL710wo35wz2D1NbZ1vmbg0wgTV4TAPU2XAPZG+QqWDynYUmM3J22L592LkZghseQ== +"@portkey/did-ui-react@1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/did-ui-react/-/did-ui-react-1.0.0-alpha.8.tgz#84135ed6cdc0fdec2c1dae54f2e2897a38c14ba9" + integrity sha512-bOKf58tbw+eugNbdSwbR/bEGXtvq+Lysk93zp2MXuxnOdKJj46Wib9fTq7EghQdJK1E2TpuOJ+eLO+897srD6g== dependencies: "@matt-block/react-recaptcha-v2" "^2.0.0" - "@portkey/did" "^1.0.0-alpha.6" - "@portkey/socket" "^1.0.0-alpha.6" + "@portkey/did" "^1.0.0-alpha.8" + "@portkey/socket" "^1.0.0-alpha.8" "@rc-component/portal" "1.0.2" aelf-sdk "^3.2.40" antd "4.24.4" @@ -5264,18 +5264,18 @@ reactjs-social-login "^2.6.2" uuid "^8.3.2" -"@portkey/did@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/did/-/did-1.0.0-alpha.6.tgz#d22a6b64f59748d0e2398d27e75839f6d8587c87" - integrity sha512-vDNM7EyPDXZZv27Bo1bNRBzrCOKK+eG5tMSoSmxr962GqcWr3xAprtAmdoHnejIzEUatBaD+GV1pS3s4I8rfqA== - dependencies: - "@portkey/accounts" "^1.0.0-alpha.6" - "@portkey/contracts" "^1.0.0-alpha.6" - "@portkey/graphql" "^1.0.0-alpha.6" - "@portkey/request" "^1.0.0-alpha.6" - "@portkey/services" "^1.0.0-alpha.6" - "@portkey/types" "^1.0.0-alpha.6" - "@portkey/utils" "^1.0.0-alpha.6" +"@portkey/did@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/did/-/did-1.0.0-alpha.8.tgz#5f5c5c8dd59f0a971aa293ee51b482761c7e3f8a" + integrity sha512-5wP8t7ZXQskgSoPk2ef3hA7tHILsmhrCVmg9B/2oMILFBmQ3Woe8fNkDgT8ECUDsjE/fSlj7ytrtpxc2QSjxog== + dependencies: + "@portkey/accounts" "^1.0.0-alpha.8" + "@portkey/contracts" "^1.0.0-alpha.8" + "@portkey/graphql" "^1.0.0-alpha.8" + "@portkey/request" "^1.0.0-alpha.8" + "@portkey/services" "^1.0.0-alpha.8" + "@portkey/types" "^1.0.0-alpha.8" + "@portkey/utils" "^1.0.0-alpha.8" aelf-sdk "^3.2.44" react "^18.2.0" @@ -5289,13 +5289,13 @@ "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/graphql@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/graphql/-/graphql-1.0.0-alpha.6.tgz#bbf9cdb6f460d54e036eadbf9d9876dc5e5633f3" - integrity sha512-O5144dCMaSkP5HV8sA8Pfh8fIMwsE+EFYQXX3NpPVBgSThEDDYU3a1M+AwpC1e5f//ANMnOAIPc4QT7lZgYvpw== +"@portkey/graphql@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/graphql/-/graphql-1.0.0-alpha.8.tgz#4aa5c1d6d5fda63c15a053650aa64bf6e9b803a8" + integrity sha512-luksErVuyAlQJeYkqVofwx7vxHJuW7o0JlT5yhGWWSE4XUro8H49FTfLJ+zxkRvhU/iG69KeeStY+wuw3z1gJA== dependencies: "@apollo/client" "^3.7.3" - "@portkey/types" "^1.0.0-alpha.6" + "@portkey/types" "^1.0.0-alpha.8" graphql "^16.6.0" subscriptions-transport-ws "^0.11.0" @@ -5331,22 +5331,22 @@ lodash "^4.17.21" readable-stream "^4.4.0" -"@portkey/request@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/request/-/request-1.0.0-alpha.6.tgz#67d724d7d2363ad828c1ae323d550991b0b30828" - integrity sha512-MctgldYxpEo5TvX/hR7fa60LvvgNEwXTX+77dZ1UlTJtVwVUEZ5Tpjusks0Ao3jVt/WVe7STToq9pcAr1IGT0Q== +"@portkey/request@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/request/-/request-1.0.0-alpha.8.tgz#af86f263289966afc1200c07058610650e75d807" + integrity sha512-y6WnZGyyN6kdr9sXcCvXo27xB10j6MqIOAd1rrnCyCZewAxM2OdoK5JgAZEd8phqltCGuME6Uw5HuL1URU4Y8w== dependencies: - "@portkey/types" "^1.0.0-alpha.6" + "@portkey/types" "^1.0.0-alpha.8" query-string "^6.14.1" -"@portkey/services@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/services/-/services-1.0.0-alpha.6.tgz#c0dab0f3edef4678c9ee20ffce8b6711bed29dd6" - integrity sha512-nKdyY6BpHKcWfSfZd3yQ8Scsz5kblu2GDNhT6dV7WRamxE6P95GY2Z8gWPE1Vv8DavK5e3Io4GS9EN/kxBt+MA== +"@portkey/services@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/services/-/services-1.0.0-alpha.8.tgz#0a804ef26917e2d1c6fc4e9839e77d81670234f6" + integrity sha512-5sZ8S7ITl7Smzx769aAg+4OCufOYrAmxgtU3JMXh9iP9LDVjNmvHeAfrMY7OVFb5MtFkJOSralxkH0O16Pg1mA== dependencies: - "@portkey/graphql" "^1.0.0-alpha.6" - "@portkey/request" "^1.0.0-alpha.6" - "@portkey/types" "^1.0.0-alpha.6" + "@portkey/graphql" "^1.0.0-alpha.8" + "@portkey/request" "^1.0.0-alpha.8" + "@portkey/types" "^1.0.0-alpha.8" aelf-sdk "^3.2.44" query-string "^7.1.1" @@ -5358,13 +5358,13 @@ "@abp/signalr" "^7.0.0" "@portkey/utils" "^1.0.0-alpha.2" -"@portkey/socket@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/socket/-/socket-1.0.0-alpha.6.tgz#cbf61c5cadf49218c56c20fad6fae56522cc32b0" - integrity sha512-FP29M/aYU84v2owQZUQiFCmFeIxd0lBI+s5ysCaP88kqHqmhgqCr32hw31kNlC7KIcMvBCtza99Dj13ChSYl7g== +"@portkey/socket@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/socket/-/socket-1.0.0-alpha.8.tgz#b929ead80dd48e473bc851f98ec03f1c8801b371" + integrity sha512-SHsX1a4/LnmZcutR9cXs49EsigdIcHfqS69ezPGvPFZoZCSrqmkCi6Amj3mETRMWx3nfmgJlcBlwjlnwq14tGw== dependencies: "@abp/signalr" "^7.0.0" - "@portkey/utils" "^1.0.0-alpha.6" + "@portkey/utils" "^1.0.0-alpha.8" "@portkey/types@1.0.0-alpha.4": version "1.0.0-alpha.4" @@ -5380,10 +5380,10 @@ dependencies: "@types/elliptic" "^6.4.14" -"@portkey/types@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/types/-/types-1.0.0-alpha.6.tgz#e15f90432c785ea500f62e0c83995a185bdd162b" - integrity sha512-JCIKVWZqO5POy1cIaxIJcsIWyjT1q1eb/uMxkNhzenBQP58nXfPerbu2zx6FsA3XdwoqtzibJBYOSddA0Jr3Wg== +"@portkey/types@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/types/-/types-1.0.0-alpha.8.tgz#cdafac8f96102250ba5c8346142f018ac595331c" + integrity sha512-RTTK8+PdUbVCpArjrQxyG7CZKDzb9f4TBs/2nkmlkwhyZpm4PoWD87gcpSoYHbJN5WDMJ0N4I4N8AO6rnivn5g== dependencies: "@types/elliptic" "^6.4.14" @@ -5401,17 +5401,17 @@ dependencies: aelf-sdk "^3.2.44" -"@portkey/utils@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.6.tgz#935a959761f22f79c97f8fc1ee359fc215cf7879" - integrity sha512-akvB4Z5OO1j7J1UcklblaJa4qlUzUNS7AivHQgdRVVdA0xpn9JamDtWUsLJk3iPG3WTRwN52O5zyoDHjZs7reA== +"@portkey/utils@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.8.tgz#3fc0d2858888761008e3366c0ff8e3032caf36ea" + integrity sha512-n4E2WZ89sarg0GsQrEtMSv6mI5sq4kxPwkXMqfdjmGFvZmDr7RoDp2BHhKdGbDXBJUJgCvSsSm2r1mg72hwaIA== dependencies: aelf-sdk "^3.2.44" -"@portkey/validator@^1.0.0-alpha.6": - version "1.0.0-alpha.6" - resolved "https://registry.npmjs.org/@portkey/validator/-/validator-1.0.0-alpha.6.tgz#2e106cb66a620c1052b7713513f1245a4652b15e" - integrity sha512-/WxtzawTDp56/uP88QarEnKKECR9Ep8eBbpBFSaXWrXyMDZU4uPndvVSJN4+5EKNFB2P0FG8Tx2Ldl8Pn5F1Xw== +"@portkey/validator@^1.0.0-alpha.8": + version "1.0.0-alpha.8" + resolved "https://registry.npmjs.org/@portkey/validator/-/validator-1.0.0-alpha.8.tgz#2a0ee1465dfd2360169ffb553040f6416e21c3fb" + integrity sha512-8ePgVtXi3juMB0WYxtnRVgqAo8lr8Lglst4kUAr9mmk0wdQPgnd4vH9PuqcKw/1p7oJcff3wjxjgp0MV3g4jBw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" From 6460e24b0699bf6e9f225887568e24f94bb687ea Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 6 Jul 2023 16:49:35 +0800 Subject: [PATCH 280/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20cms=20hook?= =?UTF-8?q?=20ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/api.test.ts | 8 +- packages/hooks/hooks-ca/cms.test.ts | 104 +++++++++++++++++++++++ packages/hooks/hooks-ca/useToken.test.ts | 4 +- packages/hooks/hooks-ca/wallet.test.ts | 12 +-- test/data/cmsState.ts | 98 ++++++--------------- test/data/networkState.ts | 12 ++- 6 files changed, 151 insertions(+), 87 deletions(-) diff --git a/packages/hooks/hooks-ca/api.test.ts b/packages/hooks/hooks-ca/api.test.ts index ac7a122665..995a29163e 100644 --- a/packages/hooks/hooks-ca/api.test.ts +++ b/packages/hooks/hooks-ca/api.test.ts @@ -5,7 +5,7 @@ import * as walletHooks from './wallet'; import aes from '@portkey-wallet/utils/aes'; import AElf from 'aelf-sdk'; import * as utils from '@portkey-wallet/api/api-did/utils'; -import { NetworkInfo } from '../../../test/data/networkState'; +import { TestnetNetworkInfo } from '../../../test/data/networkState'; import { currentWallet } from '../../../test/data/chainInfo'; describe('useRefreshTokenConfig', () => { @@ -13,7 +13,7 @@ describe('useRefreshTokenConfig', () => { jest.restoreAllMocks(); }); test('no pin, and decrypt is not been called', () => { - jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(NetworkInfo); + jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(walletHooks, 'useCurrentWalletInfo').mockReturnValue(currentWallet('TESTNET').walletInfo); jest.spyOn(walletHooks, 'useOriginChainId').mockReturnValue('AELF'); jest.spyOn(aes, 'decrypt').mockReturnValue(''); @@ -26,7 +26,7 @@ describe('useRefreshTokenConfig', () => { expect(aes.decrypt).toBeCalledTimes(0); }); test('no privateKey, and getWalletByPrivateKey is not called', () => { - jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(NetworkInfo); + jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(walletHooks, 'useCurrentWalletInfo').mockReturnValue(currentWallet('TESTNET').walletInfo); jest.spyOn(walletHooks, 'useOriginChainId').mockReturnValue('AELF'); jest.spyOn(aes, 'decrypt').mockReturnValue(''); @@ -40,7 +40,7 @@ describe('useRefreshTokenConfig', () => { expect(utils.setRefreshTokenConfig).toBeCalledTimes(0); }); test('complete data, and all methods are called ', () => { - jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(NetworkInfo); + jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(walletHooks, 'useCurrentWalletInfo').mockReturnValue(currentWallet('TESTNET').walletInfo); jest.spyOn(walletHooks, 'useOriginChainId').mockReturnValue('AELF'); jest.spyOn(aes, 'decrypt').mockReturnValue('123...rgt'); diff --git a/packages/hooks/hooks-ca/cms.test.ts b/packages/hooks/hooks-ca/cms.test.ts index e69de29bb2..31181fd30e 100644 --- a/packages/hooks/hooks-ca/cms.test.ts +++ b/packages/hooks/hooks-ca/cms.test.ts @@ -0,0 +1,104 @@ +import * as networkHook from '@portkey-wallet/hooks/hooks-ca/network'; //{ useCurrentNetworkInfo, useNetworkList } +import * as cmsStore from '@portkey-wallet/store/store-ca/cms/actions'; //{ getDiscoverGroupAsync, getSocialMediaAsync } +import { useCMS, useSocialMediaList, useDiscoverGroupList } from './cms'; +import * as indexHook from '../index'; +import { MainnetNetworkInfo, TestnetNetworkInfo } from '../../../test/data/networkState'; +import { setupStore } from '../../../test/utils/setup'; +import { renderHookWithProvider } from '../../../test/utils/render'; +import { CmsState } from '../../../test/data/cmsState'; + +jest.mock('@portkey-wallet/hooks/hooks-ca/network', () => ({ + useCurrentNetworkInfo: jest.fn(), + useNetworkList: jest.fn(), +})); + +const dispatchMock = jest.fn(); +jest.spyOn(indexHook, 'useAppCommonDispatch').mockReturnValue(dispatchMock); +jest.spyOn(cmsStore, 'getSocialMediaAsync').mockReturnValue(dispatchMock); + +describe('useCMS', () => { + test('get cms data successfully', () => { + const { result } = renderHookWithProvider(useCMS, setupStore(CmsState)); + + expect(result.current).toHaveProperty('socialMediaListNetMap'); + expect(result.current).toHaveProperty('discoverGroupListNetMap'); + expect(result.current).toHaveProperty('tabMenuListNetMap'); + }); + test('failed to get cms data', () => { + const { result } = renderHookWithProvider(useCMS, setupStore({})); + + expect(result.current).toBeUndefined(); + }); +}); + +describe('useSocialMediaList', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return social media list when isInit is false', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(useSocialMediaList, setupStore(CmsState)); + + expect(result.current).toEqual(CmsState.cms.socialMediaListNetMap.TESTNET); + }); + + it('should return social media list when isInit is true', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(() => useSocialMediaList(true), setupStore(CmsState)); + + expect(result.current).toEqual(CmsState.cms.socialMediaListNetMap.TESTNET); + }); + + it('should return [] when cms state is empty', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider( + () => useSocialMediaList(true), + setupStore({ cms: { socialMediaListNetMap: [] } }), + ); + + expect(result.current).toEqual([]); + }); +}); + +describe('useDiscoverGroupList', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return social media list when isInit is false', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(useDiscoverGroupList, setupStore(CmsState)); + + expect(result.current).toEqual(CmsState.cms.discoverGroupListNetMap.TESTNET); + }); + + it('should return social media list when isInit is true', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(() => useDiscoverGroupList(true), setupStore(CmsState)); + + expect(result.current).toEqual(CmsState.cms.discoverGroupListNetMap.TESTNET); + }); + + it('should return [] when cms state is empty', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider( + () => useDiscoverGroupList(true), + setupStore({ cms: { discoverGroupListNetMap: [] } }), + ); + + expect(result.current).toEqual([]); + }); +}); diff --git a/packages/hooks/hooks-ca/useToken.test.ts b/packages/hooks/hooks-ca/useToken.test.ts index 5d4e168b0b..5c4d592248 100644 --- a/packages/hooks/hooks-ca/useToken.test.ts +++ b/packages/hooks/hooks-ca/useToken.test.ts @@ -12,7 +12,7 @@ import { TokenManagementState } from '../../../test/data/tokenManagementState'; import * as baseHooks from '../index'; import * as networkHooks from './network'; import { request } from '@portkey-wallet/api/api-did'; -import { NetworkInfo } from '../../../test/data/networkState'; +import { TestnetNetworkInfo } from '../../../test/data/networkState'; jest.mock('@portkey-wallet/store/store-ca/tokenManagement/action'); jest.mock('@portkey-wallet/api/api-did'); @@ -31,7 +31,7 @@ describe('useToken', () => { jest.spyOn(baseHooks, 'useAppCommonDispatch').mockReturnValue(() => async (call: () => void) => { return call; }); - jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(NetworkInfo); + jest.spyOn(networkHooks, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.mocked(fetchAllTokenListAsync).mockReturnValue({ list: [{}, {}], totalRecordCount: 2 } as any); jest.mocked(request.token.displayUserToken).mockResolvedValue({}); diff --git a/packages/hooks/hooks-ca/wallet.test.ts b/packages/hooks/hooks-ca/wallet.test.ts index 777f42561e..990d41ad2d 100644 --- a/packages/hooks/hooks-ca/wallet.test.ts +++ b/packages/hooks/hooks-ca/wallet.test.ts @@ -23,7 +23,7 @@ import { useCaHolderManagerInfoQuery } from '@portkey-wallet/graphql/contract/__ import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; import { ExtraDataDecodeType } from '@portkey-wallet/types/types-ca/device'; import { ApolloClient, NormalizedCacheObject } from '@apollo/client'; -import { NetworkInfo } from '../../../test/data/networkState'; +import { TestnetNetworkInfo } from '../../../test/data/networkState'; jest.mock('./network'); jest.mock('@portkey-wallet/api/api-did'); @@ -160,7 +160,7 @@ describe('useDeviceList', () => { }; }); test('useCaHolderManagerInfoQuery.data is undefined, and return deviceAmount is 0', () => { - jest.mocked(useCurrentNetworkInfo).mockReturnValue(NetworkInfo); + jest.mocked(useCurrentNetworkInfo).mockReturnValue(TestnetNetworkInfo); jest .mocked(useCaHolderManagerInfoQuery) .mockReturnValue({ data: undefined, error: undefined, refetch: jest.fn(), loading: true } as any); @@ -171,7 +171,7 @@ describe('useDeviceList', () => { }); test('complete wallet data, and return deviceAmount is 1', async () => { - jest.mocked(useCurrentNetworkInfo).mockReturnValue(NetworkInfo); + jest.mocked(useCurrentNetworkInfo).mockReturnValue(TestnetNetworkInfo); jest.mocked(useCaHolderManagerInfoQuery).mockReturnValue({ data: CA_HOLDER_MANAGER_INFO_DATA, error: undefined, @@ -189,7 +189,7 @@ describe('useDeviceList', () => { }); test('managerInfos is undefined, and return deviceAmount is 0', async () => { - jest.mocked(useCurrentNetworkInfo).mockReturnValue(NetworkInfo); + jest.mocked(useCurrentNetworkInfo).mockReturnValue(TestnetNetworkInfo); const caHolderManagerInfoData = { caHolderManagerInfo: [{ ...CA_HOLDER_MANAGER_INFO_DATA.caHolderManagerInfo[0], managerInfos: undefined }], }; @@ -210,7 +210,7 @@ describe('useDeviceList', () => { }); test('complete wallet data, and return deviceAmount is 1', async () => { - jest.mocked(useCurrentNetworkInfo).mockReturnValue(NetworkInfo); + jest.mocked(useCurrentNetworkInfo).mockReturnValue(TestnetNetworkInfo); const caHolderManagerInfoData = { caHolderManagerInfo: [ { @@ -255,7 +255,7 @@ describe('useCaAddressInfoList', () => { describe('useSetWalletName', () => { test('the useSetWalletName method was successfully called', async () => { - jest.mocked(useCurrentNetworkInfo).mockReturnValue(NetworkInfo); + jest.mocked(useCurrentNetworkInfo).mockReturnValue(TestnetNetworkInfo); jest.mocked(request.wallet.editWalletName).mockReturnValue(Promise.resolve(() => jest.fn())); const { result } = renderHookWithProvider(useSetWalletName, setupStore(COMPLETE_WALLET_STATE)); diff --git a/test/data/cmsState.ts b/test/data/cmsState.ts index 4b75efeb61..b2b0040d2f 100644 --- a/test/data/cmsState.ts +++ b/test/data/cmsState.ts @@ -6,41 +6,29 @@ export const CmsState: { cms: CMSState } = { TESTNET: [ { index: 1, - link: 'https://twitter.com/Portkey_DID', - svgUrl: { filename_disk: '6e8441f7-2260-44fa-9673-94b0352c1889.svg' }, - title: 'Follow us on Twitter', - }, - { - index: 1, - link: 'https://t.me/Portkey_Official_Group', - svgUrl: { filename_disk: '54aefae4-1559-425d-af8b-c9a2752a9e4c.svg' }, - title: 'Join us on Telegram', + link: 'https://twitter.com', + svgUrl: { filename_disk: '1889.svg' }, + title: 'Follow us on Twitter test', }, { index: 2, - link: 'https://discord.com/invite/EUBq3rHQhr', - svgUrl: { filename_disk: '22f51bce-6836-4815-b37b-88933254c7c2.svg' }, - title: 'Join us on Discord', + link: 'https://t.me', + svgUrl: { filename_disk: '9e4c.svg' }, + title: 'Join us on Telegram test', }, ], MAIN: [ { index: 1, - link: 'https://twitter.com/Portkey_DID', - svgUrl: { filename_disk: '7fd9e50a-961a-46d4-a4b3-f8bcc66a2777.svg' }, - title: 'Follow us on Twitter', - }, - { - index: 2, - link: 'https://discord.com/invite/EUBq3rHQhr', - svgUrl: { filename_disk: 'fd871c3e-155a-4e2a-92bd-e21b24f92d3a.svg' }, - title: 'Join us on Discord', + link: 'https://twitter.com', + svgUrl: { filename_disk: '2777.svg' }, + title: 'Follow us on Twitter main', }, { index: 3, - link: 'https://t.me/Portkey_Official_Group', - svgUrl: { filename_disk: 'd77dca95-7cae-455b-891f-85e6785779e6.svg' }, - title: 'Join us on Telegram', + link: 'https://t.me', + svgUrl: { filename_disk: '79e6.svg' }, + title: 'Join us on Telegram main', }, ], }, @@ -54,40 +42,18 @@ export const CmsState: { cms: CMSState } = { { id: '1', index: 1, - title: 'GAME111wdsadasdsadgfsgfsfaddaddasdasdasasdadasdsadasdsdsfdafdaadfdas', - description: 'https://www.baidu.com', - url: 'https://www.baidu.com', - imgUrl: { filename_disk: '19f90253-7d3c-4fca-8fd0-780d40818698.png' }, + title: 'mockTitle1', + description: 'mockDescription1', + url: 'http://xxx', + imgUrl: { filename_disk: '8698.png' }, }, { id: '5', index: 1, - title: '不删档bingogame', - description: '不删档bingogame', - url: 'https://bingogame-test.portkey.finance', - imgUrl: { filename_disk: '41c87959-be1a-44ce-8ff2-fd891e8232a4.png' }, - }, - { - id: '8', - index: 1, - title: 'http', - description: '啊大赛盛大', - url: 'http://192.168.11.160:3000', - }, - { - id: '7', - index: 6, - title: '自己的local', - description: 'http://192.168.10.139:3000/', - url: 'http://192.168.10.139:3000/', - }, - { - id: '2', - index: 12, - title: 'Game2', - description: 'Game2Game2Game2Game2Game2Game2Game2Game2', - url: '', - imgUrl: { filename_disk: '91a87175-7270-4eb5-b38d-b516ca6676e5.png' }, + title: 'mockTitle5', + description: 'mockDescription5', + url: 'http://xxx', + imgUrl: { filename_disk: '32a4.png' }, }, ], }, @@ -99,26 +65,10 @@ export const CmsState: { cms: CMSState } = { { id: '3', index: 1, - title: 'Dapp1', - description: 'http://192.168.11.79:3000', - url: 'http://192.168.11.79:3000', - imgUrl: { filename_disk: 'ddd16436-0939-4676-a01f-86ea4c1d187f.png' }, - }, - { - id: '6', - index: 3, - title: 'bingogamel33', - description: 'http://192.168.66.240:3000/', - url: 'http://192.168.66.240:3000/', - imgUrl: { filename_disk: '8bfde757-c2e9-43e8-8bb5-ff18beb7d5ed.png' }, - }, - { - id: '4', - index: 6, - title: 'Dapp2', - description: 'Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2', - url: 'Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2Dapp2', - imgUrl: { filename_disk: '58b740f5-304f-4c6a-90c3-98d928132dd3.png' }, + title: 'mockTitle3', + description: 'mockDescription3', + url: 'http://xxx', + imgUrl: { filename_disk: '187f.png' }, }, ], }, diff --git a/test/data/networkState.ts b/test/data/networkState.ts index afbcb095ff..722550ed2c 100644 --- a/test/data/networkState.ts +++ b/test/data/networkState.ts @@ -1,6 +1,6 @@ import { NetworkType, ChainType } from '@portkey-wallet/types'; -export const NetworkInfo = { +export const TestnetNetworkInfo = { apiUrl: 'https://localhost', connectUrl: 'https://localhost', graphqlUrl: 'https://localhost/graphql', @@ -9,3 +9,13 @@ export const NetworkInfo = { networkType: 'TESTNET' as NetworkType, walletType: 'aelf' as ChainType, }; + +export const MainnetNetworkInfo = { + apiUrl: 'https://localhost/main', + connectUrl: 'https://localhost/main', + graphqlUrl: 'https://localhost/graphql/main', + isActive: true, + name: 'aelf Mainnet', + networkType: 'MAINNET' as NetworkType, + walletType: 'aelf' as ChainType, +}; From e980845ebba224875cf1c943b29f9a5ec45a517b Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 6 Jul 2023 17:51:38 +0800 Subject: [PATCH 281/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20hide=20sacn=20sta?= =?UTF-8?q?tus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ScanCard/index.tsx | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx index b74a788fcb..53557621e0 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/components/ScanCard/index.tsx @@ -16,8 +16,8 @@ import { setCAInfoType, setOriginChainId } from '@portkey-wallet/store/store-ca/ import { useCheckManager } from 'hooks/useLogout'; import { message } from 'antd'; import './index.less'; -import didSignalr from '@portkey-wallet/socket/socket-did'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +// import didSignalr from '@portkey-wallet/socket/socket-did'; +// import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { randomId } from '@portkey-wallet/utils'; export default function ScanCard() { @@ -29,8 +29,8 @@ export default function ScanCard() { const checkManager = useCheckManager(); const { passwordSeed: pin } = useUserInfo(); const caWallet = useIntervalQueryCAInfoByAddress(currentNetwork, newWallet?.address, checkManager); - const [isWaitingAuth, setIsWaitingAuth] = useState(); - const networkItem = useCurrentNetworkInfo(); + // const [isWaitingAuth, setIsWaitingAuth] = useState(); + // const networkItem = useCurrentNetworkInfo(); const generateKeystore = useCallback(() => { try { @@ -67,21 +67,21 @@ export default function ScanCard() { }, [currentNetwork, deviceInfo, newWallet]); // Listen whether the user is authorized - useEffect(() => { - try { - const data: LoginQRData = JSON.parse(qrData); - if (!data?.id) return; - const clientId = `${data.address}_${data.id}`; - didSignalr.onScanLogin(() => { - setIsWaitingAuth(true); - }); - didSignalr.doOpen({ url: `${networkItem.apiUrl}/ca`, clientId }).catch((error) => { - console.warn('Socket:', error); - }); - } catch (error) { - console.warn('Socket:', error); - } - }, [networkItem.apiUrl, qrData]); + // useEffect(() => { + // try { + // const data: LoginQRData = JSON.parse(qrData); + // if (!data?.id) return; + // const clientId = `${data.address}_${data.id}`; + // didSignalr.onScanLogin(() => { + // setIsWaitingAuth(true); + // }); + // didSignalr.doOpen({ url: `${networkItem.apiUrl}/ca`, clientId }).catch((error) => { + // console.warn('Socket:', error); + // }); + // } catch (error) { + // console.warn('Socket:', error); + // } + // }, [networkItem.apiUrl, qrData]); useEffect(() => { const { caInfo, originChainId } = caWallet || {}; @@ -110,7 +110,7 @@ export default function ScanCard() {
    } onBack={() => navigate('/register/start')} qrData={qrData} From 92a771dc5583fbe57cbab50ca7225a09cf04d23b Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 6 Jul 2023 17:58:42 +0800 Subject: [PATCH 282/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20cms=20hook?= =?UTF-8?q?=20ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.test.ts | 139 ++++++++++++++++++++++++++-- test/data/cmsState.ts | 10 ++ test/data/networkState.ts | 2 +- 3 files changed, 140 insertions(+), 11 deletions(-) diff --git a/packages/hooks/hooks-ca/cms.test.ts b/packages/hooks/hooks-ca/cms.test.ts index 31181fd30e..0b5570f513 100644 --- a/packages/hooks/hooks-ca/cms.test.ts +++ b/packages/hooks/hooks-ca/cms.test.ts @@ -1,19 +1,15 @@ import * as networkHook from '@portkey-wallet/hooks/hooks-ca/network'; //{ useCurrentNetworkInfo, useNetworkList } import * as cmsStore from '@portkey-wallet/store/store-ca/cms/actions'; //{ getDiscoverGroupAsync, getSocialMediaAsync } -import { useCMS, useSocialMediaList, useDiscoverGroupList } from './cms'; +import { useCMS, useSocialMediaList, useDiscoverGroupList, useBuyButton, useBuyButtonShow } from './cms'; import * as indexHook from '../index'; import { MainnetNetworkInfo, TestnetNetworkInfo } from '../../../test/data/networkState'; import { setupStore } from '../../../test/utils/setup'; import { renderHookWithProvider } from '../../../test/utils/render'; import { CmsState } from '../../../test/data/cmsState'; -jest.mock('@portkey-wallet/hooks/hooks-ca/network', () => ({ - useCurrentNetworkInfo: jest.fn(), - useNetworkList: jest.fn(), -})); - const dispatchMock = jest.fn(); jest.spyOn(indexHook, 'useAppCommonDispatch').mockReturnValue(dispatchMock); +jest.spyOn(cmsStore, 'getDiscoverGroupAsync').mockReturnValue(dispatchMock); jest.spyOn(cmsStore, 'getSocialMediaAsync').mockReturnValue(dispatchMock); describe('useCMS', () => { @@ -34,6 +30,7 @@ describe('useCMS', () => { describe('useSocialMediaList', () => { beforeEach(() => { jest.clearAllMocks(); + jest.restoreAllMocks(); }); it('should return social media list when isInit is false', () => { @@ -54,7 +51,7 @@ describe('useSocialMediaList', () => { expect(result.current).toEqual(CmsState.cms.socialMediaListNetMap.TESTNET); }); - it('should return [] when cms state is empty', () => { + it('should return [] when cms.socialMediaListNetMap state is empty', () => { jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); @@ -70,9 +67,10 @@ describe('useSocialMediaList', () => { describe('useDiscoverGroupList', () => { beforeEach(() => { jest.clearAllMocks(); + jest.restoreAllMocks(); }); - it('should return social media list when isInit is false', () => { + it('should return discover group list when isInit is false', () => { jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); @@ -81,7 +79,7 @@ describe('useDiscoverGroupList', () => { expect(result.current).toEqual(CmsState.cms.discoverGroupListNetMap.TESTNET); }); - it('should return social media list when isInit is true', () => { + it('should return discover group list when isInit is true', () => { jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); @@ -90,7 +88,7 @@ describe('useDiscoverGroupList', () => { expect(result.current).toEqual(CmsState.cms.discoverGroupListNetMap.TESTNET); }); - it('should return [] when cms state is empty', () => { + it('should return [] when cms.discoverGroupListNetMap state is empty', () => { jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); @@ -102,3 +100,124 @@ describe('useDiscoverGroupList', () => { expect(result.current).toEqual([]); }); }); + +describe('useBuyButton', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it('should return buy button info when isInit is false', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(useBuyButton, setupStore(CmsState)); + + expect(result.current).toEqual(CmsState.cms.buyButtonNetMap?.TESTNET); + }); + + it('should return buy button info when isInit is true', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(() => useBuyButton(true), setupStore(CmsState)); + + expect(result.current).toEqual(CmsState.cms.buyButtonNetMap?.TESTNET); + }); + + it('should return undefined when cms.buyButtonNetMap state is empty', () => { + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + + const { result } = renderHookWithProvider(() => useBuyButton(true), setupStore({ cms: { buyButtonNetMap: {} } })); + + expect(result.current).toBeUndefined(); + }); +}); + +describe('useBuyButtonShow', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it('should return buy button info when testnet', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(false); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + + const { result } = renderHookWithProvider(useBuyButtonShow, setupStore(CmsState)); + + expect(result.current).toHaveProperty('isBuyButtonShow', false); + expect(result.current).toHaveProperty('isBuySectionShow', false); + expect(result.current).toHaveProperty('isSellSectionShow', false); + expect(result.current).toHaveProperty('refreshBuyButton'); + + const res = await result.current?.refreshBuyButton(); + + jest.spyOn(cmsStore, 'getBuyButtonAsync').mockReturnValue(() => { + return { + payload: { + buyButtonNetMap: CmsState.cms.buyButtonNetMap, + }, + } as any; + }); + + expect(res).toHaveProperty('isBuySectionShow', false); + expect(res).toHaveProperty('isSellSectionShow', false); + }); + + it('should return buy button info when opened buy on mainnet', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(true); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(MainnetNetworkInfo); + + const { result } = renderHookWithProvider(useBuyButtonShow, setupStore(CmsState)); + + expect(result.current).toHaveProperty('isBuyButtonShow', true); + expect(result.current).toHaveProperty('isBuySectionShow', true); + expect(result.current).toHaveProperty('isSellSectionShow', true); + expect(result.current).toHaveProperty('refreshBuyButton'); + + jest.spyOn(cmsStore, 'getBuyButtonAsync').mockReturnValue(() => { + return { + payload: { + buyButtonNetMap: CmsState.cms.buyButtonNetMap, + }, + } as any; + }); + + const res = await result.current?.refreshBuyButton(); + + expect(res).toHaveProperty('isBuySectionShow', true); + expect(res).toHaveProperty('isSellSectionShow', true); + }); + + it('should return error buy button info when closed buy on mainnet ', async () => { + jest.spyOn(networkHook, 'useIsMainnet').mockReturnValue(true); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(MainnetNetworkInfo); + + const buyButtonNetMap = { + MAIN: { + isBuySectionShow: false, + isSellSectionShow: false, + }, + TESTNET: { + isBuySectionShow: false, + isSellSectionShow: false, + }, + }; + + const { result } = renderHookWithProvider(useBuyButtonShow, setupStore({ cms: { buyButtonNetMap } })); + + expect(result.current).toHaveProperty('isBuyButtonShow', false); + expect(result.current).toHaveProperty('isBuySectionShow', false); + expect(result.current).toHaveProperty('isSellSectionShow', false); + expect(result.current).toHaveProperty('refreshBuyButton'); + + jest.spyOn(cmsStore, 'getBuyButtonAsync').mockReturnValue(dispatchMock); + + const res = await result.current?.refreshBuyButton(); + + expect(res).toHaveProperty('isBuySectionShow', false); + expect(res).toHaveProperty('isSellSectionShow', false); + }); +}); diff --git a/test/data/cmsState.ts b/test/data/cmsState.ts index b2b0040d2f..3fd39a2cfd 100644 --- a/test/data/cmsState.ts +++ b/test/data/cmsState.ts @@ -75,5 +75,15 @@ export const CmsState: { cms: CMSState } = { ], }, tabMenuListNetMap: {}, + buyButtonNetMap: { + MAIN: { + isBuySectionShow: true, + isSellSectionShow: true, + }, + TESTNET: { + isBuySectionShow: false, + isSellSectionShow: false, + }, + }, }, }; diff --git a/test/data/networkState.ts b/test/data/networkState.ts index 722550ed2c..5b11fdfe27 100644 --- a/test/data/networkState.ts +++ b/test/data/networkState.ts @@ -16,6 +16,6 @@ export const MainnetNetworkInfo = { graphqlUrl: 'https://localhost/graphql/main', isActive: true, name: 'aelf Mainnet', - networkType: 'MAINNET' as NetworkType, + networkType: 'MAIN' as NetworkType, walletType: 'aelf' as ChainType, }; From d403d74bdedc7a92e23d483118e6e932fee134da Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 6 Jul 2023 18:33:21 +0800 Subject: [PATCH 283/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20udpate=20sleep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Token/Manage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx index ad3ccd8e0c..96b3a0adfe 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx @@ -102,7 +102,7 @@ export default function AddToken() { isDisplay: !item.isAdded, }, }); - sleep(800); + await sleep(1000); if (!filterWord) { await appDispatch(fetchAllTokenListAsync({ chainIdArray })); } else { From 8df5032d50155d5ef7340f9a9a4c06fcce3de1d6 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 6 Jul 2023 18:43:56 +0800 Subject: [PATCH 284/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20remove=20scanQRC?= =?UTF-8?q?ode=20waiting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Login/ScanLogin/index.tsx | 56 +++++++++---------- .../js/pages/Login/components/QRCode.tsx | 14 ++--- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index 73227e643c..9809020fec 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import PageContainer from 'components/PageContainer'; import Svg from 'components/Svg'; import { pTd } from 'utils/unit'; @@ -19,8 +19,6 @@ import { addManager } from 'utils/wallet'; import { extraDataEncode, getDeviceInfoFromQR } from '@portkey-wallet/utils/device'; import socket from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; -import useEffectOnce from 'hooks/useEffectOnce'; -import { checkQRCodeExist } from '@portkey-wallet/api/api-did/message/utils'; const ScrollViewProps = { disabled: true }; @@ -32,36 +30,36 @@ export default function ScanLogin() { const [loading, setLoading] = useState(); const getCurrentCAContract = useGetCurrentCAContract(); - const targetClientId = useMemo(() => (id ? `${managerAddress}_${id}` : undefined), [managerAddress, id]); + // const targetClientId = useMemo(() => (id ? `${managerAddress}_${id}` : undefined), [managerAddress, id]); - useEffectOnce(() => { - if (!targetClientId) return; - try { - request.message.sendScanLogin({ - params: { - targetClientId, - }, - }); - } catch (error) { - console.log('sendScanLogin: error', error); - } - }); + // useEffectOnce(() => { + // if (!targetClientId) return; + // try { + // request.message.sendScanLogin({ + // params: { + // targetClientId, + // }, + // }); + // } catch (error) { + // console.log('sendScanLogin: error', error); + // } + // }); const onLogin = useCallback(async () => { if (!caHash || loading || !managerAddress) return; setLoading(true); - try { - if (targetClientId) { - const isQRCodeExist = await checkQRCodeExist(targetClientId); - if (isQRCodeExist === false) { - CommonToast.warn('The QR code has already been scanned by another device.'); - setLoading(false); - return; - } - } - } catch (error) { - console.log(error); - } + // try { + // if (targetClientId) { + // const isQRCodeExist = await checkQRCodeExist(targetClientId); + // if (isQRCodeExist === false) { + // CommonToast.warn('The QR code has already been scanned by another device.'); + // setLoading(false); + // return; + // } + // } + // } catch (error) { + // console.log(error); + // } try { const deviceInfo = getDeviceInfoFromQR(qrExtraData, deviceType); @@ -78,7 +76,7 @@ export default function ScanLogin() { CommonToast.failError(error); } setLoading(false); - }, [caHash, loading, managerAddress, targetClientId, qrExtraData, deviceType, getCurrentCAContract, address]); + }, [caHash, loading, managerAddress, qrExtraData, deviceType, getCurrentCAContract, address]); return ( JSON.stringify(qrData), [qrData]); - const clientId = useMemo(() => (qrData.id ? `${qrData.address}_${qrData.id}` : undefined), [qrData]); - const isScanQRCode = useIsScanQRCode(clientId); + // const clientId = useMemo(() => (qrData.id ? `${qrData.address}_${qrData.id}` : undefined), [qrData]); + // const isScanQRCode = useIsScanQRCode(clientId); return ( @@ -142,12 +140,12 @@ export default function QRCode({ setLoginType }: { setLoginType: (type: PageLogi - {isScanQRCode && ( + {/* {isScanQRCode && ( Waiting for authorization.... - )} + )} */} From bab83574a49a960131f64aadd88af119a3bfee57 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 6 Jul 2023 19:11:04 +0800 Subject: [PATCH 285/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20dapp=20hoo?= =?UTF-8?q?k=20ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/dapp.test.ts | 49 ++++++++++++++++++++++++++++ test/data/dappState.ts | 22 +++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 packages/hooks/hooks-ca/dapp.test.ts create mode 100644 test/data/dappState.ts diff --git a/packages/hooks/hooks-ca/dapp.test.ts b/packages/hooks/hooks-ca/dapp.test.ts new file mode 100644 index 0000000000..838893fade --- /dev/null +++ b/packages/hooks/hooks-ca/dapp.test.ts @@ -0,0 +1,49 @@ +import { DappState } from '../../../test/data/dappState'; +import { renderHookWithProvider } from '../../../test/utils/render'; +import { setupStore } from '../../../test/utils/setup'; +import { useDapp, useCurrentDappList } from './dapp'; +import * as walletHook from './wallet'; + +describe('useDapp', () => { + test('get assets data successfully', () => { + const { result } = renderHookWithProvider(useDapp, setupStore(DappState)); + + expect(result.current).toEqual(DappState.dapp); + }); + test('failed to get assets data', () => { + const { result } = renderHookWithProvider(useDapp, setupStore({})); + + expect(result.current).toBeUndefined(); + }); +}); + +describe('useCurrentDappList', () => { + beforeEach(() => { + jest.restoreAllMocks(); + }); + + test('get assets data successfully', () => { + jest.spyOn(walletHook, 'useWallet').mockReturnValue({ + currentNetwork: 'TESTNET', + walletAvatar: '', + walletType: 'aelf', + walletName: '', + chainList: [], + }); + const { result } = renderHookWithProvider(useCurrentDappList, setupStore(DappState)); + + expect(result.current).toEqual(DappState.dapp.dappMap.TESTNET); + }); + test('failed to get assets data', () => { + jest.spyOn(walletHook, 'useWallet').mockReturnValue({ + currentNetwork: 'MAIN', + walletAvatar: '', + walletType: 'aelf', + walletName: '', + chainList: [], + }); + const { result } = renderHookWithProvider(useCurrentDappList, setupStore(DappState)); + + expect(result.current).toEqual(DappState.dapp.dappMap.MAIN); + }); +}); diff --git a/test/data/dappState.ts b/test/data/dappState.ts new file mode 100644 index 0000000000..ea31a87c12 --- /dev/null +++ b/test/data/dappState.ts @@ -0,0 +1,22 @@ +import { IDappStoreState } from '@portkey-wallet/store/store-ca/dapp/type'; + +export const DappState: { dapp: IDappStoreState } = { + dapp: { + dappMap: { + MAIN: [ + { + origin: 'href', + name: 'browser1', + icon: 'https://xxx/main', + }, + ], + TESTNET: [ + { + origin: 'href', + name: 'browser2', + icon: 'https://xxx/test', + }, + ], + }, + }, +}; From 156172dfaf6cfc4e9e82c7d1792c9276b5f99b6e Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 7 Jul 2023 11:06:33 +0800 Subject: [PATCH 286/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20checkQRCod?= =?UTF-8?q?eExist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Login/ScanLogin/index.tsx | 56 ++++++++++--------- .../js/pages/Login/components/QRCode.tsx | 2 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index 9809020fec..73227e643c 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import PageContainer from 'components/PageContainer'; import Svg from 'components/Svg'; import { pTd } from 'utils/unit'; @@ -19,6 +19,8 @@ import { addManager } from 'utils/wallet'; import { extraDataEncode, getDeviceInfoFromQR } from '@portkey-wallet/utils/device'; import socket from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; +import useEffectOnce from 'hooks/useEffectOnce'; +import { checkQRCodeExist } from '@portkey-wallet/api/api-did/message/utils'; const ScrollViewProps = { disabled: true }; @@ -30,36 +32,36 @@ export default function ScanLogin() { const [loading, setLoading] = useState(); const getCurrentCAContract = useGetCurrentCAContract(); - // const targetClientId = useMemo(() => (id ? `${managerAddress}_${id}` : undefined), [managerAddress, id]); + const targetClientId = useMemo(() => (id ? `${managerAddress}_${id}` : undefined), [managerAddress, id]); - // useEffectOnce(() => { - // if (!targetClientId) return; - // try { - // request.message.sendScanLogin({ - // params: { - // targetClientId, - // }, - // }); - // } catch (error) { - // console.log('sendScanLogin: error', error); - // } - // }); + useEffectOnce(() => { + if (!targetClientId) return; + try { + request.message.sendScanLogin({ + params: { + targetClientId, + }, + }); + } catch (error) { + console.log('sendScanLogin: error', error); + } + }); const onLogin = useCallback(async () => { if (!caHash || loading || !managerAddress) return; setLoading(true); - // try { - // if (targetClientId) { - // const isQRCodeExist = await checkQRCodeExist(targetClientId); - // if (isQRCodeExist === false) { - // CommonToast.warn('The QR code has already been scanned by another device.'); - // setLoading(false); - // return; - // } - // } - // } catch (error) { - // console.log(error); - // } + try { + if (targetClientId) { + const isQRCodeExist = await checkQRCodeExist(targetClientId); + if (isQRCodeExist === false) { + CommonToast.warn('The QR code has already been scanned by another device.'); + setLoading(false); + return; + } + } + } catch (error) { + console.log(error); + } try { const deviceInfo = getDeviceInfoFromQR(qrExtraData, deviceType); @@ -76,7 +78,7 @@ export default function ScanLogin() { CommonToast.failError(error); } setLoading(false); - }, [caHash, loading, managerAddress, qrExtraData, deviceType, getCurrentCAContract, address]); + }, [caHash, loading, managerAddress, targetClientId, qrExtraData, deviceType, getCurrentCAContract, address]); return ( Date: Fri, 7 Jul 2023 11:09:57 +0800 Subject: [PATCH 287/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20remove=20sendSca?= =?UTF-8?q?nLogin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Login/ScanLogin/index.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx index 73227e643c..270ec317c0 100644 --- a/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx +++ b/packages/mobile-app-did/js/pages/Login/ScanLogin/index.tsx @@ -19,7 +19,6 @@ import { addManager } from 'utils/wallet'; import { extraDataEncode, getDeviceInfoFromQR } from '@portkey-wallet/utils/device'; import socket from '@portkey-wallet/socket/socket-did'; import { request } from '@portkey-wallet/api/api-did'; -import useEffectOnce from 'hooks/useEffectOnce'; import { checkQRCodeExist } from '@portkey-wallet/api/api-did/message/utils'; const ScrollViewProps = { disabled: true }; @@ -34,18 +33,18 @@ export default function ScanLogin() { const targetClientId = useMemo(() => (id ? `${managerAddress}_${id}` : undefined), [managerAddress, id]); - useEffectOnce(() => { - if (!targetClientId) return; - try { - request.message.sendScanLogin({ - params: { - targetClientId, - }, - }); - } catch (error) { - console.log('sendScanLogin: error', error); - } - }); + // useEffectOnce(() => { + // if (!targetClientId) return; + // try { + // request.message.sendScanLogin({ + // params: { + // targetClientId, + // }, + // }); + // } catch (error) { + // console.log('sendScanLogin: error', error); + // } + // }); const onLogin = useCallback(async () => { if (!caHash || loading || !managerAddress) return; From 2f3612e2222a8ed4d9e3946a6d03cebdd274571b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 7 Jul 2023 11:45:24 +0800 Subject: [PATCH 288/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20filter?= =?UTF-8?q?=20func=20#1164479?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index ad766c437d..24ab45fecc 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -70,7 +70,7 @@ export default function DiscoverSearch() { onDiscoverJump(getHost(prefixUrlWithProtocol(newValue)), prefixUrlWithProtocol(newValue)); } else { // else search in Discover list - const filterList = flatList.filter(item => item.title.replace(/\s+/g, '').includes(newValue)); + const filterList = flatList.filter(item => item.title.replace(/\s+/g, '').toLocaleLowerCase().includes(newValue)); setFilteredDiscoverList(filterList); setShowRecord(false); } From f02a035f207ee9b7fc5edc4e9809fc0ac53a02d5 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 7 Jul 2023 14:23:23 +0800 Subject: [PATCH 289/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20UI=20&=20dapp=20c?= =?UTF-8?q?onnect=20lock=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/assets/theme/color.less | 1 + .../web-extension-did/app/web/pages/Token/Custom/index.less | 4 ++++ .../app/web/pages/components/PermissionCheck/index.tsx | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/assets/theme/color.less b/packages/web-extension-did/app/web/assets/theme/color.less index da524a0a3b..67607d38e7 100644 --- a/packages/web-extension-did/app/web/assets/theme/color.less +++ b/packages/web-extension-did/app/web/assets/theme/color.less @@ -56,6 +56,7 @@ @bg-19: #515a62; @bg-20: #f2f4f6; @bg-21: #00AB34; +@bg-22: #F0F1F4; @hover1: @border-4; @hover2: @border-3; diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.less b/packages/web-extension-did/app/web/pages/Token/Custom/index.less index 9ff983f979..cd0573382d 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.less +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.less @@ -60,6 +60,10 @@ .label { margin-bottom: 8px; } + .@{app-prefix}-input[disabled] { + color: @font-1; + background-color: @bg-22; + } .err-msg { margin: 4px; diff --git a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx index 2a71e822d0..24f5b8a4a9 100644 --- a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx @@ -71,7 +71,8 @@ export default function PermissionCheck({ const detail = (res as any)?.data; if (detail?.registerStatus) { detail?.privateKey && dispatch(setPasswordSeed(detail.privateKey)); - !detail?.privateKey && navigate('/unlock'); + // navigate to unlock expect dapp connect + location.pathname !== '/permission' && !detail?.privateKey && navigate('/unlock'); } else { InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); } From c3bd82df2350219bc11d4fdaf2e8ec2fbb9c5c7a Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 7 Jul 2023 14:36:12 +0800 Subject: [PATCH 290/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20delete=20buy=20w?= =?UTF-8?q?arning=20tip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Buy/components/{BuyFrom => BuyForm}/index.tsx | 4 +--- .../Buy/components/{SellFrom => SellForm}/index.tsx | 2 +- packages/web-extension-did/app/web/pages/Buy/index.tsx | 9 ++++----- 3 files changed, 6 insertions(+), 9 deletions(-) rename packages/web-extension-did/app/web/pages/Buy/components/{BuyFrom => BuyForm}/index.tsx (94%) rename packages/web-extension-did/app/web/pages/Buy/components/{SellFrom => SellForm}/index.tsx (97%) diff --git a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/BuyForm/index.tsx similarity index 94% rename from packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx rename to packages/web-extension-did/app/web/pages/Buy/components/BuyForm/index.tsx index 9c5cfc4478..306e58dab3 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/BuyFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/BuyForm/index.tsx @@ -19,7 +19,7 @@ export interface IBuyOrSellFromProps { curToken: ICurToken; errMsg: string; - warningMsg: string; + warningMsg?: string; side: PaymentTypeEnum; } @@ -37,7 +37,6 @@ export default function BuyFrom({ curToken, errMsg, - warningMsg, side, }: IBuyOrSellFromProps) { const { t } = useTranslation(); @@ -55,7 +54,6 @@ export default function BuyFrom({ onSelect={handleCurrencySelect} /> {!!errMsg &&
    {t(errMsg)}
    } - {!!warningMsg &&
    {t(warningMsg)}
    }
    {`I will receive≈`}
    diff --git a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx b/packages/web-extension-did/app/web/pages/Buy/components/SellForm/index.tsx similarity index 97% rename from packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx rename to packages/web-extension-did/app/web/pages/Buy/components/SellForm/index.tsx index 26a30288a3..1fcf08d6a1 100644 --- a/packages/web-extension-did/app/web/pages/Buy/components/SellFrom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/components/SellForm/index.tsx @@ -1,6 +1,6 @@ import CurrencyInput from '../CurrencyInput'; import TokenInput from '../TokenInput'; -import { IBuyOrSellFromProps } from '../BuyFrom'; +import { IBuyOrSellFromProps } from '../BuyForm'; import { useTranslation } from 'react-i18next'; export default function SellFrom({ diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 2631c1b842..8b520efad5 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -29,8 +29,8 @@ import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; -import BuyFrom from './components/BuyFrom'; -import SellFrom from './components/SellFrom'; +import BuyForm from './components/BuyForm'; +import SellForm from './components/SellForm'; import { useEffectOnce } from 'react-use'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import BigNumber from 'bignumber.js'; @@ -489,7 +489,7 @@ export default function Buy() {
    {page === PaymentTypeEnum.BUY && ( - handleSelect(v, DrawerType.token)} curToken={curToken} errMsg={errMsg} - warningMsg={warningMsg} side={PaymentTypeEnum.BUY} /> )} {page === PaymentTypeEnum.SELL && ( - Date: Fri, 7 Jul 2023 14:50:32 +0800 Subject: [PATCH 291/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20login=20text=20ch?= =?UTF-8?q?ange?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Login/components/Email.tsx | 9 +++++---- .../mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Login/components/Email.tsx b/packages/mobile-app-did/js/pages/Login/components/Email.tsx index 025efb8ff2..21398e0ef5 100644 --- a/packages/mobile-app-did/js/pages/Login/components/Email.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/Email.tsx @@ -71,11 +71,12 @@ export default function Email({ }, []), ); - useEffect(() => { - return () => { + useEffect( + () => () => { if (timerRef.current) clearTimeout(timerRef.current); - }; - }, []); + }, + [], + ); return ( diff --git a/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx b/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx index 8015242d8a..23938ddea4 100644 --- a/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx +++ b/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx @@ -72,7 +72,7 @@ export default function SetBiometrics() { } if (managerInfo) { timer.current?.remove(); - Loading.show({ text: t('Creating address on the chain...') }); + Loading.show({ text: t('Initiating social recovery') }); timer.current = onIntervalGetResult({ managerInfo, onPass: (info: CAInfo) => { From 00c0da30cd0a8ff5fa470e5c2c06135f6f7be725 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 7 Jul 2023 15:02:41 +0800 Subject: [PATCH 292/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/assets/theme/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mobile-app-did/js/assets/theme/index.ts b/packages/mobile-app-did/js/assets/theme/index.ts index a645480039..4050b156d6 100644 --- a/packages/mobile-app-did/js/assets/theme/index.ts +++ b/packages/mobile-app-did/js/assets/theme/index.ts @@ -24,6 +24,7 @@ export const defaultColors = { bg15: 'rgba(104,170,253,.2)', bg16: '#C5CBD5', bg17: '#EA4F45', + bg18: '#F0F1F4', font1: '#464B53', font2: 'white', From 24f7d12a323fda7f0dfe6d8ad92ece177c36a82b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 7 Jul 2023 15:17:28 +0800 Subject: [PATCH 293/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20reset=20error?= =?UTF-8?q?=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Token/CustomToken/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 717077f59d..81d2f160ca 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -52,6 +52,8 @@ const CustomToken: React.FC = () => { if (!keyword) return; Loading.show(); + + setErrorMessage(''); setBtnDisable(true); setTokenItem(pre => ({ ...pre, decimals: '--', symbol: '' })); @@ -192,8 +194,8 @@ export const pageStyles = StyleSheet.create({ }, tokenDecimal: { lineHeight: pTd(56), - backgroundColor: defaultColors.bg7, - opacity: 0.3, + backgroundColor: defaultColors.bg18, + color: defaultColors.font7, overflow: 'hidden', borderRadius: pTd(6), paddingLeft: pTd(16), From 04f1fa8c15180e47642bde13051ccc37db467b1c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 7 Jul 2023 17:42:44 +0800 Subject: [PATCH 294/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/login.ts | 6 ++++-- .../mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/hooks/login.ts b/packages/mobile-app-did/js/hooks/login.ts index 44833fc5a8..9ed2f6b340 100644 --- a/packages/mobile-app-did/js/hooks/login.ts +++ b/packages/mobile-app-did/js/hooks/login.ts @@ -105,9 +105,11 @@ export function useOnManagerAddressAndQueryResult() { verifierInfo?: VerifierInfo; guardiansApproved?: GuardiansApproved; }) => { - showLoading && Loading.show({ text: t('Initiating social recovery...') }); - await sleep(500); const isRecovery = managerInfo.verificationType === VerificationType.communityRecovery; + showLoading && + Loading.show({ text: t(isRecovery ? 'Initiating social recovery' : 'Creating address on the chain...') }); + + await sleep(500); try { const tmpWalletInfo = walletInfo?.address ? walletInfo : AElf.wallet.createNewWallet(); const extraData = await extraDataEncode(getDeviceInfo()); diff --git a/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx b/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx index 23938ddea4..5a48ae28f4 100644 --- a/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx +++ b/packages/mobile-app-did/js/pages/Pin/SetBiometrics/index.tsx @@ -72,7 +72,8 @@ export default function SetBiometrics() { } if (managerInfo) { timer.current?.remove(); - Loading.show({ text: t('Initiating social recovery') }); + const isRecovery = managerInfo?.verificationType === VerificationType.communityRecovery; + Loading.show({ text: t(isRecovery ? 'Initiating social recovery' : 'Creating address on the chain...') }); timer.current = onIntervalGetResult({ managerInfo, onPass: (info: CAInfo) => { @@ -86,8 +87,7 @@ export default function SetBiometrics() { Loading.hide(); navigationService.reset('Tab'); }, - onFail: message => - onResultFail(message, managerInfo?.verificationType === VerificationType.communityRecovery, true), + onFail: message => onResultFail(message, isRecovery, true), }); } }, [caInfo, dispatch, isSyncCAInfo, managerInfo, onIntervalGetResult, onResultFail, originChainId, pin, t]); From 1b493aa590761fdff335a5cdd45f460dc7135314 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 7 Jul 2023 17:43:53 +0800 Subject: [PATCH 295/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20discover=20&=20e?= =?UTF-8?q?rrorHandle=20&=20device?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.js | 3 + .../web/controllers/SWEventController.test.ts | 56 + .../approval/ApprovalController.test.ts | 97 ++ .../AELFMethodController.test.ts | 1280 +++++++++++++++++ .../methodController/AELFMethodController.ts | 2 +- .../ExtensionDappManager.test.ts | 59 + .../web-extension-did/app/web/inject.test.ts | 56 + .../app/web/utils/device.test.ts | 78 + .../app/web/utils/errorHandler.test.ts | 110 ++ .../app/web/utils/errorHandler.ts | 2 +- 10 files changed, 1741 insertions(+), 2 deletions(-) create mode 100644 packages/web-extension-did/app/web/controllers/SWEventController.test.ts create mode 100644 packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts create mode 100644 packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.test.ts create mode 100644 packages/web-extension-did/app/web/controllers/methodController/ExtensionDappManager.test.ts create mode 100644 packages/web-extension-did/app/web/inject.test.ts create mode 100644 packages/web-extension-did/app/web/utils/device.test.ts create mode 100644 packages/web-extension-did/app/web/utils/errorHandler.test.ts diff --git a/jest.config.js b/jest.config.js index 768467c10f..c6d3fcc741 100644 --- a/jest.config.js +++ b/jest.config.js @@ -59,10 +59,13 @@ module.exports = { roots: ['/packages/web-extension-did'], moduleNameMapper: { '^react$': '/node_modules/react', + '^utils$': '/packages/web-extension-did/app/web/utils', '^utils/(.*)$': '/packages/web-extension-did/app/web/utils/$1', '^store/(.*)$': '/packages/web-extension-did/app/web/store/$1', '^constants/(.*)$': '/packages/web-extension-did/app/web/constants/$1', '^messages/(.*)$': '/packages/web-extension-did/app/web/messages/$1', + '^service/(.*)$': '/packages/web-extension-did/app/web/service/$1', + '^controllers/(.*)$': '/packages/web-extension-did/app/web/controllers/$1', }, coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/hooks-ca/', '/utils/'], }, diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.test.ts b/packages/web-extension-did/app/web/controllers/SWEventController.test.ts new file mode 100644 index 0000000000..a335e4a461 --- /dev/null +++ b/packages/web-extension-did/app/web/controllers/SWEventController.test.ts @@ -0,0 +1,56 @@ +import SWEventController from './SWEventController'; +// import { getConnections, setLocalStorage } from 'utils/storage/storage.utils'; +// import errorHandler from 'utils/errorHandler'; + +// Mock dependencies +jest.mock('utils/storage/storage.utils', () => ({ + getConnections: jest.fn(), +})); +jest.mock('utils/errorHandler', () => jest.fn()); +jest.mock('utils/storage/chromeStorage', () => ({ + setLocalStorage: jest.fn(), +})); +describe('SWEventController', () => { + describe('registerOperator', () => { + it('should throw error if sender.url or sender.tab.id does not exist', async () => { + const sender = {}; + + const errorHandlerMock = jest.requireMock('utils/errorHandler'); + // errorHandlerMock.mockReturnRejectedValue(600001); + expect(SWEventController.registerOperator(sender)).toThrowError(); + expect(errorHandlerMock).toHaveBeenCalledWith(600001); + }); + + it('should register the operator and update connections', async () => { + const sender = { + url: 'http://example.com', + tab: { + id: 123, + }, + id: 'senderId', + origin: 'senderOrigin', + }; + + const connections = {}; + + const getConnectionsMock = jest.requireMock('utils/storage/storage.utils').getConnections; + getConnectionsMock.mockResolvedValue(connections); + + const setLocalStorageMock = jest.requireMock('utils/storage/chromeStorage').setLocalStorage; + setLocalStorageMock.mockResolvedValue(null); + + await SWEventController.registerOperator(sender as any); + + expect(getConnectionsMock).toHaveBeenCalled(); + expect(setLocalStorageMock).toHaveBeenCalledWith({ + connections: { + [sender.url]: { + id: sender.id, + origin: sender.origin, + tabs: [sender.tab.id], + }, + }, + }); + }); + }); +}); diff --git a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts new file mode 100644 index 0000000000..9433ad6855 --- /dev/null +++ b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts @@ -0,0 +1,97 @@ +import ApprovalController from './ApprovalController'; +import { PromptRouteTypes } from 'messages/InternalMessageTypes'; +import NotificationService from 'service/NotificationService'; + +jest.mock('service/NotificationService', () => { + return jest.fn().mockImplementation(() => { + return { + openPrompt: jest.fn(), + }; + }); +}); + +describe('ApprovalController', () => { + let notificationService: any; + let getPageState: jest.Mock; + let controller: ApprovalController; + + beforeEach(() => { + notificationService = new NotificationService(); + getPageState = jest.fn(); + controller = new ApprovalController({ notificationService, getPageState }); + }); + + describe('authorizedToConnect', () => { + it('should return permissionData', async () => { + const promptData = { appName: 'My App', appLogo: 'logo.png', origin: 'https://example.com' }; + const permissionData = { success: true, message: 'Authorized' }; + notificationService.openPrompt.mockResolvedValue(permissionData); + + const result = await controller.authorizedToConnect(promptData); + + expect(notificationService.openPrompt).toHaveBeenCalledWith({ + method: PromptRouteTypes.CONNECT_WALLET, + search: JSON.stringify({ + appName: 'My App', + appLogo: 'logo.png', + appHref: 'https://example.com', + }), + }); + expect(result).toEqual(permissionData); + }); + + it('should use origin as appName when appName is not provided', async () => { + const promptData = { appLogo: 'logo.png', origin: 'https://example.com' }; + const permissionData = { success: true, message: 'Authorized' }; + notificationService.openPrompt.mockResolvedValue(permissionData); + + const result = await controller.authorizedToConnect(promptData); + + expect(notificationService.openPrompt).toHaveBeenCalledWith({ + method: PromptRouteTypes.CONNECT_WALLET, + search: JSON.stringify({ + appName: 'https://example.com', + appLogo: 'logo.png', + appHref: 'https://example.com', + }), + }); + expect(result).toEqual(permissionData); + }); + }); + + describe('authorizedToSendTransactions', () => { + it('should return permissionData', async () => { + const promptData = { + origin: 'https://example.com', + transactionInfoId: '123', + payload: { amount: 100, recipient: 'Alice' }, + }; + const permissionData = { success: true, message: 'Authorized' }; + notificationService.openPrompt.mockResolvedValue(permissionData); + + const result = await controller.authorizedToSendTransactions(promptData); + + expect(notificationService.openPrompt).toHaveBeenCalledWith({ + method: PromptRouteTypes.SEND_TRANSACTION, + search: JSON.stringify(promptData), + }); + expect(result).toEqual(permissionData); + }); + }); + + describe('authorizedToGetSignature', () => { + it('should return permissionData', async () => { + const promptData = { message: 'Sign this data' }; + const permissionData = { success: true, message: 'Authorized' }; + notificationService.openPrompt.mockResolvedValue(permissionData); + + const result = await controller.authorizedToGetSignature(promptData); + + expect(notificationService.openPrompt).toHaveBeenCalledWith({ + method: PromptRouteTypes.GET_SIGNATURE, + search: JSON.stringify(promptData), + }); + expect(result).toEqual(permissionData); + }); + }); +}); diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.test.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.test.ts new file mode 100644 index 0000000000..78e6f5690f --- /dev/null +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.test.ts @@ -0,0 +1,1280 @@ +import AELFMethodController from './AELFMethodController'; +import NotificationService from 'service/NotificationService'; +import ApprovalController from 'controllers/approval/ApprovalController'; +import errorHandler from 'utils/errorHandler'; +import { MethodsBase, MethodsWallet, ResponseCode } from '@portkey/provider-types'; +import SWEventController from 'controllers/SWEventController'; +import * as utils from '@portkey-wallet/utils'; +import { removeLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; + +jest.mock('service/NotificationService', () => { + return jest.fn().mockImplementation(() => ({ + openPrompt: jest.fn(), + })); +}); +jest.mock('controllers/SWEventController', () => ({ + dispatchEvent: jest.fn().mockImplementation(() => ({})), +})); +jest.mock('utils/storage/chromeStorage', () => ({ + setLocalStorage: jest.fn(), + removeLocalStorage: jest.fn(), +})); + +describe('AELFMethodController', () => { + describe('dispenseMessage', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + let sendResponse: any; + const chainIds = ['AELF', 'tDVW']; + const mockDappManager = { + chainIds: jest.fn().mockResolvedValue(chainIds), + locked: jest.fn().mockReturnValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('message.type is not exist', async () => { + const message = {}; + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(sendResponse).toHaveBeenCalledWith(errorHandler(700001, 'Not Support')); + }); + test('message.type is MethodsBase.CHAIN_ID', async () => { + const message = { + type: MethodsBase.CHAIN_ID, + }; + jest.spyOn(aelfMethodController, 'getChainId'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getChainId).toHaveBeenCalled(); + }); + test('message.type is MethodsBase.CHAIN_IDS', async () => { + const message = { + type: MethodsBase.CHAIN_IDS, + }; + jest.spyOn(aelfMethodController, 'getChainIds'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getChainIds).toHaveBeenCalled(); + }); + test('message.type is MethodsBase.ACCOUNTS', async () => { + const message = { + type: MethodsBase.ACCOUNTS, + }; + jest.spyOn(aelfMethodController, 'getAccounts'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getAccounts).toHaveBeenCalled(); + }); + test('message.type is MethodsBase.CHAINS_INFO', async () => { + const message = { + type: MethodsBase.CHAINS_INFO, + }; + jest.spyOn(aelfMethodController, 'getChainsInfo'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getChainsInfo).toHaveBeenCalled(); + }); + test('message.type is MethodsBase.SEND_TRANSACTION', async () => { + const message = { + type: MethodsBase.SEND_TRANSACTION, + }; + jest.spyOn(aelfMethodController, 'sendTransaction'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.sendTransaction).toHaveBeenCalled(); + }); + test('message.type is MethodsBase.REQUEST_ACCOUNTS', async () => { + const message = { + type: MethodsBase.REQUEST_ACCOUNTS, + }; + jest.spyOn(aelfMethodController, 'requestAccounts'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.requestAccounts).toHaveBeenCalled(); + }); + test('message.type is MethodsBase.NETWORK', async () => { + const message = { + type: MethodsBase.NETWORK, + }; + jest.spyOn(aelfMethodController, 'getNetwork'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getNetwork).toHaveBeenCalled(); + }); + test('message.type is MethodsWallet.GET_WALLET_SIGNATURE', async () => { + const message = { + type: MethodsWallet.GET_WALLET_SIGNATURE, + }; + jest.spyOn(aelfMethodController, 'getSignature'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getSignature).toHaveBeenCalled(); + }); + test('message.type is MethodsWallet.GET_WALLET_STATE', async () => { + const message = { + type: MethodsWallet.GET_WALLET_STATE, + }; + jest.spyOn(aelfMethodController, 'getWalletState'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getWalletState).toHaveBeenCalled(); + }); + test('message.type is MethodsWallet.GET_WALLET_NAME', async () => { + const message = { + type: MethodsWallet.GET_WALLET_NAME, + }; + jest.spyOn(aelfMethodController, 'getWalletName'); + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(aelfMethodController.getWalletName).toHaveBeenCalled(); + }); + test('message.type is other type', async () => { + const message = { + type: 'OTHER', + }; + aelfMethodController.dispenseMessage(message as any, sendResponse); + expect(sendResponse).toHaveBeenCalledWith(errorHandler(700001, 'Not Support')); + }); + }); + + describe('isUnlocked', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + test('should return true if the password is exist', () => { + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + expect(aelfMethodController.isUnlocked()).toBe(true); + }); + test('should return false if the password is not exist', () => { + const getPassword = jest.fn().mockReturnValue(null); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + expect(aelfMethodController.isUnlocked()).toBe(false); + }); + }); + + describe('getWalletName', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('isActive return true', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + walletName: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const sendResponse = jest.fn(); + await aelfMethodController.getWalletName(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: await mockDappManager.walletName(), + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('isActive return false', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + walletName: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getWalletName(sendResponse, message as any); + const expectParams = { + ...errorHandler(400001), + data: { + code: ResponseCode.UNAUTHENTICATED, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('isActive throw error', async () => { + const mockDappManager = { + isActive: jest.fn(() => { + throw new Error('error'); + }), + walletName: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getWalletName(sendResponse, message as any); + const expectParams = { + ...errorHandler(500001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('getWalletState', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('isActive return true', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + accounts: jest.fn().mockResolvedValue({}), + chainId: jest.fn().mockResolvedValue({}), + getWallet: jest.fn().mockResolvedValue({ currentNetwork: 'MAIN' }), + }; + (aelfMethodController as any).dappManager = mockDappManager; + aelfMethodController.isUnlocked = jest.fn().mockReturnValue(true); + await aelfMethodController.getWalletState(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: { + isUnlocked: true, + isConnected: true, + accounts: {}, + chainIds: {}, + networkType: 'MAIN', + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('isActive return false', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + aelfMethodController.isUnlocked = jest.fn().mockReturnValue(true); + await aelfMethodController.getWalletState(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: { + isUnlocked: true, + isConnected: false, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('isActive throw error', async () => { + const errorMsg = { + message: 'get accounts error', + }; + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + accounts: jest.fn().mockRejectedValue(errorMsg), + chainId: jest.fn().mockResolvedValue({}), + getWallet: jest.fn().mockResolvedValue({ currentNetwork: 'MAIN' }), + }; + (aelfMethodController as any).dappManager = mockDappManager; + aelfMethodController.isUnlocked = jest.fn().mockReturnValue(true); + await aelfMethodController.getWalletState(sendResponse, message as any); + const expectParams = { + error: 200002, + message: errorMsg.message, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('getChainsInfo', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('get chainsInfo successful', async () => { + const mockDappManager = { + chainsInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getChainsInfo(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: {}, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('chainsInfo throw error', async () => { + const mockDappManager = { + chainsInfo: jest.fn().mockRejectedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getChainsInfo(sendResponse, message as any); + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('getAccounts', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('return accounts if isUnlocked return true', async () => { + const mockDappManager = { + accounts: jest.fn().mockResolvedValue({ AELF: {} }), + }; + (aelfMethodController as any).dappManager = mockDappManager; + aelfMethodController.isUnlocked = jest.fn().mockReturnValue(true); + await aelfMethodController.getAccounts(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: { AELF: {} }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('return {} if isUnlocked return false', async () => { + const mockDappManager = { + accounts: jest.fn().mockResolvedValue({ AELF: {} }), + }; + (aelfMethodController as any).dappManager = mockDappManager; + aelfMethodController.isUnlocked = jest.fn().mockReturnValue(false); + await aelfMethodController.getAccounts(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: {}, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('accounts throw error', async () => { + const mockDappManager = { + accounts: jest.fn().mockRejectedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + aelfMethodController.isUnlocked = jest.fn().mockReturnValue(true); + await aelfMethodController.getAccounts(sendResponse, message as any); + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('getChainId', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('return chainId successful', async () => { + const chainIds = ['AELF', 'tDVW']; + const mockDappManager = { + chainId: jest.fn().mockResolvedValue(chainIds), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getChainId(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: chainIds, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('chainId throw error', async () => { + const chainIds = ['AELF', 'tDVW']; + const mockDappManager = { + chainId: jest.fn().mockRejectedValue(chainIds), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getChainId(sendResponse, message as any); + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('getChainIds', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + }); + test('return chainIds successful', async () => { + const chainIds = ['AELF', 'tDVW']; + const mockDappManager = { + chainIds: jest.fn().mockResolvedValue(chainIds), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getChainIds(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: chainIds, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('chainIds throw error', async () => { + const chainIds = ['AELF', 'tDVW']; + const mockDappManager = { + chainIds: jest.fn().mockRejectedValue(chainIds), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getChainIds(sendResponse, message as any); + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('requestAccounts', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + jest.clearAllMocks(); + }); + test('isActive return true and return successful', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + accounts: jest.fn().mockResolvedValue({}), + chainIds: jest.fn().mockResolvedValue(['AELF', 'tDVW']), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.requestAccounts(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: {}, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('SWEventController.dispatchEvent is called if isActive return true', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + accounts: jest.fn().mockResolvedValue({}), + chainIds: jest.fn().mockResolvedValue(['AELF', 'tDVW']), + getWallet: jest.fn().mockResolvedValue({ currentNetwork: 'MAIN' }), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.requestAccounts(sendResponse, message as any); + const expectParams = { + eventName: 'connected', + data: { chainIds: ['AELF', 'tDVW'], origin: 'origin' }, + }; + expect(SWEventController.dispatchEvent).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('SWEventController.dispatchEvent is not called if isActive return false', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + accounts: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(SWEventController.dispatchEvent).not.toHaveBeenCalled(); + }); + test('approvalController.authorizedToConnect is called if isActive return false', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToConnect'); + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect((aelfMethodController as any).approvalController.authorizedToConnect).toHaveBeenCalled(); + }); + test('approvalController.authorizedToConnect is called and return 200003 error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToConnect').mockResolvedValue({ + error: 200003, + }); + const expectParams = { + ...errorHandler(200003, 'User denied'), + data: { + code: ResponseCode.USER_DENIED, + }, + }; + await aelfMethodController.requestAccounts(sendResponse, message as any); + console.log('sendResponse.mock.calls 200003'); + + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToConnect is called and return other error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToConnect').mockResolvedValue({ + error: 1, + }); + const expectParams = { + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }; + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToConnect is called and return successful', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + accounts: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToConnect').mockResolvedValue({ + error: 0, + }); + const expectParams = { + ...errorHandler(0), + data: {}, + }; + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('dappManager.isActive throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockRejectedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('dappManager.chainIds throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + chainIds: jest.fn().mockRejectedValue([]), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('dappManager.accounts throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + accounts: jest.fn().mockRejectedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToConnect throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToConnect').mockRejectedValue({}); + await aelfMethodController.requestAccounts(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('sendTransaction', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const approvalController = new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }); + let message: any; + let sendResponse: any; + let aelfMethodController: any; + beforeEach(() => { + jest.clearAllMocks(); + message = { + origin: 'origin', + payload: { + params: {}, + method: 'ManagerTransfer', + contractAddress: 'caContractAddress', + }, + }; + sendResponse = jest.fn(); + aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + test('message?.payload?.params is not exist', async () => { + const message = { origin: 'origin' }; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + const res = { ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('isActive return false', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + const res = { + ...errorHandler(200004), + data: { + code: ResponseCode.UNAUTHENTICATED, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('chainInfo is not exist', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue(undefined), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + const res = { + ...errorHandler(200005), + data: { + code: 40001, + msg: 'invalid chain id', + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('chainInfo.endPoint is not exist', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: undefined, + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + const res = { + ...errorHandler(200005), + data: { + code: 40001, + msg: 'invalid chain id', + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('caInfo is not exist', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue(undefined), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + const res = { + ...errorHandler(200005), + data: { + code: 40001, + msg: 'invalid chain id', + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('the method is not in the whitelist', async () => { + const message = { + origin: 'origin', + payload: { + params: {}, + method: 'test', + }, + }; + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + const res = { + ...errorHandler(400001), + data: { + code: ResponseCode.CONTRACT_ERROR, + msg: 'The current method is not supported', + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + + test('setLocalStorage is called', async () => { + jest.spyOn(utils, 'randomId').mockReturnValue('randomId'); + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const res = { txPayload: { randomId: JSON.stringify({}) } }; + await (aelfMethodController as any).sendTransaction(sendResponse, message); + expect(setLocalStorage).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + + test('approvalController.authorizedToSendTransactions is called, and removeLocalStorage is called', async () => { + jest.spyOn(utils, 'randomId').mockReturnValue('randomId'); + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToSendTransactions') + .mockImplementation(() => ({ error: 0 })); + await aelfMethodController.sendTransaction(sendResponse, message); + expect(removeLocalStorage).toHaveBeenCalled(); + }); + test('approvalController.authorizedToSendTransactions is called, and return 200003 error', async () => { + jest.spyOn(utils, 'randomId').mockReturnValue('randomId'); + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToSendTransactions') + .mockResolvedValue({ error: 200003 }); + const expectParams = { + ...errorHandler(200003), + data: { + code: ResponseCode.USER_DENIED, + }, + }; + await aelfMethodController.sendTransaction(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToSendTransactions is called, and return non-0 error', async () => { + jest.spyOn(utils, 'randomId').mockReturnValue('randomId'); + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToSendTransactions') + .mockResolvedValue({ error: 1 }); + const expectParams = { + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }; + await aelfMethodController.sendTransaction(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToSendTransactions is called, and return value successful', async () => { + jest.spyOn(utils, 'randomId').mockReturnValue('randomId'); + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const res = { error: 0, data: {} }; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToSendTransactions') + .mockResolvedValue(res); + await aelfMethodController.sendTransaction(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('isActive throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockRejectedValue(false), + }; + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.sendTransaction(sendResponse, message as any); + const res = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('getChainInfo throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockRejectedValue(false), + }; + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.sendTransaction(sendResponse, message as any); + const res = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('getCaInfo throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue(true), + getCaInfo: jest.fn().mockRejectedValue(false), + }; + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.sendTransaction(sendResponse, message as any); + const res = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('approvalController.authorizedToSendTransactions throw error', async () => { + jest.spyOn(utils, 'randomId').mockReturnValue('randomId'); + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + getChainInfo: jest.fn().mockResolvedValue({ + endPoint: 'http://endpoint', + }), + getCaInfo: jest.fn().mockResolvedValue({}), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToSendTransactions') + .mockRejectedValue({ error: 200003 }); + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + await aelfMethodController.sendTransaction(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); + + describe('getSignature', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const approvalController = new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }); + const message = { + origin: 'origin', + payload: { + data: 1, + }, + }; + let sendResponse: any; + let aelfMethodController: any; + beforeEach(() => { + sendResponse = jest.fn(); + aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + // jest.clearAllMocks(); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + test('message.payload.data is not exist', async () => { + const message = { origin: 'origin' }; + await (aelfMethodController as any).getSignature(sendResponse, message); + const res = { ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('the type of message.payload.data is string', async () => { + const message = { + origin: 'origin', + payload: { + data: 'data', + }, + }; + await (aelfMethodController as any).getSignature(sendResponse, message); + const res = { ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }; + expect(sendResponse).not.toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('the type of message.payload.data is number', async () => { + const message = { + origin: 'origin', + payload: { + data: 1, + }, + }; + await (aelfMethodController as any).getSignature(sendResponse, message); + const res = { ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }; + expect(sendResponse).not.toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('the type of message.payload.data is object', async () => { + const message = { + origin: 'origin', + payload: { + data: {}, + }, + }; + await (aelfMethodController as any).getSignature(sendResponse, message); + const res = { ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS } }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('isActive return false', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(false), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await (aelfMethodController as any).getSignature(sendResponse, message); + const res = { + ...errorHandler(200004), + data: { + code: ResponseCode.UNAUTHENTICATED, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('approvalController.authorizedToGetSignature is called, and return 200003 error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToGetSignature') + .mockResolvedValue({ error: 200003 }); + const expectParams = { + ...errorHandler(200003), + data: { + code: ResponseCode.USER_DENIED, + }, + }; + await aelfMethodController.getSignature(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToGetSignature is called, and return non-0 error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + }; + (aelfMethodController as any).dappManager = mockDappManager; + jest + .spyOn((aelfMethodController as any).approvalController, 'authorizedToGetSignature') + .mockResolvedValue({ error: 1 }); + const expectParams = { + ...errorHandler(700002), + data: { + code: ResponseCode.CONTRACT_ERROR, + }, + }; + await aelfMethodController.getSignature(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('approvalController.authorizedToGetSignature is called, and return value successful', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + }; + (aelfMethodController as any).dappManager = mockDappManager; + const res = { error: 0, data: {} }; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToGetSignature').mockResolvedValue(res); + await aelfMethodController.getSignature(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('isActive throw error', async () => { + const mockDappManager = { + isActive: jest.fn(() => { + throw new Error(''); + }), + }; + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getSignature(sendResponse, message as any); + const res = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + test('approvalController.authorizedToGetSignature throw error', async () => { + const mockDappManager = { + isActive: jest.fn().mockResolvedValue(true), + }; + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: approvalController, + getPageState, + getPassword, + }); + (aelfMethodController as any).dappManager = mockDappManager; + jest.spyOn((aelfMethodController as any).approvalController, 'authorizedToGetSignature').mockRejectedValue(false); + await aelfMethodController.getSignature(sendResponse, message as any); + expect(sendResponse).toHaveBeenCalled(); + const res = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(res)); + }); + }); + + describe('getNetwork', () => { + const getPageState = jest.fn().mockReturnValue({ + lockTime: 60, + registerStatus: undefined, + wallet: {}, + }); + const getPassword = jest.fn().mockReturnValue('123456'); + const aelfMethodController = new AELFMethodController({ + notificationService: new NotificationService(), + approvalController: new ApprovalController({ + notificationService: new NotificationService(), + getPageState, + }), + getPageState, + getPassword, + }); + const message = { + origin: 'origin', + }; + let sendResponse: any; + beforeEach(() => { + sendResponse = jest.fn(); + jest.clearAllMocks(); + }); + test('getNetwork successful', async () => { + const mockDappManager = { + networkType: jest.fn().mockResolvedValue('MAIN'), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getNetwork(sendResponse, message as any); + const expectParams = { + ...errorHandler(0), + data: 'MAIN', + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + test('getNetwork error', async () => { + const mockDappManager = { + networkType: jest.fn().mockRejectedValue('MAIN'), + }; + (aelfMethodController as any).dappManager = mockDappManager; + await aelfMethodController.getNetwork(sendResponse, message as any); + const expectParams = { + ...errorHandler(100001), + data: { + code: ResponseCode.INTERNAL_ERROR, + }, + }; + expect(sendResponse).toHaveBeenCalledWith(expect.objectContaining(expectParams)); + }); + }); +}); diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 92823d0d09..81744c7867 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -268,7 +268,7 @@ export default class AELFMethodController { const { payload, origin } = message; const chainInfo = await this.dappManager.getChainInfo(payload.chainId); const caInfo = await this.dappManager.getCaInfo(payload.chainId); - if (!chainInfo || !chainInfo.endPoint || !payload.params || !caInfo) + if (!chainInfo || !chainInfo.endPoint || !caInfo) return sendResponse({ ...errorHandler(200005), data: { diff --git a/packages/web-extension-did/app/web/controllers/methodController/ExtensionDappManager.test.ts b/packages/web-extension-did/app/web/controllers/methodController/ExtensionDappManager.test.ts new file mode 100644 index 0000000000..4656a1b021 --- /dev/null +++ b/packages/web-extension-did/app/web/controllers/methodController/ExtensionDappManager.test.ts @@ -0,0 +1,59 @@ +import { ExtensionDappManager } from './ExtensionDappManager'; +import { DappManager } from '@portkey-wallet/utils/dapp/dappManager'; + +describe('ExtensionDappManager', () => { + describe('isActive', () => { + test('should return true if the origin is active and not locked', async () => { + const isActiveSpy = jest.spyOn(DappManager.prototype, 'isActive').mockResolvedValue(true); + const dappManager = new ExtensionDappManager({ + store: {}, + locked: () => false, + }); + const isActive = await dappManager.isActive('http://localhost:3000'); + expect(isActiveSpy).toBeCalledWith('http://localhost:3000'); + expect(isActive).toBe(true); + }); + + test('should return false if the origin is not active', async () => { + const isActiveSpy = jest.spyOn(DappManager.prototype, 'isActive').mockResolvedValue(false); + const dappManager = new ExtensionDappManager({ + store: {}, + locked: () => false, + }); + const isActive = await dappManager.isActive('http://not-active.com'); + expect(isActiveSpy).toBeCalledWith('http://localhost:3000'); + expect(isActive).toBe(false); + }); + + test('should return false if the extension is locked', async () => { + const isActiveSpy = jest.spyOn(DappManager.prototype, 'isActive').mockResolvedValue(true); + const dappManager = new ExtensionDappManager({ + store: {}, + locked: () => true, + }); + const isActive = await dappManager.isActive('http://localhost:3000'); + expect(isActiveSpy).toBeCalledWith('http://localhost:3000'); + expect(isActive).toBe(false); + }); + }); + + describe('isLocked', () => { + test('should return true if locked is true', async () => { + const dappManager = new ExtensionDappManager({ + store: {}, + locked: () => true, + }); + const isLocked = await dappManager.isLocked(); + expect(isLocked).toBe(true); + }); + + test('should return false if locked is false', async () => { + const dappManager = new ExtensionDappManager({ + store: {}, + locked: () => false, + }); + const isLocked = await dappManager.isLocked(); + expect(isLocked).toBe(false); + }); + }); +}); diff --git a/packages/web-extension-did/app/web/inject.test.ts b/packages/web-extension-did/app/web/inject.test.ts new file mode 100644 index 0000000000..5b830f6d15 --- /dev/null +++ b/packages/web-extension-did/app/web/inject.test.ts @@ -0,0 +1,56 @@ +import { InitializeProvider, InpagePostStream } from '@portkey/extension-provider'; +import { shouldInjectProvider } from '@portkey/provider-utils'; +import Inject from './inject'; + +jest.mock('@portkey/extension-provider', () => { + return { + InitializeProvider: jest.fn(), + InpagePostStream: jest.fn(), + }; +}); + +jest.mock('@portkey/provider-utils', () => { + return { + shouldInjectProvider: jest.fn(), + }; +}); + +describe('Inject', () => { + let InitializeProviderMock: jest.Mock; + let InpagePostStreamMock: jest.Mock; + let shouldInjectProviderFn: jest.Mock; + + beforeEach(() => { + InitializeProviderMock = InitializeProvider as jest.Mock; + InpagePostStreamMock = InpagePostStream as unknown as jest.Mock; + shouldInjectProviderFn = shouldInjectProvider as jest.Mock; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('initPortKey', () => { + it('should initialize PortKey provider if shouldInjectProvider returns true', () => { + shouldInjectProviderFn.mockReturnValue(true); + const portkeyStream = { name: jest.fn() }; + InpagePostStreamMock.mockReturnValue(portkeyStream); + + new Inject(); + + expect(shouldInjectProviderFn).toHaveBeenCalled(); + expect(InpagePostStreamMock).toHaveBeenCalledWith({ name: 'portkey-inpage' }); + expect(InitializeProviderMock).toHaveBeenCalledWith({ connectionStream: portkeyStream }); + }); + + it('should not initialize PortKey provider if shouldInjectProvider returns false', () => { + shouldInjectProviderFn.mockReturnValue(false); + + new Inject(); + + expect(shouldInjectProviderFn).toHaveBeenCalled(); + expect(InpagePostStreamMock).not.toHaveBeenCalled(); + expect(InitializeProviderMock).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/web-extension-did/app/web/utils/device.test.ts b/packages/web-extension-did/app/web/utils/device.test.ts new file mode 100644 index 0000000000..32e4da79d4 --- /dev/null +++ b/packages/web-extension-did/app/web/utils/device.test.ts @@ -0,0 +1,78 @@ +import { getDeviceInfo, getDeviceIcon } from './device'; +import { DeviceInfoType, DeviceType } from '@portkey-wallet/types/types-ca/device'; +import { IconType } from 'types/icon'; +describe('getDeviceInfo', () => { + it('should return correct device info for MAC', () => { + const deviceType = DeviceType.MAC; + const expectedDeviceInfo: DeviceInfoType = { deviceName: 'macOS', deviceType }; + + const result = getDeviceInfo(deviceType); + + expect(result).toEqual(expectedDeviceInfo); + }); + + it('should return correct device info for WINDOWS', () => { + const deviceType = DeviceType.WINDOWS; + const expectedDeviceInfo: DeviceInfoType = { deviceName: 'Windows', deviceType }; + + const result = getDeviceInfo(deviceType); + + expect(result).toEqual(expectedDeviceInfo); + }); + + it('should return correct device info for other devices', () => { + const deviceType = DeviceType.OTHER; + const expectedDeviceInfo: DeviceInfoType = { deviceName: 'Other', deviceType }; + + const result = getDeviceInfo(deviceType); + + expect(result).toEqual(expectedDeviceInfo); + }); +}); + +describe('getDeviceIcon', () => { + it('should return correct icon for ANDROID', () => { + const deviceType = DeviceType.ANDROID; + const expectedIcon: IconType = 'phone-Android'; + + const result = getDeviceIcon(deviceType); + + expect(result).toEqual(expectedIcon); + }); + + it('should return correct icon for IOS', () => { + const deviceType = DeviceType.IOS; + const expectedIcon: IconType = 'phone-iOS'; + + const result = getDeviceIcon(deviceType); + + expect(result).toEqual(expectedIcon); + }); + + it('should return correct icon for MAC', () => { + const deviceType = DeviceType.MAC; + const expectedIcon: IconType = 'desk-mac'; + + const result = getDeviceIcon(deviceType); + + expect(result).toEqual(expectedIcon); + }); + + it('should return correct icon for WINDOWS', () => { + const deviceType = DeviceType.WINDOWS; + const expectedIcon: IconType = 'desk-win'; + + const result = getDeviceIcon(deviceType); + + expect(result).toEqual(expectedIcon); + }); + + it('should return default icon for other devices', () => { + const deviceType = DeviceType.OTHER; + const expectedIcon: IconType = 'desk-win'; + + const result = getDeviceIcon(deviceType); + + expect(result).toEqual(expectedIcon); + }); +}); diff --git a/packages/web-extension-did/app/web/utils/errorHandler.test.ts b/packages/web-extension-did/app/web/utils/errorHandler.test.ts new file mode 100644 index 0000000000..0338a91d73 --- /dev/null +++ b/packages/web-extension-did/app/web/utils/errorHandler.test.ts @@ -0,0 +1,110 @@ +import errorHandler from './errorHandler'; +describe('errorHandler', () => { + test('should return the correct output for a success code', () => { + const code = 0; + const output = errorHandler(code); + expect(output).toEqual({ + error: code, + message: '', + }); + }); + test('should return the correct output for an error object', () => { + const code = 200002; + const error = new Error('Payload is false.'); + const output = errorHandler(code, error); + expect(output).toEqual({ + error: code, + name: 'Error', + message: 'Payload is false.', + stack: error.stack, + data: undefined, + }); + }); + test('should return the correct output for an error number', () => { + const code = 200002; + const error = 200002; + const output = errorHandler(code, error); + expect(output).toEqual({ + error: code, + name: undefined, + message: undefined, + data: undefined, + }); + }); + test('should return the correct output for a custom error message', () => { + const code = 410002; + const error = 'Invalid parameters.'; + const output = errorHandler(code, error); + expect(output).toEqual({ + error: code, + name: 'customError', + message: 'Invalid parameters.', + }); + }); + test('should return the correct output for error is not exist', () => { + const code = 200002; + const output = errorHandler(code, undefined); + expect(output).toEqual({ + error: code, + name: 'errorMap', + message: 'No Wallet Info.', + data: undefined, + }); + }); + test('should return the correct output for an error object with additional properties', () => { + const code = 200002; + const error = { + name: 'CustomError', + message: 'Payload is false.', + stack: 'Error stack', + data: { key: 'value' }, + }; + const output = errorHandler(code, error); + expect(output).toEqual({ + error: code, + name: 'CustomError', + message: 'Payload is false.', + stack: 'Error stack', + data: { key: 'value' }, + }); + }); + test('should return the correct output for an error object with nested Error property', () => { + const code = 200002; + const error = { + name: 'CustomError', + Error: { + Message: 'Payload is false.', + }, + }; + const output = errorHandler(code, error); + expect(output).toEqual({ + error: code, + name: 'CustomError', + message: 'Payload is false.', + stack: undefined, + data: undefined, + }); + }); + test('should return the correct output for error type is not string and Error is undefined', () => { + const code = 500001; + const output = errorHandler(code, { name: 'name', stack: 'stack', data: 'data', Error: undefined }); + expect(output).toEqual({ + error: code, + name: 'name', + message: undefined, + data: 'data', + stack: 'stack', + }); + }); + test('should return the correct output for error type is not string', () => { + const code = 500001; + const output = errorHandler(code, { name: 'name', stack: 'stack', data: {}, Error: 'error message' }); + expect(output).toEqual({ + error: code, + name: 'name', + message: 'error message', + data: {}, + stack: 'stack', + }); + }); +}); diff --git a/packages/web-extension-did/app/web/utils/errorHandler.ts b/packages/web-extension-did/app/web/utils/errorHandler.ts index ff82dceb68..15478b8386 100644 --- a/packages/web-extension-did/app/web/utils/errorHandler.ts +++ b/packages/web-extension-did/app/web/utils/errorHandler.ts @@ -73,7 +73,7 @@ export default function errorHandler(code: keyof typeof errorMap, error?: any | name: error.name, message: error.message || error.Error?.Message || error.Error, stack: error.stack, - data: error?.data, + data: error.data, }; } else if (typeof error === 'string') { output = { From e34b98edbe71c31436bee0d7d7d44619b67552fc Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 7 Jul 2023 18:02:40 +0800 Subject: [PATCH 296/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20update=20jest.co?= =?UTF-8?q?nfig.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.js | 12 +++- .../web/controllers/SWEventController.test.ts | 56 ------------------- .../approval/ApprovalController.test.ts | 8 +-- .../app/web/utils/device.test.ts | 16 +++--- 4 files changed, 23 insertions(+), 69 deletions(-) delete mode 100644 packages/web-extension-did/app/web/controllers/SWEventController.test.ts diff --git a/jest.config.js b/jest.config.js index c6d3fcc741..7b00a25cc8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,6 +9,10 @@ module.exports = { '**/packages/store/store-ca/**/slice.{ts,tsx}', '**/packages/utils/wallet/index.ts', '**/packages/web-extension-did/app/web/store/reducers/**/*.{ts,tsx}', + '**/packages/web-extension-did/app/web/controllers/approval/*.ts', + '**/packages/web-extension-did/app/web/controllers/methodController/*.ts', + '**/packages/web-extension-did/app/web/utils/device.ts', + '**/packages/web-extension-did/app/web/utils/errorHandle.ts', '**/packages/web-extension-did/app/web/hooks/useActiveLockStatus.ts', '**/packages/web-extension-did/app/web/hooks/useCaInfoOnChain.ts', '**/packages/web-extension-did/app/web/hooks/useNetwork.ts', @@ -48,7 +52,13 @@ module.exports = { { displayName: 'web-extension-did-hooks', preset: 'ts-jest', - testMatch: ['/packages/web-extension-did/app/web/hooks/*.test.{ts,tsx}'], + testMatch: [ + '/packages/web-extension-did/app/web/hooks/*.test.{ts,tsx}', + '/packages/web-extension-did/app/web/controllers/approval/*.test.ts', + '/packages/web-extension-did/app/web/controllers/methodController/*.test.ts', + '/packages/web-extension-did/app/web/utils/errorHandle.test.ts', + '/packages/web-extension-did/app/web/utils/device/*.test.ts', + ], testEnvironment: 'jsdom', transform: { '^.+\\.(ts|tsx)$': [ diff --git a/packages/web-extension-did/app/web/controllers/SWEventController.test.ts b/packages/web-extension-did/app/web/controllers/SWEventController.test.ts deleted file mode 100644 index a335e4a461..0000000000 --- a/packages/web-extension-did/app/web/controllers/SWEventController.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import SWEventController from './SWEventController'; -// import { getConnections, setLocalStorage } from 'utils/storage/storage.utils'; -// import errorHandler from 'utils/errorHandler'; - -// Mock dependencies -jest.mock('utils/storage/storage.utils', () => ({ - getConnections: jest.fn(), -})); -jest.mock('utils/errorHandler', () => jest.fn()); -jest.mock('utils/storage/chromeStorage', () => ({ - setLocalStorage: jest.fn(), -})); -describe('SWEventController', () => { - describe('registerOperator', () => { - it('should throw error if sender.url or sender.tab.id does not exist', async () => { - const sender = {}; - - const errorHandlerMock = jest.requireMock('utils/errorHandler'); - // errorHandlerMock.mockReturnRejectedValue(600001); - expect(SWEventController.registerOperator(sender)).toThrowError(); - expect(errorHandlerMock).toHaveBeenCalledWith(600001); - }); - - it('should register the operator and update connections', async () => { - const sender = { - url: 'http://example.com', - tab: { - id: 123, - }, - id: 'senderId', - origin: 'senderOrigin', - }; - - const connections = {}; - - const getConnectionsMock = jest.requireMock('utils/storage/storage.utils').getConnections; - getConnectionsMock.mockResolvedValue(connections); - - const setLocalStorageMock = jest.requireMock('utils/storage/chromeStorage').setLocalStorage; - setLocalStorageMock.mockResolvedValue(null); - - await SWEventController.registerOperator(sender as any); - - expect(getConnectionsMock).toHaveBeenCalled(); - expect(setLocalStorageMock).toHaveBeenCalledWith({ - connections: { - [sender.url]: { - id: sender.id, - origin: sender.origin, - tabs: [sender.tab.id], - }, - }, - }); - }); - }); -}); diff --git a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts index 9433ad6855..ad8ea39d6e 100644 --- a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts +++ b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.test.ts @@ -22,7 +22,7 @@ describe('ApprovalController', () => { }); describe('authorizedToConnect', () => { - it('should return permissionData', async () => { + test('should return permissionData', async () => { const promptData = { appName: 'My App', appLogo: 'logo.png', origin: 'https://example.com' }; const permissionData = { success: true, message: 'Authorized' }; notificationService.openPrompt.mockResolvedValue(permissionData); @@ -40,7 +40,7 @@ describe('ApprovalController', () => { expect(result).toEqual(permissionData); }); - it('should use origin as appName when appName is not provided', async () => { + test('should use origin as appName when appName is not provided', async () => { const promptData = { appLogo: 'logo.png', origin: 'https://example.com' }; const permissionData = { success: true, message: 'Authorized' }; notificationService.openPrompt.mockResolvedValue(permissionData); @@ -60,7 +60,7 @@ describe('ApprovalController', () => { }); describe('authorizedToSendTransactions', () => { - it('should return permissionData', async () => { + test('should return permissionData', async () => { const promptData = { origin: 'https://example.com', transactionInfoId: '123', @@ -80,7 +80,7 @@ describe('ApprovalController', () => { }); describe('authorizedToGetSignature', () => { - it('should return permissionData', async () => { + test('should return permissionData', async () => { const promptData = { message: 'Sign this data' }; const permissionData = { success: true, message: 'Authorized' }; notificationService.openPrompt.mockResolvedValue(permissionData); diff --git a/packages/web-extension-did/app/web/utils/device.test.ts b/packages/web-extension-did/app/web/utils/device.test.ts index 32e4da79d4..fbb67bf6ac 100644 --- a/packages/web-extension-did/app/web/utils/device.test.ts +++ b/packages/web-extension-did/app/web/utils/device.test.ts @@ -2,7 +2,7 @@ import { getDeviceInfo, getDeviceIcon } from './device'; import { DeviceInfoType, DeviceType } from '@portkey-wallet/types/types-ca/device'; import { IconType } from 'types/icon'; describe('getDeviceInfo', () => { - it('should return correct device info for MAC', () => { + test('should return correct device info for MAC', () => { const deviceType = DeviceType.MAC; const expectedDeviceInfo: DeviceInfoType = { deviceName: 'macOS', deviceType }; @@ -11,7 +11,7 @@ describe('getDeviceInfo', () => { expect(result).toEqual(expectedDeviceInfo); }); - it('should return correct device info for WINDOWS', () => { + test('should return correct device info for WINDOWS', () => { const deviceType = DeviceType.WINDOWS; const expectedDeviceInfo: DeviceInfoType = { deviceName: 'Windows', deviceType }; @@ -20,7 +20,7 @@ describe('getDeviceInfo', () => { expect(result).toEqual(expectedDeviceInfo); }); - it('should return correct device info for other devices', () => { + test('should return correct device info for other devices', () => { const deviceType = DeviceType.OTHER; const expectedDeviceInfo: DeviceInfoType = { deviceName: 'Other', deviceType }; @@ -31,7 +31,7 @@ describe('getDeviceInfo', () => { }); describe('getDeviceIcon', () => { - it('should return correct icon for ANDROID', () => { + test('should return correct icon for ANDROID', () => { const deviceType = DeviceType.ANDROID; const expectedIcon: IconType = 'phone-Android'; @@ -40,7 +40,7 @@ describe('getDeviceIcon', () => { expect(result).toEqual(expectedIcon); }); - it('should return correct icon for IOS', () => { + test('should return correct icon for IOS', () => { const deviceType = DeviceType.IOS; const expectedIcon: IconType = 'phone-iOS'; @@ -49,7 +49,7 @@ describe('getDeviceIcon', () => { expect(result).toEqual(expectedIcon); }); - it('should return correct icon for MAC', () => { + test('should return correct icon for MAC', () => { const deviceType = DeviceType.MAC; const expectedIcon: IconType = 'desk-mac'; @@ -58,7 +58,7 @@ describe('getDeviceIcon', () => { expect(result).toEqual(expectedIcon); }); - it('should return correct icon for WINDOWS', () => { + test('should return correct icon for WINDOWS', () => { const deviceType = DeviceType.WINDOWS; const expectedIcon: IconType = 'desk-win'; @@ -67,7 +67,7 @@ describe('getDeviceIcon', () => { expect(result).toEqual(expectedIcon); }); - it('should return default icon for other devices', () => { + test('should return default icon for other devices', () => { const deviceType = DeviceType.OTHER; const expectedIcon: IconType = 'desk-win'; From f30e6cdc7369f66d3b599ca36c1a3ee73fb96cfc Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 7 Jul 2023 19:09:07 +0800 Subject: [PATCH 297/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Token/CustomToken/index.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 81d2f160ca..b5431429da 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -17,7 +17,7 @@ import { request } from '@portkey-wallet/api/api-did'; import { useDebounceCallback } from '@portkey-wallet/hooks'; import Loading from 'components/Loading'; import navigationService from 'utils/navigationService'; -import { sleep } from '@portkey-wallet/utils'; +import { handleErrorMessage, sleep } from '@portkey-wallet/utils'; import CommonToast from 'components/CommonToast'; import { FontStyles } from 'assets/theme/styles'; @@ -71,8 +71,10 @@ const CustomToken: React.FC = () => { setKeyword(symbol); setBtnDisable(false); } - } catch (error) { + } catch (err) { setBtnDisable(true); + Loading.hide(); + CommonToast.fail(handleErrorMessage(err)); } finally { Loading.hide(); } @@ -109,12 +111,14 @@ const CustomToken: React.FC = () => { isDisplay: true, }, }); - Loading.hide(); CommonToast.success('success'); await sleep(500); navigationService.goBack(); - } catch (error: any) { - console.log('add custom token error', error); + } catch (err: any) { + CommonToast.fail(handleErrorMessage(err)); + console.log('add custom token error', err); + } finally { + Loading.hide(); } } }, [tokenItem]); From ed238cd624ae7696b72195119f0382064a4e49a0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 13:39:25 +0800 Subject: [PATCH 298/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20ui=20popular=20he?= =?UTF-8?q?ader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Token/ManageTokenList/components/PopularToken/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx index cbb562a483..b5b35297ad 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx @@ -49,6 +49,7 @@ export const pageStyles = StyleSheet.create({ ...fonts.mediumFont, paddingLeft: pTd(16), paddingTop: pTd(16), + marginBottom: pTd(8), }, noResult: { marginTop: pTd(40), From 3611a0a9895e3290cc9db18e9bcf66fdcd314a3b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 13:40:06 +0800 Subject: [PATCH 299/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20cusotm=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/FormItem/index.tsx | 9 +++++---- .../mobile-app-did/js/components/SelectChain/index.tsx | 1 - .../mobile-app-did/js/pages/Token/CustomToken/index.tsx | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/mobile-app-did/js/components/FormItem/index.tsx b/packages/mobile-app-did/js/components/FormItem/index.tsx index 3a638c16aa..f1c081ee29 100644 --- a/packages/mobile-app-did/js/components/FormItem/index.tsx +++ b/packages/mobile-app-did/js/components/FormItem/index.tsx @@ -1,22 +1,23 @@ import { defaultColors } from 'assets/theme'; import { TextM } from 'components/CommonText'; import React, { ReactNode } from 'react'; -import { View, StyleSheet } from 'react-native'; +import { View, StyleSheet, ViewProps, StyleProp } from 'react-native'; import { pTd } from 'utils/unit'; export type FormItemType = { title: string; children: ReactNode; + style?: StyleProp; }; export default function FormItem(props: FormItemType) { - const { title, children } = props; + const { title, children, style = {} } = props; return ( - <> + {title} {children} - + ); } diff --git a/packages/mobile-app-did/js/components/SelectChain/index.tsx b/packages/mobile-app-did/js/components/SelectChain/index.tsx index a18b25ef18..ea9adad3e7 100644 --- a/packages/mobile-app-did/js/components/SelectChain/index.tsx +++ b/packages/mobile-app-did/js/components/SelectChain/index.tsx @@ -73,7 +73,6 @@ const styles = StyleSheet.create({ fontSize: pTd(14), }, selectedItem: { - marginBottom: pTd(12), borderRadius: pTd(6), height: pTd(56), }, diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index b5431429da..e82c7ea85a 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -134,7 +134,7 @@ const CustomToken: React.FC = () => { 'To add a token, you need to select the network that it belongs to and enter its symbol for automatic recognition.', )} - + Date: Mon, 10 Jul 2023 13:44:51 +0800 Subject: [PATCH 300/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20bottom=20btn=20st?= =?UTF-8?q?yle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index e82c7ea85a..b5e03da0b2 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -20,6 +20,7 @@ import navigationService from 'utils/navigationService'; import { handleErrorMessage, sleep } from '@portkey-wallet/utils'; import CommonToast from 'components/CommonToast'; import { FontStyles } from 'assets/theme/styles'; +import GStyles from 'assets/theme/GStyles'; interface CustomTokenProps { route?: any; @@ -196,8 +197,7 @@ export const pageStyles = StyleSheet.create({ position: 'absolute', bottom: 0, width: screenWidth, - paddingLeft: pTd(20), - paddingRight: pTd(20), + ...GStyles.paddingArg(20, 16), }, tokenDecimal: { lineHeight: pTd(56), From c92b2e0b67239da70722765a2e7995a2dab830ff Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Mon, 10 Jul 2023 13:47:20 +0800 Subject: [PATCH 301/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20UI=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/Token/Manage/index.tsx | 2 +- .../app/web/pages/components/PermissionCheck/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx index 96b3a0adfe..355ea5e949 100644 --- a/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Manage/index.tsx @@ -198,7 +198,7 @@ export default function AddToken() { tokenShowList.length ? (
    -
    {t('Popular Assets')}
    + {!filterWord.length &&
    {t('Popular Assets')}
    } {tokenShowList.map((item) => renderTokenItem(item))}
    {filterWord && renderSearchResultTip} diff --git a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx index 24f5b8a4a9..9b71d53249 100644 --- a/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/PermissionCheck/index.tsx @@ -71,7 +71,7 @@ export default function PermissionCheck({ const detail = (res as any)?.data; if (detail?.registerStatus) { detail?.privateKey && dispatch(setPasswordSeed(detail.privateKey)); - // navigate to unlock expect dapp connect + // navigate to unlock except dapp connect location.pathname !== '/permission' && !detail?.privateKey && navigate('/unlock'); } else { InternalMessage.payload(PortkeyMessageTypes.REGISTER_WALLET, {}).send(); From f7b51c511e5561008cd749bf4982b50e8a23f5d2 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 10 Jul 2023 13:54:02 +0800 Subject: [PATCH 302/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20error?= =?UTF-8?q?=20png?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/assets/image/pngs/error.png | Bin 4216 -> 12664 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/mobile-app-did/js/assets/image/pngs/error.png b/packages/mobile-app-did/js/assets/image/pngs/error.png index 7850daf76272494f38cda95a3488f4e49f251502..60e5dc9120494b699e11b281ff22030516f0b1ff 100755 GIT binary patch literal 12664 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 literal 4216 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 O0000 Date: Mon, 10 Jul 2023 14:27:15 +0800 Subject: [PATCH 303/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20ui=20color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/components/HttpModal/index.tsx | 2 +- packages/mobile-app-did/js/components/CommonInput/index.tsx | 2 +- packages/mobile-app-did/js/components/FormItem/index.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx index dc2bea65b3..0862d42942 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/components/HttpModal/index.tsx @@ -74,7 +74,7 @@ export const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'space-between', paddingTop: pTd(10), - paddingBottom: pTd(16), + paddingBottom: pTd(26), }, buttonBaseStyle: { width: pTd(160), diff --git a/packages/mobile-app-did/js/components/CommonInput/index.tsx b/packages/mobile-app-did/js/components/CommonInput/index.tsx index 78a2fcf582..84f1dc484a 100644 --- a/packages/mobile-app-did/js/components/CommonInput/index.tsx +++ b/packages/mobile-app-did/js/components/CommonInput/index.tsx @@ -27,7 +27,7 @@ const CommonInput = forwardRef(function CommonInput(props: CommonInputProps, for if (type === 'search') return ( ; + style?: any; }; export default function FormItem(props: FormItemType) { From 1aad5a4d3cccb963508d5db2c597ab2bccfc85dc Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 17:13:15 +0800 Subject: [PATCH 304/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20get=20ic?= =?UTF-8?q?on=20func?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/dapp/browser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/dapp/browser.ts b/packages/utils/dapp/browser.ts index 5ab739932d..46932ec633 100644 --- a/packages/utils/dapp/browser.ts +++ b/packages/utils/dapp/browser.ts @@ -69,7 +69,7 @@ export function getProtocolAndHost(url: string) { * @returns string */ export function getFaviconUrl(url: string, size: number = 50): string { - return `https://api.faviconkit.com/${getHost(url)}/${size}`; + return `https://icon.horse/icon/${getHost(url)}/${size}`; } /** From 17c805b687d8adb0e98fdecf74c6181fddce8391 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 17:26:23 +0800 Subject: [PATCH 305/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20disable?= =?UTF-8?q?=20text=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/My/Contacts/ContactDetail/index.tsx | 2 +- packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactDetail/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactDetail/index.tsx index fa726f72c6..1f1c9fb883 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactDetail/index.tsx @@ -72,7 +72,7 @@ const ContactDetail: React.FC = () => { { navigationService.navigate('ContactEdit', { contact }); diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index b5e03da0b2..20d8a2b99a 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -162,7 +162,7 @@ const CustomToken: React.FC = () => { - + {t('Add')} From 416e60fc5f85a82113abda9e1aeaf27916cdc1dc Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 17:26:57 +0800 Subject: [PATCH 306/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20add=20tabs=20head?= =?UTF-8?q?er=20title?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/TabsDrawerContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index d1356c1480..1a537608d5 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -123,7 +123,7 @@ const TabsDrawerContent: React.FC = () => { From 3b89b9b5d87867c45417d4055d1232073f62bb17 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 17:37:48 +0800 Subject: [PATCH 307/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20headers?= =?UTF-8?q?tyle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/CustomHeader/style/index.style.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts b/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts index db3d7c7046..e4d8d806f5 100644 --- a/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts +++ b/packages/mobile-app-did/js/components/CustomHeader/style/index.style.ts @@ -23,7 +23,7 @@ export const blueStyles = StyleSheet.create({ flex: 1, }, centerWrap: { - flex: 1, + flex: 1.2, display: 'flex', justifyContent: 'center', alignItems: 'center', @@ -61,7 +61,7 @@ export const whitStyles = StyleSheet.create({ flex: 1, }, centerWrap: { - flex: 1, + flex: 1.2, display: 'flex', justifyContent: 'center', alignItems: 'center', From 5a603a205a1c6bd520cef9854619296d8b465b77 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 10 Jul 2023 17:38:08 +0800 Subject: [PATCH 308/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20max?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Send/SendHome/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index c9ef49bddb..35b59fb997 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -141,7 +141,7 @@ const SendHome: React.FC = () => { const onPressMax = useCallback(async () => { // check is SYNCHRONIZING const _isManagerSynced = await checkManagerSyncState(chainInfo?.chainId || 'AELF'); - if (_isManagerSynced) { + if (!_isManagerSynced) { return setErrorMessage([TransactionError.SYNCHRONIZING]); } From 72acb044472c7b842538a4df3f0bcffe33dec53a Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 10 Jul 2023 18:02:33 +0800 Subject: [PATCH 309/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adapt=20SetPinAn?= =?UTF-8?q?dAddManager=20ui=20sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/utils/wallet.ts | 4 +- packages/types/types-ca/guardian.ts | 8 - .../useOnManagerAddressAndQueryResult.ts | 6 +- .../app/web/pages/SetWalletPin/index.less | 17 +- .../app/web/pages/SetWalletPin/index.tsx | 329 ++++++------------ 5 files changed, 124 insertions(+), 240 deletions(-) diff --git a/packages/api/api-did/utils/wallet.ts b/packages/api/api-did/utils/wallet.ts index 361835e0a7..4a1f9298c1 100644 --- a/packages/api/api-did/utils/wallet.ts +++ b/packages/api/api-did/utils/wallet.ts @@ -1,4 +1,4 @@ -import { GuardiansApprovedType } from '@portkey-wallet/types/types-ca/guardian'; +import type { GuardiansApproved } from '@portkey/services'; import { LoginKeyType } from '@portkey-wallet/types/types-ca/wallet'; import { VerificationType } from '@portkey-wallet/types/verifier'; import { request } from '..'; @@ -35,7 +35,7 @@ interface RecoveryDIDWalletParams extends IContext { manager: string; extraData: string; chainId: string; - guardiansApproved: GuardiansApprovedType[]; + guardiansApproved: GuardiansApproved[]; } export const recoveryDIDWallet = async ( diff --git a/packages/types/types-ca/guardian.ts b/packages/types/types-ca/guardian.ts index 605f63ed0e..9f6dcbe6ea 100644 --- a/packages/types/types-ca/guardian.ts +++ b/packages/types/types-ca/guardian.ts @@ -27,11 +27,3 @@ export interface GuardiansInfo { guardianList: { guardians: Guardian[] }; managerInfos: Manager[]; } - -export interface GuardiansApprovedType { - type: LoginKeyType; - identifier: string; - verifierId: string; - verificationDoc: string; - signature: string; -} diff --git a/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts b/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts index 5d33fc3228..fb35510054 100644 --- a/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts +++ b/packages/web-extension-did/app/web/hooks/useOnManagerAddressAndQueryResult.ts @@ -8,7 +8,7 @@ import { extraDataEncode } from '@portkey-wallet/utils/device'; import { getDeviceInfo } from 'utils/device'; import { DEVICE_TYPE } from 'constants/index'; import { recoveryDIDWallet, registerDIDWallet } from '@portkey-wallet/api/api-did/utils/wallet'; -import { GuardiansApprovedType } from '@portkey-wallet/types/types-ca/guardian'; +import type { AccountType, GuardiansApproved } from '@portkey/services'; import { VerificationType, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import { setManagerInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; @@ -31,11 +31,11 @@ export function useOnManagerAddressAndQueryResult(state: string | undefined) { const originChainId = useOriginChainId(); - const getGuardiansApproved: () => GuardiansApprovedType[] = useCallback(() => { + const getGuardiansApproved: () => GuardiansApproved[] = useCallback(() => { return Object.values(userGuardianStatus ?? {}) .filter((guardian) => guardian.status === VerifyStatus.Verified) .map((guardian) => ({ - type: LoginType[guardian.guardianType], + type: LoginType[guardian.guardianType] as AccountType, identifier: guardian.guardianAccount, verifierId: guardian.verifier?.id || '', verificationDoc: guardian.verificationDoc || '', diff --git a/packages/web-extension-did/app/web/pages/SetWalletPin/index.less b/packages/web-extension-did/app/web/pages/SetWalletPin/index.less index 23e54ea0af..22325cf4ac 100644 --- a/packages/web-extension-did/app/web/pages/SetWalletPin/index.less +++ b/packages/web-extension-did/app/web/pages/SetWalletPin/index.less @@ -1,17 +1,10 @@ @import '../../assets/theme/color.less'; -.set-pin-wrapper { + +.set-wallet-pin { .set-pin-content { - > .title { - margin-bottom: 40px; - } - .@{app-prefix}-form-vertical .@{app-prefix}-form-item-row { - flex: 1; - } - .portkey-form-item-label { - padding-bottom: 4px; - } - .submit-btn { - border-radius: 22px; + height: 432px; + .portkey-ui-wrapper { + height: 100%; } } } diff --git a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx index 7ac53db219..9db2bcedfc 100644 --- a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx +++ b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx @@ -1,120 +1,74 @@ -import { Button, Form, message } from 'antd'; -import { FormItem } from 'components/BaseAntd'; -import ConfirmPassword from 'components/ConfirmPassword'; +import { Button, message } from 'antd'; import PortKeyTitle from 'pages/components/PortKeyTitle'; -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router'; import { useAppDispatch, useGuardiansInfo, useLoading, useLoginInfo } from 'store/Provider/hooks'; import { setPinAction } from 'utils/lib/serviceWorkerAction'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { createWallet, setManagerInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; +import { createWallet, resetWallet, setCAInfo } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useTranslation } from 'react-i18next'; -import { recoveryDIDWallet, registerDIDWallet } from '@portkey-wallet/api/api-did/utils/wallet'; -import { VerificationType, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { VerificationType } from '@portkey-wallet/types/verifier'; import { isWalletError } from '@portkey-wallet/store/wallet/utils'; import { useHardwareBack } from 'hooks/useHardwareBack'; -import CommonModal from 'components/CommonModal'; -import AElf from 'aelf-sdk'; -import { randomId } from '@portkey-wallet/utils'; -import useFetchDidWallet from 'hooks/useFetchDidWallet'; import { setPasswordSeed } from 'store/reducers/user/slice'; -import { DEVICE_TYPE } from 'constants/index'; -import { GuardiansApprovedType } from '@portkey-wallet/types/types-ca/guardian'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; -import { extraDataEncode } from '@portkey-wallet/utils/device'; -import { getDeviceInfo } from 'utils/device'; import { sendScanLoginSuccess } from '@portkey-wallet/api/api-did/message/utils'; import ModalTip from 'pages/components/ModalTip'; import './index.less'; -import { CreateAddressLoading, InitLoginLoading } from '@portkey-wallet/constants/constants-ca/wallet'; +import { + SetPinAndAddManager, + AddManagerType, + DIDWalletInfo, + CommonModal, + PortkeyStyleProvider, + CreatePendingInfo, + handleErrorMessage, +} from '@portkey/did-ui-react'; +import type { AccountType, GuardiansApproved } from '@portkey/services'; +import { getHolderInfo } from 'utils/sandboxUtil/getHolderInfo'; export default function SetWalletPin() { - const [form] = Form.useForm(); const { t } = useTranslation(); - const { type: state } = useParams<{ type: 'login' | 'scan' | 'register' }>(); + const loginType: AddManagerType = useMemo(() => (state === 'register' ? 'register' : 'recovery'), [state]); const navigate = useNavigate(); const dispatch = useAppDispatch(); const { setLoading } = useLoading(); const { walletInfo } = useCurrentWallet(); const [returnOpen, setReturnOpen] = useState(); const { scanWalletInfo, scanCaWalletInfo, loginAccount, registerVerifier } = useLoginInfo(); - const getWalletCAAddressResult = useFetchDidWallet(); - const network = useCurrentNetworkInfo(); const { userGuardianStatus } = useGuardiansInfo(); const originChainId = useOriginChainId(); console.log(walletInfo, state, scanWalletInfo, scanCaWalletInfo, 'walletInfo===caWallet'); - const requestRegisterDIDWallet = useCallback( - async ({ managerAddress }: { managerAddress: string }) => { - console.log(loginAccount, registerVerifier, 'requestRegisterDIDWallet=='); - if (!loginAccount?.guardianAccount || !LoginType[loginAccount.loginType]) - throw 'Missing account!!! Please login/register again'; - const requestId = randomId(); - if (!registerVerifier) throw 'Missing Verifier Server'; - const extraData = await extraDataEncode(getDeviceInfo(DEVICE_TYPE)); - const result = await registerDIDWallet({ - type: LoginType[loginAccount.loginType], - loginGuardianIdentifier: loginAccount.guardianAccount.replaceAll(' ', ''), - manager: managerAddress, - extraData, //navigator.userAgent, - chainId: originChainId, - verifierId: registerVerifier.verifierId, - verificationDoc: registerVerifier.verificationDoc, - signature: registerVerifier.signature, - context: { - clientId: managerAddress, - requestId, - }, - }); - return { - requestId, - sessionId: result.sessionId, - }; - }, - [loginAccount, originChainId, registerVerifier], - ); - - const getGuardiansApproved: () => GuardiansApprovedType[] = useCallback(() => { - return Object.values(userGuardianStatus ?? {}) - .filter((guardian) => guardian.status === VerifyStatus.Verified) - .map((guardian) => ({ - type: LoginType[guardian.guardianType], - identifier: guardian.guardianAccount, - verifierId: guardian.verifier?.id || '', - verificationDoc: guardian.verificationDoc || '', - signature: guardian.signature || '', - })); - }, [userGuardianStatus]); + useEffect(() => { + if (state === 'scan' && (!scanWalletInfo || !scanCaWalletInfo)) { + message.error('Wallet information is wrong, please go back to scan the code and try again'); + navigate('/register/start/scan'); + } + }, [navigate, scanCaWalletInfo, scanWalletInfo, state]); - const requestRecoveryDIDWallet = useCallback( - async ({ managerAddress }: { managerAddress: string }) => { - if (!loginAccount?.guardianAccount || !LoginType[loginAccount.loginType]) - throw 'Missing account!!! Please login/register again'; - const guardiansApproved = getGuardiansApproved(); - const requestId = randomId(); - const extraData = await extraDataEncode(getDeviceInfo(DEVICE_TYPE)); - const result = await recoveryDIDWallet({ - loginGuardianIdentifier: loginAccount.guardianAccount.replaceAll(' ', ''), - manager: managerAddress, - extraData, //navigator.userAgent, - chainId: originChainId, - guardiansApproved, - context: { - clientId: managerAddress, - requestId, + const approvedList: GuardiansApproved[] = useMemo(() => { + if (state === 'register') { + return [ + { + type: LoginType[loginAccount?.loginType as any] as AccountType, + identifier: loginAccount?.guardianAccount || '', + verifierId: registerVerifier?.verifierId || '', + verificationDoc: registerVerifier?.verificationDoc || '', + signature: registerVerifier?.signature || '', }, - }); - - return { - requestId, - sessionId: result.sessionId, - }; - }, - [loginAccount, getGuardiansApproved, originChainId], - ); + ]; + } + return Object.values(userGuardianStatus ?? {}).map((guardian) => ({ + type: LoginType[guardian.guardianType] as AccountType, + identifier: guardian.guardianAccount, + verifierId: guardian.verifier?.id || '', + verificationDoc: guardian.verificationDoc || '', + signature: guardian.signature || '', + })); + }, [loginAccount, registerVerifier, state, userGuardianStatus]); const createByScan = useCallback( async (pin: string) => { @@ -142,108 +96,76 @@ export default function SetWalletPin() { ); const onCreate = useCallback( - async (values: any) => { + async (value: DIDWalletInfo | string) => { try { - const { pin } = values; - console.log(state, 'state==='); - - if (state === 'scan') return createByScan(pin); - if (!loginAccount?.guardianAccount || !LoginType[loginAccount.loginType]) - return message.error('Missing account!!! Please login/register again'); - - if (loginAccount.createType === 'register') { - setLoading(true, t(CreateAddressLoading)); - } else { - setLoading(true, t(InitLoginLoading)); - } - const _walletInfo = walletInfo.address ? walletInfo : AElf.wallet.createNewWallet(); - console.log(pin, walletInfo.address, 'onCreate=='); - - // Step 9 - let sessionInfo = { - requestId: walletInfo.address, - sessionId: '', - }; - - if (state === 'register') { - sessionInfo = await requestRegisterDIDWallet({ managerAddress: _walletInfo.address }); - } else { - sessionInfo = await requestRecoveryDIDWallet({ managerAddress: _walletInfo.address }); - } - - const managerInfo = { - managerUniqueId: sessionInfo.sessionId, - requestId: sessionInfo.requestId, - loginAccount: loginAccount?.guardianAccount, - type: loginAccount.loginType, - verificationType: state === 'login' ? VerificationType.communityRecovery : VerificationType.register, - }; - - dispatch(setPasswordSeed(pin)); - - !walletInfo.address - ? dispatch( - createWallet({ - walletInfo: _walletInfo, - pin, - caInfo: { managerInfo }, - }), - ) - : dispatch( - setManagerInfo({ - networkType: network.networkType, - pin, - managerInfo, - }), - ); + if (state === 'scan' && typeof value === 'string') return createByScan(value); + if (typeof value !== 'object') return; + const result = await getHolderInfo({ + chainId: originChainId, + caHash: value.caInfo.caHash, + }); - setPinAction(pin); + const managerList: any[] = result.managerInfos; - // TODO Step 14 Only get Main Chain caAddress + if (!managerList.find((info) => info?.address === value.walletInfo.address)) + throw `${value.walletInfo.address} is not a manager`; - // Socket - await getWalletCAAddressResult({ - requestId: sessionInfo.requestId, - clientId: _walletInfo.address, - verificationType: state === 'login' ? VerificationType.communityRecovery : VerificationType.register, - managerUniqueId: sessionInfo.sessionId, - pwd: pin, - managerAddress: _walletInfo.address, - }); + dispatch( + setCAInfo({ + caInfo: value.caInfo, + pin: value.pin, + chainId: value.chainId, + }), + ); + const path = state ? 'register' : 'login'; + navigate(`/success-page/${path}`); setLoading(false); + ModalTip({ content: 'Requested successfully', }); } catch (error: any) { - console.log(error, 'onCreate==error'); + dispatch(resetWallet()); + setLoading(false); + const walletError = isWalletError(error); if (walletError) return message.error(walletError); - if (error?.message || error?.error?.message) return message.error(error?.message || error?.error?.message); - const errorString = typeof error === 'string' ? error : 'Something error'; - message.error(walletError || errorString); + message.error(handleErrorMessage(error, 'Create wallet failed')); } finally { setLoading(false); } }, - [ - setLoading, - state, - network, - createByScan, - loginAccount, - walletInfo, - dispatch, - getWalletCAAddressResult, - requestRegisterDIDWallet, - requestRecoveryDIDWallet, - t, - ], + [state, createByScan, originChainId, dispatch, navigate, setLoading], ); - const onFinishFailed = useCallback((errorInfo: any) => { - console.error(errorInfo, 'onFinishFailed=='); - message.error('Something error'); - }, []); + const onCreatePending = useCallback( + async (info: CreatePendingInfo) => { + try { + const verificationType = state === 'login' ? VerificationType.communityRecovery : VerificationType.register; + const managerInfo = { + managerUniqueId: info.sessionId, + requestId: info.requestId, + loginAccount: loginAccount?.guardianAccount as string, + type: loginAccount?.loginType as LoginType, + verificationType, + }; + + const pin = info.pin; + dispatch(setPasswordSeed(pin)); + dispatch( + createWallet({ + walletInfo: info.walletInfo, + pin, + caInfo: { managerInfo }, + }), + ); + await setPinAction(pin); + } catch (error) { + console.log('onCreatePending error:', error); + } + }, + [dispatch, loginAccount?.guardianAccount, loginAccount?.loginType, state], + ); const backHandler = useCallback(async () => { switch (state) { @@ -275,54 +197,31 @@ export default function SetWalletPin() { }); return ( -
    +
    -
    {t('Enter Pin to Protect Your Wallet')}
    -
    - {/* eslint-disable-next-line no-inline-styles/no-inline-styles */} - - - - - - {() => ( - - )} - -
    + onCreatePending={onCreatePending} + />
    - -

    {t('returnTip')}

    -
    - - -
    -
    + + +

    {t('returnTip')}

    +
    + + +
    +
    +
    ); } From 08820ef047e6d1964b4d79b13a73a3d2d3ada27b Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 10 Jul 2023 19:02:42 +0800 Subject: [PATCH 310/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20api=20v?= =?UTF-8?q?ersion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/fetch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/fetch.ts b/packages/utils/fetch.ts index 2c810fc3a0..446752044f 100644 --- a/packages/utils/fetch.ts +++ b/packages/utils/fetch.ts @@ -15,7 +15,7 @@ const defaultHeaders = { Accept: 'text/plain;v=1.0', 'Content-Type': 'application/json', // FIXME: delete - version: 'v1.3.0', + version: 'v1.3.2', }; function formatResponse(response: string) { From 50e65ab0cee5aa2f29b93e9becab1472f2f6a0fe Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 10 Jul 2023 19:24:15 +0800 Subject: [PATCH 311/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adapt=20ui=20sdk?= =?UTF-8?q?=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/guardians/type.ts | 2 +- .../app/web/components/Loading/data.json | 1 - .../app/web/components/Loading/index.tsx | 31 ------------ .../web/components/ScreenLoading/index.less | 38 --------------- .../web/components/ScreenLoading/index.tsx | 34 -------------- .../app/web/pages/VerifierAccount/index.tsx | 47 +++++++++++++++---- .../app/web/store/Provider/hooks.ts | 13 ++--- .../app/web/store/Provider/index.tsx | 2 - 8 files changed, 44 insertions(+), 124 deletions(-) delete mode 100644 packages/web-extension-did/app/web/components/Loading/data.json delete mode 100644 packages/web-extension-did/app/web/components/Loading/index.tsx delete mode 100644 packages/web-extension-did/app/web/components/ScreenLoading/index.less delete mode 100644 packages/web-extension-did/app/web/components/ScreenLoading/index.tsx diff --git a/packages/store/store-ca/guardians/type.ts b/packages/store/store-ca/guardians/type.ts index 6984293a65..a5aabcd1a4 100644 --- a/packages/store/store-ca/guardians/type.ts +++ b/packages/store/store-ca/guardians/type.ts @@ -18,7 +18,7 @@ export interface BaseGuardianItem { export interface IVerifierInfo { sessionId: string; - endPoint: string; + endPoint?: string; } export interface UserGuardianItem extends BaseGuardianItem { diff --git a/packages/web-extension-did/app/web/components/Loading/data.json b/packages/web-extension-did/app/web/components/Loading/data.json deleted file mode 100644 index 18e24d52aa..0000000000 --- a/packages/web-extension-did/app/web/components/Loading/data.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.6.9","fr":30,"ip":0,"op":32,"w":132,"h":132,"nm":"image_processing20210907-2077-1j38mb7","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[66,66,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":165,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 9","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":45,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.94],"y":[0.806]},"o":{"x":[0.06],"y":[0.078]},"t":6,"s":[10]},{"i":{"x":[0.94],"y":[0.833]},"o":{"x":[0.06],"y":[0.417]},"t":11,"s":[-8.751]},{"i":{"x":[0.94],"y":[2.458]},"o":{"x":[0.06],"y":[-2.917]},"t":16,"s":[-17.5]},{"i":{"x":[0.97],"y":[1.106]},"o":{"x":[0.03],"y":[-0.106]},"t":21,"s":[-17.5]},{"t":31,"s":[10]}],"ix":4}},"a":{"a":0,"k":[141,-9.812,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141,1.125],[141,-20.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.356862745098,0.603921568627,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":7,"s":[0]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":12,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[99]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":22,"s":[99]},{"i":{"x":[0.618],"y":[0.753]},"o":{"x":[0.173],"y":[0]},"t":27,"s":[99]},{"i":{"x":[0.691],"y":[1]},"o":{"x":[0.352],"y":[-5.587]},"t":31,"s":[1]},{"t":32,"s":[1]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[1]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":7,"s":[15]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":12,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[100]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":22,"s":[100]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":27,"s":[0]},{"t":32,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":168.90162901629,"st":6,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 8","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":15,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.94],"y":[0.806]},"o":{"x":[0.06],"y":[0.078]},"t":4,"s":[10]},{"i":{"x":[0.94],"y":[0.833]},"o":{"x":[0.06],"y":[0.417]},"t":9,"s":[-8.751]},{"i":{"x":[0.94],"y":[2.458]},"o":{"x":[0.06],"y":[-2.917]},"t":14,"s":[-17.5]},{"i":{"x":[0.97],"y":[1.106]},"o":{"x":[0.03],"y":[-0.106]},"t":19,"s":[-17.5]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[-1.62]},"t":29,"s":[10]},{"t":30,"s":[10]}],"ix":4}},"a":{"a":0,"k":[141,-9.812,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141,1.125],[141,-20.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.254901960784,0.521568627451,0.956862745098,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":5,"s":[0]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":10,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[99]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":20,"s":[99]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":25,"s":[99]},{"t":30,"s":[1]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[1]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":5,"s":[15]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":10,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[100]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":20,"s":[100]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":25,"s":[0]},{"t":30,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":166.90162901629,"st":4,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 7","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":-15,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.94],"y":[0.806]},"o":{"x":[0.06],"y":[0.078]},"t":2,"s":[10]},{"i":{"x":[0.94],"y":[0.833]},"o":{"x":[0.06],"y":[0.417]},"t":7,"s":[-8.751]},{"i":{"x":[0.94],"y":[2.458]},"o":{"x":[0.06],"y":[-2.917]},"t":12,"s":[-17.5]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.03],"y":[-0.117]},"t":17,"s":[-17.5]},{"t":28,"s":[10]}],"ix":4}},"a":{"a":0,"k":[141,-9.812,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141,1.125],[141,-20.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.356862745098,0.603921568627,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":8,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[99]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":18,"s":[99]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":23,"s":[99]},{"t":28,"s":[1]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[1]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":3,"s":[15]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":8,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":13,"s":[100]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":18,"s":[100]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":23,"s":[0]},{"t":28,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":164.90162901629,"st":2,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 6","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.94],"y":[1]},"o":{"x":[0.06],"y":[0]},"t":0,"s":[-45]},{"i":{"x":[0.94],"y":[1]},"o":{"x":[0.06],"y":[0]},"t":5,"s":[-45]},{"i":{"x":[0.94],"y":[1]},"o":{"x":[0.06],"y":[0]},"t":10,"s":[-45.5]},{"i":{"x":[0.97],"y":[1]},"o":{"x":[0.03],"y":[0]},"t":15,"s":[-45]},{"t":25,"s":[-45]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.94],"y":[0.806]},"o":{"x":[0.06],"y":[0.078]},"t":0,"s":[10]},{"i":{"x":[0.94],"y":[0.833]},"o":{"x":[0.06],"y":[0.417]},"t":5,"s":[-8.751]},{"i":{"x":[0.94],"y":[2.458]},"o":{"x":[0.06],"y":[-2.917]},"t":10,"s":[-17.5]},{"i":{"x":[0.97],"y":[1.106]},"o":{"x":[0.03],"y":[-0.106]},"t":15,"s":[-17.5]},{"t":25,"s":[10]}],"ix":4}},"a":{"a":0,"k":[141,-9.812,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[141,1.125],[141,-20.75]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.098039217293,0.427450984716,0.929411768913,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.18],"y":[1]},"o":{"x":[0.28],"y":[0]},"t":6,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":11,"s":[99]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":16,"s":[99]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":21,"s":[99]},{"t":26,"s":[1]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[1]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":1,"s":[15]},{"i":{"x":[0.492],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":6,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":11,"s":[100]},{"i":{"x":[0.13],"y":[1]},"o":{"x":[0.18],"y":[0]},"t":16,"s":[100]},{"i":{"x":[0.67],"y":[1]},"o":{"x":[0.17],"y":[0]},"t":21,"s":[0]},{"t":26,"s":[0]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":162.90162901629,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/packages/web-extension-did/app/web/components/Loading/index.tsx b/packages/web-extension-did/app/web/components/Loading/index.tsx deleted file mode 100644 index 1201bb3ae8..0000000000 --- a/packages/web-extension-did/app/web/components/Loading/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useRef, useEffect } from 'react'; -import lottie, { AnimationItem } from 'lottie-web'; -import animationData from './data.json'; - -const LoadingIndicator = () => { - const containerRef = useRef(null); - const animation = useRef(null); - - useEffect(() => { - if (!animation.current) { - animation.current = lottie.loadAnimation({ - container: containerRef.current!, - renderer: 'svg', - loop: true, - autoplay: true, - animationData: animationData, - }); - } - return () => { - if (animation.current) { - animation.current.stop(); - animation.current.destroy(); - animation.current = null; - } - }; - }, []); - - return
    ; -}; - -export default LoadingIndicator; diff --git a/packages/web-extension-did/app/web/components/ScreenLoading/index.less b/packages/web-extension-did/app/web/components/ScreenLoading/index.less deleted file mode 100644 index 3b4f5f81b8..0000000000 --- a/packages/web-extension-did/app/web/components/ScreenLoading/index.less +++ /dev/null @@ -1,38 +0,0 @@ -@import '../../assets/theme/color.less'; - -.portkey-loading-wrapper { - background-color: rgb(00 00 00 / 30%); - z-index: 9999; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - - .loading-indicator { - width: 224px; - min-height: 120px; - padding: 20px; - background-color: @bg-11; - border-radius: 6px; - - .loading { - width: 44px; - height: 44px; - } - .loading-text { - margin-top: 8px; - max-width: 210px; - font-size: 14px; - line-height: 20px; - color: @font-11; - } - .ellipsis { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .center { - text-align: center; - } - } -} diff --git a/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx b/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx deleted file mode 100644 index fbb97fb8e8..0000000000 --- a/packages/web-extension-did/app/web/components/ScreenLoading/index.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import Loading from 'components/Loading'; -import { useAppSelector } from 'store/Provider/hooks'; -import './index.less'; -import clsx from 'clsx'; - -export default function ScreenLoading() { - const { - loadingInfo: { isLoading, loadingText, isEllipsis }, - } = useAppSelector((state) => state.userInfo); - - console.log(isLoading, 'isLoading==='); - return ( - <> - {!!isLoading && ( -
    -
    - -
    - {loadingText ? loadingText : 'Loading...'} -
    -
    -
    - )} - - ); -} diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 63f2a16b49..a2b1e0b141 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -1,10 +1,12 @@ import { useNavigate } from 'react-router'; -import VerifierPage from 'pages/components/VerifierPage'; import { useAppDispatch, useLoginInfo, useGuardiansInfo, useUserInfo, useLoading } from 'store/Provider/hooks'; import { useCallback, useMemo } from 'react'; import { message } from 'antd'; -import { setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; -import { OperationTypeEnum, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { + setUserGuardianItemStatus, + setUserGuardianSessionIdAction, +} from '@portkey-wallet/store/store-ca/guardians/actions'; +import { OperationTypeEnum, VerifierInfo, VerifierItem, VerifyStatus } from '@portkey-wallet/types/verifier'; import useLocationState from 'hooks/useLocationState'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { handleGuardian } from 'utils/sandboxUtil/handleGuardian'; @@ -23,6 +25,9 @@ import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQ import { useCommonState } from 'store/Provider/hooks'; import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; +import { CodeVerify, OnErrorFunc } from '@portkey/did-ui-react'; +import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; +import type { AccountType } from '@portkey/services'; export default function VerifierAccount() { const { loginAccount } = useLoginInfo(); @@ -218,20 +223,44 @@ export default function VerifierAccount() { } }, [state]); + const onReSend = useCallback( + (res: { verifier: VerifierItem; verifierSessionId: string }) => { + dispatch( + setUserGuardianSessionIdAction({ + key: currentGuardian?.key ?? `${currentGuardian?.guardianAccount}&${currentGuardian?.verifier?.name}`, + verifierInfo: { + sessionId: res.verifierSessionId, + }, + }), + ); + }, + [currentGuardian, dispatch], + ); + + const onError: OnErrorFunc = useCallback((error) => { + console.log('CodeVerify:', error); + }, []); + const renderContent = useMemo( () => (
    -
    ), - [currentGuardian, isInitStatus, loginAccount, onSuccess, operationType], + [currentGuardian, isInitStatus, loginAccount, onError, onReSend, onSuccess, operationType, originChainId], ); const props = useMemo( diff --git a/packages/web-extension-did/app/web/store/Provider/hooks.ts b/packages/web-extension-did/app/web/store/Provider/hooks.ts index 4c9ae0639e..83bf1659f4 100644 --- a/packages/web-extension-did/app/web/store/Provider/hooks.ts +++ b/packages/web-extension-did/app/web/store/Provider/hooks.ts @@ -1,7 +1,7 @@ import { OpacityType } from '@portkey-wallet/types'; import { useCallback, useMemo } from 'react'; import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import { setGlobalLoading } from 'store/reducers/user/slice'; +import { LoadingInfoType, setLoading } from '@portkey/did-ui-react'; import type { RootState, AppDispatch } from './store'; // Use throughout your app instead of plain `useDispatch` and `useSelector` @@ -23,12 +23,9 @@ export const useCommonState = () => useAppSelector((state) => state.common); export const usePayment = () => useAppSelector((state) => state.payment); export const useDapp = () => useAppSelector((state) => state.dapp); export const useLoading = () => { - const { loadingInfo } = useAppSelector((state) => state.userInfo); - const dispatch = useAppDispatch(); - const setLoading = useCallback( - (isLoading: boolean | OpacityType, loadingText?: string, isEllipsis = true) => - dispatch(setGlobalLoading({ isLoading, loadingText, isEllipsis })), - [dispatch], + const _setLoading = useCallback( + (isLoading: boolean | OpacityType, loadingInfo?: LoadingInfoType) => setLoading(isLoading, loadingInfo), + [], ); - return useMemo(() => ({ isLoading: !!loadingInfo.isLoading, setLoading }), [loadingInfo.isLoading, setLoading]); + return useMemo(() => ({ setLoading: _setLoading }), [_setLoading]); }; diff --git a/packages/web-extension-did/app/web/store/Provider/index.tsx b/packages/web-extension-did/app/web/store/Provider/index.tsx index 836778a05c..4f9878793e 100644 --- a/packages/web-extension-did/app/web/store/Provider/index.tsx +++ b/packages/web-extension-did/app/web/store/Provider/index.tsx @@ -1,6 +1,5 @@ import { ConfigProvider } from 'antd'; import ErrorBoundary from 'components/ErrorBoundary'; -import ScreenLoading from 'components/ScreenLoading'; import { useLanguage } from 'i18n'; import { ANTD_LOCAL } from 'i18n/config'; import Modals from 'models'; @@ -62,7 +61,6 @@ export default function ContextProviders({ - From 9ad081816fd324f1708d79a533c221787f5b3822 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 10 Jul 2023 19:25:00 +0800 Subject: [PATCH 312/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20delete=20param?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/hooks/useHandleAchSell.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts index 4a01cc369f..bdcff3af1c 100644 --- a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts +++ b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts @@ -69,7 +69,7 @@ export const useHandleAchSell = () => { return useCallback( async (orderId: string) => { try { - setLoading(true, 'Payment is being processed and may take around 10 seconds to complete.', false); + setLoading(true, 'Payment is being processed and may take around 10 seconds to complete.'); await sellTransfer({ merchantName: ACH_MERCHANT_NAME, orderId, From 47231fb0f66dc37e7de1a2037a096079f7a578c0 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 11 Jul 2023 10:58:53 +0800 Subject: [PATCH 313/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20deviceList?= =?UTF-8?q?=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/wallet.ts | 20 +++-- .../pages/My/WalletSecurity/Device/index.tsx | 74 ++++++++++++++----- .../components/DeviceDetailCom/index.tsx | 4 +- .../components/DeviceList/index.tsx | 4 +- 4 files changed, 73 insertions(+), 29 deletions(-) diff --git a/packages/hooks/hooks-ca/wallet.ts b/packages/hooks/hooks-ca/wallet.ts index d2500efe4d..370733833b 100644 --- a/packages/hooks/hooks-ca/wallet.ts +++ b/packages/hooks/hooks-ca/wallet.ts @@ -5,7 +5,7 @@ import { CAInfoType } from '@portkey-wallet/types/types-ca/wallet'; import { WalletState } from '@portkey-wallet/store/store-ca/wallet/type'; import { useCurrentNetworkInfo } from './network'; import { useCurrentChain, useCurrentChainList } from './chainList'; -import { useCaHolderManagerInfoQuery } from '@portkey-wallet/graphql/contract/__generated__/hooks/caHolderManagerInfo'; +import { useCaHolderManagerInfoLazyQuery } from '@portkey-wallet/graphql/contract/__generated__/hooks/caHolderManagerInfo'; import { getApolloClient } from '@portkey-wallet/graphql/contract/apollo'; import { request } from '@portkey-wallet/api/api-did'; import { useAppCommonDispatch } from '../index'; @@ -14,13 +14,14 @@ import { DeviceInfoType } from '@portkey-wallet/types/types-ca/device'; import { extraDataListDecode } from '@portkey-wallet/utils/device'; import { ChainId } from '@portkey-wallet/types'; import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; +import useEffectOnce from 'hooks/useEffectOnce'; export interface CurrentWalletType extends WalletInfoType, CAInfoType { caHash?: string; caAddressList?: string[]; } -export interface IDeviceList { +export interface IDeviceItem { managerAddress: string | null | undefined; deviceInfo: DeviceInfoType; transactionTime: number; @@ -75,12 +76,13 @@ export const useCurrentWallet = () => { }, [originChainId, wallet]); }; -export const useDeviceList = () => { +export const useDeviceList = (isInitLoad = true) => { const networkInfo = useCurrentNetworkInfo(); const walletInfo = useCurrentWalletInfo(); const originChainId = useOriginChainId(); const chainInfo = useCurrentChain(originChainId); - const { data, error, refetch, loading } = useCaHolderManagerInfoQuery({ + + const [load, { data, error, loading, refetch }] = useCaHolderManagerInfoLazyQuery({ client: getApolloClient(networkInfo.networkType), variables: { dto: { @@ -90,10 +92,10 @@ export const useDeviceList = () => { maxResultCount: 100, }, }, - fetchPolicy: 'cache-and-network', + fetchPolicy: 'no-cache', }); - const [deviceList, setDeviceList] = useState([]); + const [deviceList, setDeviceList] = useState([]); const [deviceAmount, setDeviceAmount] = useState(0); const [decodeLoading, setDecodeLoading] = useState(false); @@ -128,7 +130,11 @@ export const useDeviceList = () => { getDeviceList(); }, [getDeviceList]); - return { deviceList, refetch, deviceAmount, loading: loading || decodeLoading }; + useEffectOnce(() => { + isInitLoad && load(); + }); + + return { load, deviceList, refetch, deviceAmount, loading: loading || decodeLoading }; }; export const useSetWalletName = () => { diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx index c812ebbc66..5c6ed3e308 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx @@ -1,40 +1,50 @@ -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import PageContainer from 'components/PageContainer'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, FlatList } from 'react-native'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { TextM } from 'components/CommonText'; -import { useCurrentWalletInfo, useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { IDeviceItem, useCurrentWalletInfo, useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import DeviceItem from './components/DeviceItem'; import navigationService from 'utils/navigationService'; import { FontStyles } from 'assets/theme/styles'; import { pTd } from 'utils/unit'; import myEvents from 'utils/deviceEvent'; +import useEffectOnce from 'hooks/useEffectOnce'; const DeviceList: React.FC = () => { - const { deviceList, refetch } = useDeviceList(); + const { deviceList, refetch, loading: isRefreshing } = useDeviceList(false); const walletInfo = useCurrentWalletInfo(); + const isLoadingRef = useRef(false); + const getDeviceList = useCallback(async () => { + if (isLoadingRef.current) return; + isLoadingRef.current = true; + await refetch(); + isLoadingRef.current = false; + }, [refetch]); + + useEffectOnce(() => { + const timer = setTimeout(() => { + getDeviceList(); + }, 100); + return () => { + clearTimeout(timer); + }; + }); + useEffect(() => { const listener = myEvents.refreshDeviceList.addListener(() => { - refetch(); + getDeviceList(); }); return () => { listener.remove(); }; - }, [refetch]); + }, [getDeviceList]); - return ( - - - {`You can manage your login devices and remove any device. -Please note that when you log in again on a removed device, you will need to verify your identity through your guardians.`} - - {deviceList.map(item => ( + const renderItem = useCallback( + ({ item }: { item: IDeviceItem }) => { + return ( - ))} + ); + }, + [walletInfo.address], + ); + + return ( + + `${index}`} + renderItem={renderItem} + onRefresh={getDeviceList} + onEndReached={getDeviceList} + ListHeaderComponent={ + + {`You can manage your login devices and remove any device. +Please note that when you log in again on a removed device, you will need to verify your identity through your guardians.`} + + } + /> ); }; @@ -51,6 +86,9 @@ Please note that when you log in again on a removed device, you will need to ver const pageStyles = StyleSheet.create({ pageWrap: { backgroundColor: defaultColors.bg4, + padding: 0, + }, + listWrap: { ...GStyles.paddingArg(24, 20, 18), }, tipsWrap: { diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceDetailCom/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceDetailCom/index.tsx index 8444f98fc3..e3e099ecec 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceDetailCom/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceDetailCom/index.tsx @@ -1,4 +1,4 @@ -import { IDeviceList, useCurrentWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { IDeviceItem, useCurrentWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { DeviceType } from '@portkey-wallet/types/types-ca/device'; import { Button } from 'antd'; import CustomSvg from 'components/CustomSvg'; @@ -8,7 +8,7 @@ import { getDeviceIcon } from 'utils/device'; import './index.less'; export interface IDeviceDetailComProps { - device: IDeviceList; + device: IDeviceItem; isCurrent: boolean; onDelete: () => Promise; } diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceList/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceList/index.tsx index 7264354759..9981f3171f 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceList/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/DeviceList/index.tsx @@ -1,13 +1,13 @@ import CustomSvg from 'components/CustomSvg'; import { getDeviceIcon } from 'utils/device'; import { DeviceItemType, DeviceType } from '@portkey-wallet/types/types-ca/device'; -import { IDeviceList, useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { IDeviceItem, useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { dateFormat } from 'utils'; import { useTranslation } from 'react-i18next'; import './index.less'; export interface IDeviceListProps { - list: IDeviceList[]; + list: IDeviceItem[]; onClick: (item: DeviceItemType) => void; } From b03c45eeeed62cf5f4f043249bb50b0b384c9fd6 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 11 Jul 2023 10:59:00 +0800 Subject: [PATCH 314/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20check=20decimals?= =?UTF-8?q?=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web-extension-did/app/web/pages/Token/Custom/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx index 5b0b775e84..dd16f31c2d 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx @@ -61,8 +61,8 @@ export default function CustomToken() { chainId, }, }); - const { symbol, decimals, id } = res; - if (symbol && decimals && id) { + const { symbol, id } = res; + if (symbol && id) { setCurToken(res); setValue(symbol); } From 6b55e6f2771084ac82c31fd7cc2ffdac0fd9dcd3 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 11 Jul 2023 11:04:47 +0800 Subject: [PATCH 315/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20decimals=20is=20z?= =?UTF-8?q?ero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 20d8a2b99a..288c056c8b 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -65,9 +65,9 @@ const CustomToken: React.FC = () => { chainId: tokenItem.chainId, }, }); - const { symbol, decimals, id } = res; + const { symbol, id } = res || {}; - if (symbol && decimals && id) { + if (symbol && id) { setTokenItem(pre => ({ ...pre, ...res })); setKeyword(symbol); setBtnDisable(false); From 367626d12f623179f4ba67d0438379cdb2400d43 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 11 Jul 2023 11:20:42 +0800 Subject: [PATCH 316/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20sentry=20cap?= =?UTF-8?q?ture=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/components/ErrorBoundary/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx b/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx index dcdeb06cb6..fca38e62a7 100644 --- a/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx +++ b/packages/web-extension-did/app/web/components/ErrorBoundary/index.tsx @@ -16,9 +16,7 @@ export default function ErrorBoundary({ children, view, pageType }: ErrorBoundar const isPrompt = useMemo(() => pageType === 'Prompt', [pageType]); const onError = useCallback( ({ error, componentStack }: Omit) => { - const sendError = handleReportError({ error, componentStack, view }); - console.log(sendError, '====sendError'); - Sentry.captureException({ error, componentStack, view }); + Sentry.captureException(handleReportError({ error, componentStack, view }), { level: 'error' }); }, [view], ); From b8f37659c4354a716b0172bfc3b80fa3fade1e64 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 11 Jul 2023 11:41:19 +0800 Subject: [PATCH 317/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20book=20mark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/ErrorBoundary/index.tsx | 2 +- .../Bookmark/components/BookmarkItem.tsx | 83 ++++++ .../js/pages/Discover/Bookmark/context.tsx | 47 ++++ .../js/pages/Discover/Bookmark/index.tsx | 61 +++++ .../js/pages/Discover/index.tsx | 3 +- packages/mobile-app-did/package.json | 2 + yarn.lock | 258 ++++++++++++++++++ 7 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/context.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx diff --git a/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx b/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx index 101f813d8b..dcca695356 100644 --- a/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx +++ b/packages/mobile-app-did/js/components/ErrorBoundary/index.tsx @@ -26,7 +26,7 @@ class ReactNativeErrorBoundary extends ReactErrorBoundary { export default function ErrorBoundary({ children, view }: ErrorBoundaryProps) { const onCaptureException = useCallback( ({ error, componentStack }: Omit) => { - exceptionManager.reportError(handleReportError({ error, componentStack: componentStack, view }), Severity.Error); + exceptionManager.reportError(handleReportError({ error, componentStack, view }), Severity.Error); }, [view], ); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx new file mode 100644 index 0000000000..41aa6ec74a --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -0,0 +1,83 @@ +import GStyles from 'assets/theme/GStyles'; +import { TextM } from 'components/CommonText'; +import Touchable from 'components/Touchable'; +import React, { memo, useCallback, useEffect, useRef } from 'react'; +import { StyleSheet } from 'react-native'; +import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; +import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; +import { useBookmark } from '../context'; +import usePrevious from 'hooks/usePrevious'; +import { BGStyles } from 'assets/theme/styles'; + +export default memo( + function BookmarkItem(props: RenderItemParams) { + const { drag, isActive, item } = props; + const swipeableRef = useRef(null); + const [{ isEdit }] = useBookmark(); + const preIsEdit = usePrevious(isEdit); + useEffect(() => { + if (!isEdit && isEdit !== preIsEdit) swipeableRef.current?.close(); + }, [preIsEdit, isEdit]); + + const renderUnderlayLeft = useCallback( + () => ( + swipeableRef.current?.close()}> + close + + ), + [], + ); + + return ( + + + + {isEdit && ( + swipeableRef.current?.open(OpenDirection.LEFT)}> + open + + )} + {item} + + + + ); + }, + (prevProps: RenderItemParams, nextProps: RenderItemParams) => { + return prevProps.item === nextProps.item && prevProps.isActive === nextProps.isActive; + }, +); + +const styles = StyleSheet.create({ + marginContainer: { + marginHorizontal: 16, + }, + underlayLeftBox: { + flex: 1, + paddingRight: 30, + flexDirection: 'row', + alignItems: 'center', + marginHorizontal: 16, + justifyContent: 'flex-end', + }, + itemRow: { + padding: 20, + height: 80, + marginVertical: 10, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/context.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/context.tsx new file mode 100644 index 0000000000..793b7532cd --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/context.tsx @@ -0,0 +1,47 @@ +import { basicActions } from 'contexts/utils'; +import React, { ReactNode, createContext, useContext, useMemo, useReducer } from 'react'; + +export interface BookmarkState { + isEdit?: boolean; +} +export const BookmarkContext = createContext<[BookmarkState, React.Dispatch] | []>([]); + +export function useBookmark() { + return useContext(BookmarkContext) as [BookmarkState, React.Dispatch]; +} + +export enum BookmarkActions { + setEdit = 'setEdit', +} + +// reducer +export function reducer(state: BookmarkState, { type, payload }: any) { + switch (type) { + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +} + +// actions +export const basicBookmarkActions = { + setEdit: (isEdit: boolean) => basicActions(BookmarkActions.setEdit, { isEdit }), +}; + +export const { setEdit } = basicBookmarkActions; + +// provider +export const INITIAL_STATE = {}; + +export function BookmarkProvider({ children }: { children?: ReactNode | undefined }) { + const [state, dispatch] = useReducer(reducer, INITIAL_STATE); + + const value = useMemo(() => [state, dispatch], [state]); + return ( + ]}> + {children} + + ); +} diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx new file mode 100644 index 0000000000..96a716112c --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx @@ -0,0 +1,61 @@ +import React, { useMemo, useState } from 'react'; +import PageContainer from 'components/PageContainer'; +import DraggableFlatList from 'react-native-draggable-flatlist'; +import GStyles from 'assets/theme/GStyles'; +import { StyleSheet, View } from 'react-native'; +import { BookmarkProvider, setEdit, useBookmark } from './context'; +import CommonButton from 'components/CommonButton'; +import BookmarkItem from './components/BookmarkItem'; +import { FontStyles } from 'assets/theme/styles'; + +const mockData = ['1', '2', '3', '4']; + +function Bookmark() { + const [list, setList] = useState(mockData); + const [{ isEdit }, dispatch] = useBookmark(); + + const BottomBox = useMemo( + () => ( + + {isEdit ? ( + <> + + dispatch(setEdit(false))} title="Done" type="primary" /> + + ) : ( + dispatch(setEdit(true))} title="Edit" type="primary" /> + )} + + ), + [dispatch, isEdit], + ); + return ( + + + _item} + renderItem={props => } + onDragEnd={({ data }) => setList(data)} + /> + + {BottomBox} + + ); +} + +export default function Container() { + return ( + + + + ); +} + +const styles = StyleSheet.create({ + // remove padding to scale item + containerStyles: { ...GStyles.paddingArg(0) }, + paddingContainer: { + paddingHorizontal: 16, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/index.tsx b/packages/mobile-app-did/js/pages/Discover/index.tsx index 7874314f6f..b33595da75 100644 --- a/packages/mobile-app-did/js/pages/Discover/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/index.tsx @@ -1,11 +1,12 @@ import DiscoverSearch from './DiscoverSearch'; import Browser from './Browser'; import DiscoverHome from './DiscoverHome'; - +import Bookmark from './Bookmark'; const stackNav = [ { name: 'DiscoverSearch', component: DiscoverSearch }, { name: 'Browser', component: Browser }, { name: 'DiscoverHome', component: DiscoverHome }, + { name: 'Bookmark', component: Bookmark }, ] as const; export default stackNav; diff --git a/packages/mobile-app-did/package.json b/packages/mobile-app-did/package.json index 8707bafc37..15b34024a3 100644 --- a/packages/mobile-app-did/package.json +++ b/packages/mobile-app-did/package.json @@ -113,6 +113,7 @@ "react-native-config": "^1.4.6", "react-native-crypto": "^2.2.0", "react-native-drawer-layout": "^3.2.0", + "react-native-draggable-flatlist": "^4.0.1", "react-native-echarts-pro": "^1.8.5", "react-native-flipper": "^0.161.0", "react-native-fs": "^2.20.0", @@ -132,6 +133,7 @@ "react-native-svg": "^13.2.0", "react-native-svg-uri": "^1.2.3", "react-native-switch-toggle": "^2.2.1", + "react-native-swipeable-item": "^2.0.9", "react-native-tab-view": "^3.2.1", "react-native-vector-icons": "^9.1.0", "react-native-view-shot": "^3.6.0", diff --git a/yarn.lock b/yarn.lock index 3f98247abb..984c82900e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -195,6 +195,13 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.3": version "7.19.3" resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151" @@ -296,6 +303,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.22.7": + version "7.22.7" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.22.7.tgz#a6b8152d5a621893f2c9dacf9a4e286d520633d5" + integrity sha512-p+jPjMG+SI8yvIaxGgeW24u7q9+5+TGpZh8/CuB7RhBKd7RCy8FayNEFNNKrNK/eUcY/4ExQqLmyrvBXKsIcwQ== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -303,6 +320,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" @@ -360,6 +384,21 @@ "@babel/helper-split-export-declaration" "^7.18.6" semver "^6.3.0" +"@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz#58564873c889a6fea05a538e23f9f6d201f10950" + integrity sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" @@ -390,6 +429,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" @@ -422,6 +466,14 @@ "@babel/template" "^7.20.7" "@babel/types" "^7.21.0" +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-get-function-arity@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15" @@ -436,6 +488,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.18.9": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" @@ -450,6 +509,13 @@ dependencies: "@babel/types" "^7.22.3" +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -464,6 +530,13 @@ dependencies: "@babel/types" "^7.21.4" +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" @@ -506,6 +579,20 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.0" +"@babel/helper-module-transforms@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" + integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -513,6 +600,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" @@ -528,6 +622,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" @@ -561,6 +660,18 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.0" +"@babel/helper-replace-supers@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" + integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/helper-simple-access@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" @@ -582,6 +693,13 @@ dependencies: "@babel/types" "^7.21.5" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.18.9": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" @@ -596,6 +714,13 @@ dependencies: "@babel/types" "^7.20.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" @@ -610,6 +735,13 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-split-export-declaration@^7.22.5", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.18.10": version "7.18.10" resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" @@ -625,11 +757,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + "@babel/helper-validator-option@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" @@ -640,6 +782,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + "@babel/helper-wrap-function@^7.18.9": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" @@ -686,6 +833,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.19.3", "@babel/parser@^7.7.0", "@babel/parser@^7.9.4": version "7.19.3" resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a" @@ -706,6 +862,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== +"@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -975,6 +1136,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-syntax-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.npmmirror.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -1045,6 +1213,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.20.2" +"@babel/plugin-syntax-typescript@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.18.6": version "7.18.6" resolved "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" @@ -1202,6 +1377,15 @@ "@babel/helper-plugin-utils" "^7.21.5" "@babel/helper-simple-access" "^7.21.5" +"@babel/plugin-transform-modules-commonjs@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/plugin-transform-modules-systemjs@^7.19.0": version "7.19.0" resolved "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" @@ -1406,6 +1590,16 @@ "@babel/helper-plugin-utils" "^7.21.5" "@babel/plugin-syntax-typescript" "^7.21.4" +"@babel/plugin-transform-typescript@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" + integrity sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.22.5" + "@babel/plugin-transform-unicode-escapes@^7.18.10": version "7.18.10" resolved "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" @@ -1562,6 +1756,17 @@ "@babel/plugin-transform-modules-commonjs" "^7.21.5" "@babel/plugin-transform-typescript" "^7.21.3" +"@babel/preset-typescript@^7.17.12": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" + integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-typescript" "^7.22.5" + "@babel/register@^7.13.16": version "7.18.9" resolved "https://registry.npmmirror.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" @@ -1646,6 +1851,15 @@ "@babel/parser" "^7.21.9" "@babel/types" "^7.21.5" +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/traverse@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" @@ -1726,6 +1940,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.22.5": + version "7.22.8" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmmirror.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" @@ -1771,6 +2001,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -4845,6 +5084,11 @@ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.0.tgz#9f66664f9122ca555b96a5f2fc6e2af677bf801b" integrity sha512-OI14ozFLThEV3ey6jE47zrzSTV/6eIMsvbwozo+XfdWqOPwQ7X00YkRx4GVMKMC0rM44oGS2gmwMKYpe4EblnA== +"@nicolo-ribaudo/semver-v6@^6.3.3": + version "6.3.3" + resolved "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz#ea6d23ade78a325f7a52750aab1526b02b628c29" + integrity sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -23623,6 +23867,13 @@ react-native-crypto@^2.2.0: public-encrypt "^4.0.0" randomfill "^1.0.3" +react-native-draggable-flatlist@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/react-native-draggable-flatlist/-/react-native-draggable-flatlist-4.0.1.tgz#2f027d387ba4b8f3eb0907340e32cb85e6460df2" + integrity sha512-ZO1QUTNx64KZfXGXeXcBfql67l38X7kBcJ3rxUVZzPHt5r035GnGzIC0F8rqSXp6zgnwgUYMfB6zQc5PKmPL9Q== + dependencies: + "@babel/preset-typescript" "^7.17.12" + react-native-drawer-layout@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-3.2.0.tgz#1ab05d0bed6bb684353c17c96e1d3e6c1a4e225d" @@ -23801,6 +24052,13 @@ react-native-svg@^13.2.0: css-select "^5.1.0" css-tree "^1.1.3" +react-native-swipeable-item@^2.0.9: + version "2.0.9" + resolved "https://registry.npmjs.org/react-native-swipeable-item/-/react-native-swipeable-item-2.0.9.tgz#694a3f10333b47ba7f0e57d916fa39407b76a6d6" + integrity sha512-NUBX5Xs8cYCU7lWj5O/NtY7kq8I9dIo3eQVtnSbfYU39RXi/n0TpRqpmaQTPM6sE5EQR/BcygD4jwDcrE5h/sQ== + dependencies: + "@babel/preset-typescript" "^7.17.12" + react-native-switch-toggle@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-native-switch-toggle/-/react-native-switch-toggle-2.2.1.tgz#1a7f88a54bded38e6e34988ddac6ba05ccf0adbf" From c38c40814bc03fa6c337c70bac8aaef7f055d29b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 11 Jul 2023 13:59:31 +0800 Subject: [PATCH 318/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20diff=20n?= =?UTF-8?q?etwork=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/QrScanner/index.tsx | 5 ++++- packages/mobile-app-did/js/utils/qrcode.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 4523eb0965..da5f6ab265 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -45,7 +45,10 @@ const QrScanner: React.FC = () => { const qrCodeData = expandQrData(JSON.parse(data)); // if not currentNetwork - if (currentNetwork !== qrCodeData.netWorkType) return invalidQRCode(InvalidQRCodeText.DIFFERENT_NETWORK); + if (currentNetwork !== qrCodeData.netWorkType) + return invalidQRCode( + currentNetwork === 'MAIN' ? InvalidQRCodeText.SWITCH_TO_TESTNET : InvalidQRCodeText.SWITCH_TO_MAINNET, + ); handleQRCodeData(qrCodeData, previousRouteInfo, setRefresh); } diff --git a/packages/mobile-app-did/js/utils/qrcode.ts b/packages/mobile-app-did/js/utils/qrcode.ts index 8ac443de10..cb4f1b9685 100644 --- a/packages/mobile-app-did/js/utils/qrcode.ts +++ b/packages/mobile-app-did/js/utils/qrcode.ts @@ -13,7 +13,8 @@ export interface RouteInfoType { } export enum InvalidQRCodeText { - DIFFERENT_NETWORK = 'Please ensure that you are connected to the same network', + SWITCH_TO_MAINNET = 'Please switch to aelf Mainnet before scanning the QR code', + SWITCH_TO_TESTNET = 'Please switch to aelf Testnet before scanning the QR code', INVALID_QR_CODE = 'The QR code is invalid', } From 97f48b35a9d1858ab94a084f9c084039a0b7a617 Mon Sep 17 00:00:00 2001 From: Portkey-David <120542595+Portkey-David@users.noreply.github.com> Date: Tue, 11 Jul 2023 14:39:08 +0800 Subject: [PATCH 319/893] Update network.ts --- packages/constants/constants-ca/network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/constants/constants-ca/network.ts b/packages/constants/constants-ca/network.ts index acc046ba08..cedcba6d2e 100644 --- a/packages/constants/constants-ca/network.ts +++ b/packages/constants/constants-ca/network.ts @@ -1 +1 @@ -export * from './network-test1'; +export * from './network-mainnet'; From 44b497ffd367866ab819ab3c8e301f78bb9705f5 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 11 Jul 2023 16:23:30 +0800 Subject: [PATCH 320/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20verify?= =?UTF-8?q?=20code=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/pages/VerifierAccount/Popup/index.tsx | 2 +- .../pages/VerifierAccount/Prompt/index.tsx | 3 +- .../app/web/pages/VerifierAccount/index.less | 25 +-- .../app/web/pages/VerifierAccount/index.tsx | 1 - .../pages/components/VerifierPage/index.less | 110 ---------- .../pages/components/VerifierPage/index.tsx | 196 ------------------ 6 files changed, 9 insertions(+), 328 deletions(-) delete mode 100644 packages/web-extension-did/app/web/pages/components/VerifierPage/index.less delete mode 100644 packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/Popup/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/Popup/index.tsx index 2a726dfd5e..5c25e0cf37 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/Popup/index.tsx @@ -8,7 +8,7 @@ export interface IVerifierAccountPopupProps { const VerifierAccountPopup = ({ renderContent, onBack }: IVerifierAccountPopupProps) => { return ( -
    +
    {renderContent}
    diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/Prompt/index.tsx index ccd5748090..6946b6a6cc 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/Prompt/index.tsx @@ -12,8 +12,7 @@ export interface IVerifierAccountPromptProps { const VerifierAccountPrompt = ({ renderContent, onBack, isBigScreenPrompt }: IVerifierAccountPromptProps) => { return ( -
    +
    {isBigScreenPrompt ? ( ) : ( diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.less b/packages/web-extension-did/app/web/pages/VerifierAccount/index.less index 4036dd18fd..f8b203b959 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.less +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.less @@ -1,22 +1,20 @@ -.verifier-account-wrapper { +.verifier-account { .verifier-account-content { - padding: 0 24px; width: 350px; margin-top: 14px; } - .verifier-page-wrapper{ - width: 350px; + .verifier-account-wrapper .login-account { + width: 0; + flex: 1; } } -.verifier-account-wrapper.big-screen-verifier-account { +.verifier-account.big-screen-verifier-account { .verifier-account-content { - width: auto; - } - .verifier-page-wrapper{ + padding: 0 24px; width: auto; } } -.verifier-account-wrapper.popup-page { +.verifier-account.popup-page { min-width: 360px; width: 100%; .setting-header-wrapper { @@ -33,14 +31,5 @@ .verifier-account-content { padding: 0 24px; width: auto; - .verifier-page-wrapper.popup-page { - width: 100%; - .password-wrapper .adm-passcode-input.adm-passcode-input-seperated .adm-passcode-input-cell:not(:last-child) { - margin-right: 12px; - } - } - } - .login-account-wrapper { - margin-bottom: 24px; } } diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index a2b1e0b141..8237612fa7 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -245,7 +245,6 @@ export default function VerifierAccount() { () => (
    void; -} - -export default function VerifierPage({ - operationType, - currentGuardian, - guardianType, - isInitStatus, - onSuccess, -}: VerifierPageProps) { - const { setLoading } = useLoading(); - const [timer, setTimer] = useState(0); - const { isNotLessThan768 } = useCommonState(); - const { pathname } = useLocation(); - const [isFromLoginOrRegister, setIsFromLoginOrRegister] = useState(true); - const [pinVal, setPinVal] = useState(); - const timerRef = useRef(); - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - - const originChainId = useOriginChainId(); - - useEffect(() => { - setIsFromLoginOrRegister(pathname.includes('register') || pathname.includes('login')); - }, [pathname]); - - useEffectOnce(() => { - isInitStatus && setTimer(MAX_TIMER); - }); - - const onFinish = useCallback( - async (code: string) => { - try { - console.log(code); - if (code && code.length === 6) { - if (!guardianType && guardianType !== 0) return message.error('Missing guardiansType'); - if (!currentGuardian?.verifierInfo) throw 'Missing verifierInfo!!!'; - setLoading(true); - - const res = await verification.checkVerificationCode({ - params: { - type: LoginType[currentGuardian?.guardianType as LoginType], - guardianIdentifier: currentGuardian.guardianAccount.replaceAll(' ', ''), - verifierSessionId: currentGuardian.verifierInfo.sessionId, - verificationCode: code, - verifierId: currentGuardian.verifier?.id || '', - chainId: originChainId, - operationType, - }, - }); - - setLoading(false); - if (res.signature) return onSuccess?.({ ...res, verifierId: currentGuardian.verifier?.id || '' }); - - if (res?.error?.message) { - message.error(t(res.error.message)); - } else { - message.error(t(VerificationError.InvalidCode)); - } - setPinVal(''); - } - } catch (error: any) { - console.log(error, 'error===='); - setLoading(false); - setPinVal(''); - const _error = verifyErrorHandler(error); - message.error(_error); - } - }, - [guardianType, originChainId, currentGuardian, setLoading, onSuccess, t, operationType], - ); - - const resendCode = useCallback(async () => { - try { - if (!currentGuardian?.guardianAccount) throw 'Missing loginGuardianType'; - if (!guardianType && guardianType !== 0) throw 'Missing guardiansType'; - setLoading(true); - - const res = await verification.sendVerificationCode({ - params: { - guardianIdentifier: currentGuardian.guardianAccount.replaceAll(' ', ''), - type: LoginType[guardianType], - verifierId: currentGuardian.verifier?.id || '', - chainId: originChainId, - operationType, - }, - }); - setLoading(false); - if (res.verifierSessionId) { - setTimer(MAX_TIMER); - dispatch( - setUserGuardianSessionIdAction({ - key: currentGuardian?.key ?? `${currentGuardian?.guardianAccount}&${currentGuardian?.verifier?.name}`, - verifierInfo: { - sessionId: res.verifierSessionId, - endPoint: res.endPoint, - }, - }), - ); - } - } catch (error: any) { - console.log(error, 'error==='); - setLoading(false); - const _error = verifyErrorHandler(error); - message.error(_error); - } - }, [currentGuardian, guardianType, originChainId, dispatch, setLoading, operationType]); - - useEffect(() => { - if (timer !== MAX_TIMER) return; - timerRef.current && clearInterval(timerRef.current); - timerRef.current = setInterval(() => { - setTimer((t) => { - const newTime = t - 1; - if (newTime <= 0) { - timerRef.current && clearInterval(timerRef.current); - timerRef.current = undefined; - return 0; - } - return newTime; - }); - }, 1000); - }, [timer]); - - return ( -
    - {currentGuardian?.isLoginAccount &&
    {t('Login Account')}
    } -
    - - {currentGuardian?.guardianAccount || ''} -
    -
    - {!isFromLoginOrRegister && 'Please contact your guardians, and enter '} - {t('sendCodeTip1', { codeCount: DIGIT_CODE.length })} - {currentGuardian?.guardianAccount} - {`. `} - {t('sendCodeTip2', { minute: DIGIT_CODE.expiration })} -
    -
    - setPinVal(v)} - onFill={onFinish} - /> - -
    -
    - ); -} From cb5086367a77d7c30c4544f7caee99b6caf6a68c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 11 Jul 2023 14:28:18 +0800 Subject: [PATCH 321/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20add=20t?= =?UTF-8?q?oken=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/SimulatedInputBox/index.tsx | 0 .../js/pages/Discover/DiscoverHome/index.tsx | 2 +- .../js/pages/Token/CustomToken/index.tsx | 2 +- .../js/pages/Token/ManageTokenList/index.tsx | 76 ++------ .../js/pages/Token/SearchTokenList/index.tsx | 164 ++++++++++++++++++ .../components/FilterToken/index.tsx | 0 .../components/PopularToken/index.tsx | 0 .../components/TokenItem/index.tsx | 0 .../mobile-app-did/js/pages/Token/index.ts | 2 + 9 files changed, 180 insertions(+), 66 deletions(-) rename packages/mobile-app-did/js/{pages/Discover => }/components/SimulatedInputBox/index.tsx (100%) create mode 100644 packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx rename packages/mobile-app-did/js/pages/Token/{ManageTokenList => }/components/FilterToken/index.tsx (100%) rename packages/mobile-app-did/js/pages/Token/{ManageTokenList => }/components/PopularToken/index.tsx (100%) rename packages/mobile-app-did/js/pages/Token/{ManageTokenList => }/components/TokenItem/index.tsx (100%) diff --git a/packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx b/packages/mobile-app-did/js/components/SimulatedInputBox/index.tsx similarity index 100% rename from packages/mobile-app-did/js/pages/Discover/components/SimulatedInputBox/index.tsx rename to packages/mobile-app-did/js/components/SimulatedInputBox/index.tsx diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 76db5509ec..c76dc76eff 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { StyleSheet, View } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import navigationService from 'utils/navigationService'; -import SimulatedInputBox from '../components/SimulatedInputBox'; +import SimulatedInputBox from 'components/SimulatedInputBox'; import { DiscoverCmsListSection } from '../components/DiscoverCmsListSection'; import { defaultColors } from 'assets/theme'; import SafeAreaBox from 'components/SafeAreaBox'; diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 20d8a2b99a..17ae04f8bd 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -3,7 +3,7 @@ import CommonInput from 'components/CommonInput'; import { StyleSheet, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { TextM } from 'components/CommonText'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index 08852ab03c..9e39a8f0a7 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -1,26 +1,24 @@ import PageContainer from 'components/PageContainer'; import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; -import CommonInput from 'components/CommonInput'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import CommonToast from 'components/CommonToast'; import { useLanguage } from 'i18n/hooks'; import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; -import useDebounce from 'hooks/useDebounce'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { request } from '@portkey-wallet/api/api-did'; import { useCaAddresses, useCaAddressInfoList, useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { fetchTokenListAsync } from '@portkey-wallet/store/store-ca/assets/slice'; import Loading from 'components/Loading'; -import FilterTokenSection from './components/FilterToken'; -import PopularTokenSection from './components/PopularToken'; +import PopularTokenSection from '../components/PopularToken'; import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; import { useFocusEffect } from '@react-navigation/native'; +import SimulatedInputBox from 'components/SimulatedInputBox'; interface ManageTokenListProps { route?: any; @@ -37,35 +35,6 @@ const ManageTokenList: React.FC = () => { const { tokenDataShowInMarket } = useAppCASelector(state => state.tokenManagement); - const [keyword, setKeyword] = useState(''); - const [filterTokenList, setFilterTokenList] = useState([]); - - const debounceWord = useDebounce(keyword, 800); - - const fetchSearchedTokenList = useCallback(async () => { - try { - if (!debounceWord) return; - Loading.showOnce(); - const list = await request.token.fetchTokenListBySearch({ - params: { - symbol: debounceWord, - chainIds: chainIdList, - }, - }); - - const tmpToken: TokenItemShowType[] = list.map((item: any) => ({ - ...item, - isAdded: item.isDisplay, - userTokenId: item.id, - })); - setFilterTokenList(tmpToken); - Loading.hide(); - } catch (error) { - console.log('filter search error', error); - Loading.hide(); - } - }, [chainIdList, debounceWord]); - const onHandleTokenItem = useCallback( async (item: TokenItemShowType, isDisplay: boolean) => { Loading.showOnce(); @@ -79,11 +48,8 @@ const ManageTokenList: React.FC = () => { }); timerRef.current = setTimeout(async () => { dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); - if (debounceWord) { - await fetchSearchedTokenList(); - } else { - await dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); - } + await dispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray: chainIdList })); + Loading.hide(); CommonToast.success('Success'); }, 800); @@ -93,31 +59,24 @@ const ManageTokenList: React.FC = () => { CommonToast.fail('Fail'); } }, - [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch, fetchSearchedTokenList], + [caAddressArray, caAddressInfos, chainIdList, dispatch], ); useFocusEffect( useCallback(() => { - fetchSearchedTokenList(); dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); - }, [chainIdList, dispatch, fetchSearchedTokenList]), + }, [chainIdList, dispatch]), ); useEffect(() => { if (tokenDataShowInMarket.length) return; - dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); + dispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray: chainIdList })); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { - if (debounceWord) { - // get filter token - setFilterTokenList([]); - fetchSearchedTokenList(); - } else { - dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); - } - }, [chainIdList, debounceWord, dispatch, fetchSearchedTokenList]); + dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); + }, [chainIdList, dispatch]); // clear timer useEffect( @@ -143,20 +102,9 @@ const ManageTokenList: React.FC = () => { containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> - { - setKeyword(v.trim()); - }} - /> + navigationService.navigate('SearchTokenList')} /> - - {debounceWord ? ( - - ) : ( - - )} + ); }; diff --git a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx new file mode 100644 index 0000000000..1eacae6322 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx @@ -0,0 +1,164 @@ +import PageContainer from 'components/PageContainer'; +import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; +import CommonInput from 'components/CommonInput'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import gStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import CommonToast from 'components/CommonToast'; +import { useLanguage } from 'i18n/hooks'; +import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; +import useDebounce from 'hooks/useDebounce'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { request } from '@portkey-wallet/api/api-did'; +import { useCaAddresses, useCaAddressInfoList, useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { fetchTokenListAsync } from '@portkey-wallet/store/store-ca/assets/slice'; +import Loading from 'components/Loading'; +import { pTd } from 'utils/unit'; +import navigationService from 'utils/navigationService'; +import Svg from 'components/Svg'; +import { useFocusEffect } from '@react-navigation/native'; +import FilterTokenSection from '../components/FilterToken'; + +interface ManageTokenListProps { + route?: any; +} +const SearchTokenList: React.FC = () => { + const { t } = useLanguage(); + const timerRef = useRef(null); + + const chainIdList = useChainIdList(); + + const dispatch = useAppCommonDispatch(); + const caAddressArray = useCaAddresses(); + const caAddressInfos = useCaAddressInfoList(); + + const [keyword, setKeyword] = useState(''); + const [filterTokenList, setFilterTokenList] = useState([]); + + const debounceWord = useDebounce(keyword, 800); + + const fetchSearchedTokenList = useCallback(async () => { + try { + if (!debounceWord) return; + Loading.showOnce(); + const list = await request.token.fetchTokenListBySearch({ + params: { + symbol: debounceWord, + chainIds: chainIdList, + }, + }); + + const tmpToken: TokenItemShowType[] = list.map((item: any) => ({ + ...item, + isAdded: item.isDisplay, + userTokenId: item.id, + })); + setFilterTokenList(tmpToken); + Loading.hide(); + } catch (error) { + console.log('filter search error', error); + Loading.hide(); + } + }, [chainIdList, debounceWord]); + + const onHandleTokenItem = useCallback( + async (item: TokenItemShowType, isDisplay: boolean) => { + Loading.showOnce(); + + try { + await request.token.displayUserToken({ + resourceUrl: `${item.userTokenId}/display`, + params: { + isDisplay, + }, + }); + timerRef.current = setTimeout(async () => { + dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); + await fetchSearchedTokenList(); + Loading.hide(); + CommonToast.success('Success'); + }, 800); + } catch (err) { + console.log(err); + Loading.hide(); + CommonToast.fail('Fail'); + } + }, + [caAddressArray, caAddressInfos, dispatch, fetchSearchedTokenList], + ); + + const backToAddTokenHome = useCallback(() => { + if (keyword) setKeyword(''); + }, [keyword]); + + useFocusEffect( + useCallback(() => { + fetchSearchedTokenList(); + }, [fetchSearchedTokenList]), + ); + + useEffect(() => { + dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); + }, [chainIdList, debounceWord, dispatch, fetchSearchedTokenList]); + + // clear timer + useEffect( + () => () => { + if (timerRef.current) clearInterval(timerRef.current); + }, + [], + ); + + return ( + { + navigationService.navigate('CustomToken'); + }}> + + + } + containerStyles={pageStyles.pageWrap} + scrollViewProps={{ disabled: true }}> + + { + setKeyword(v.trim()); + }} + rightIcon={ + keyword ? ( + setKeyword('')}> + + + ) : undefined + } + /> + + + + ); +}; + +export default SearchTokenList; + +export const pageStyles = StyleSheet.create({ + pageWrap: { + flex: 1, + ...gStyles.paddingArg(0), + }, + inputWrap: { + backgroundColor: defaultColors.bg5, + ...gStyles.paddingArg(0, 16, 16), + }, + list: { + flex: 1, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx b/packages/mobile-app-did/js/pages/Token/components/FilterToken/index.tsx similarity index 100% rename from packages/mobile-app-did/js/pages/Token/ManageTokenList/components/FilterToken/index.tsx rename to packages/mobile-app-did/js/pages/Token/components/FilterToken/index.tsx diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx b/packages/mobile-app-did/js/pages/Token/components/PopularToken/index.tsx similarity index 100% rename from packages/mobile-app-did/js/pages/Token/ManageTokenList/components/PopularToken/index.tsx rename to packages/mobile-app-did/js/pages/Token/components/PopularToken/index.tsx diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx b/packages/mobile-app-did/js/pages/Token/components/TokenItem/index.tsx similarity index 100% rename from packages/mobile-app-did/js/pages/Token/ManageTokenList/components/TokenItem/index.tsx rename to packages/mobile-app-did/js/pages/Token/components/TokenItem/index.tsx diff --git a/packages/mobile-app-did/js/pages/Token/index.ts b/packages/mobile-app-did/js/pages/Token/index.ts index 9eb08d984a..8dd799c8a7 100644 --- a/packages/mobile-app-did/js/pages/Token/index.ts +++ b/packages/mobile-app-did/js/pages/Token/index.ts @@ -1,4 +1,5 @@ import ManageTokenList from './ManageTokenList'; +import SearchTokenList from './SearchTokenList'; import TokenDetail from './TokenDetail/index'; import CustomToken from './CustomToken/index'; @@ -6,6 +7,7 @@ const stackNav = [ { name: 'ManageTokenList', component: ManageTokenList }, { name: 'TokenDetail', component: TokenDetail }, { name: 'CustomToken', component: CustomToken }, + { name: 'SearchTokenList', component: SearchTokenList }, ] as const; export default stackNav; From 7f7567dd66bb5428948b3f07f1845942893474e6 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 11 Jul 2023 18:56:16 +0800 Subject: [PATCH 322/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20add=20t?= =?UTF-8?q?oken=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Token/CustomToken/index.tsx | 4 +- .../js/pages/Token/ManageTokenList/index.tsx | 4 +- .../js/pages/Token/SearchTokenList/index.tsx | 37 ++++++++----------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 17ae04f8bd..38301d9ab9 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -3,7 +3,7 @@ import CommonInput from 'components/CommonInput'; import { StyleSheet, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import { TextM } from 'components/CommonText'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; @@ -114,7 +114,7 @@ const CustomToken: React.FC = () => { }); CommonToast.success('success'); await sleep(500); - navigationService.goBack(); + navigationService.navigate('ManageTokenList'); } catch (err: any) { CommonToast.fail(handleErrorMessage(err)); console.log('add custom token error', err); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index 9e39a8f0a7..08d7e03788 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -101,9 +101,7 @@ const ManageTokenList: React.FC = () => { } containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> - - navigationService.navigate('SearchTokenList')} /> - + navigationService.navigate('SearchTokenList')} /> ); diff --git a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx index 1eacae6322..e6827e64ab 100644 --- a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx @@ -7,7 +7,6 @@ import { defaultColors } from 'assets/theme'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import CommonToast from 'components/CommonToast'; import { useLanguage } from 'i18n/hooks'; -import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; import useDebounce from 'hooks/useDebounce'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { request } from '@portkey-wallet/api/api-did'; @@ -17,7 +16,6 @@ import Loading from 'components/Loading'; import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; -import { useFocusEffect } from '@react-navigation/native'; import FilterTokenSection from '../components/FilterToken'; interface ManageTokenListProps { @@ -26,6 +24,7 @@ interface ManageTokenListProps { const SearchTokenList: React.FC = () => { const { t } = useLanguage(); const timerRef = useRef(null); + const iptRef = useRef(); const chainIdList = useChainIdList(); @@ -36,7 +35,7 @@ const SearchTokenList: React.FC = () => { const [keyword, setKeyword] = useState(''); const [filterTokenList, setFilterTokenList] = useState([]); - const debounceWord = useDebounce(keyword, 800); + const debounceWord = useDebounce(keyword, 500); const fetchSearchedTokenList = useCallback(async () => { try { @@ -88,31 +87,24 @@ const SearchTokenList: React.FC = () => { [caAddressArray, caAddressInfos, dispatch, fetchSearchedTokenList], ); - const backToAddTokenHome = useCallback(() => { - if (keyword) setKeyword(''); - }, [keyword]); - - useFocusEffect( - useCallback(() => { - fetchSearchedTokenList(); - }, [fetchSearchedTokenList]), - ); + useEffect(() => { + setFilterTokenList([]); + fetchSearchedTokenList(); + }, [chainIdList, setFilterTokenList, debounceWord, fetchSearchedTokenList]); useEffect(() => { - dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); - }, [chainIdList, debounceWord, dispatch, fetchSearchedTokenList]); + // input focus + timerRef.current = setTimeout(() => { + if (iptRef && iptRef?.current) iptRef.current.focus(); + }, 200); - // clear timer - useEffect( - () => () => { + return () => { if (timerRef.current) clearInterval(timerRef.current); - }, - [], - ); + }; + }, []); return ( = () => { scrollViewProps={{ disabled: true }}> { @@ -156,7 +149,7 @@ export const pageStyles = StyleSheet.create({ }, inputWrap: { backgroundColor: defaultColors.bg5, - ...gStyles.paddingArg(0, 16, 16), + ...gStyles.paddingArg(8, 20), }, list: { flex: 1, From bedd1c4a0a7dddbc723608519b45b4069daad1c7 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 11 Jul 2023 19:27:58 +0800 Subject: [PATCH 323/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20select?= =?UTF-8?q?=20verifier=20use=20ui-sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/guardians/type.ts | 2 +- .../web-extension-did/app/web/manifest.json | 6 +- .../web/messages/UISdkSandboxEventTypes.ts | 7 + .../app/web/pages/SelectVerifier/index.less | 63 +---- .../app/web/pages/SelectVerifier/index.tsx | 233 +++++------------- .../web-extension-did/app/web/prompt.html | 6 - .../app/web/sandbox/README.md | 3 - .../app/web/sandbox/index.html | 13 - .../web-extension-did/app/web/sandboxUtil.ts | 3 + packages/web-extension-did/package.json | 2 +- packages/web-extension-did/webpack.config.js | 5 - 11 files changed, 75 insertions(+), 268 deletions(-) create mode 100644 packages/web-extension-did/app/web/messages/UISdkSandboxEventTypes.ts delete mode 100644 packages/web-extension-did/app/web/sandbox/README.md delete mode 100644 packages/web-extension-did/app/web/sandbox/index.html diff --git a/packages/store/store-ca/guardians/type.ts b/packages/store/store-ca/guardians/type.ts index 6984293a65..a5aabcd1a4 100644 --- a/packages/store/store-ca/guardians/type.ts +++ b/packages/store/store-ca/guardians/type.ts @@ -18,7 +18,7 @@ export interface BaseGuardianItem { export interface IVerifierInfo { sessionId: string; - endPoint: string; + endPoint?: string; } export interface UserGuardianItem extends BaseGuardianItem { diff --git a/packages/web-extension-did/app/web/manifest.json b/packages/web-extension-did/app/web/manifest.json index 46e22ec2f7..dd340641b1 100644 --- a/packages/web-extension-did/app/web/manifest.json +++ b/packages/web-extension-did/app/web/manifest.json @@ -20,7 +20,7 @@ "default_popup": "popup.html" }, "sandbox": { - "pages": ["sandbox.html", "sandbox-index.html"] + "pages": ["sandbox.html"] }, "commands": { "_execute_action": { @@ -49,10 +49,6 @@ { "resources": ["sandbox.html"], "matches": [""] - }, - { - "resources": ["sandbox-index.html"], - "matches": [""] } ], "options_page": "options.html", diff --git a/packages/web-extension-did/app/web/messages/UISdkSandboxEventTypes.ts b/packages/web-extension-did/app/web/messages/UISdkSandboxEventTypes.ts new file mode 100644 index 0000000000..548a512dde --- /dev/null +++ b/packages/web-extension-did/app/web/messages/UISdkSandboxEventTypes.ts @@ -0,0 +1,7 @@ +enum UISdkSandboxEventTypes { + // View + callViewMethod = '@portkey/did-ui-sdk:callViewMethod', + // Send + callSendMethod = '@portkey/did-ui-sdk:callSendMethod', +} +export default UISdkSandboxEventTypes; diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.less b/packages/web-extension-did/app/web/pages/SelectVerifier/index.less index c3bf9a2008..1b56927c2e 100644 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.less +++ b/packages/web-extension-did/app/web/pages/SelectVerifier/index.less @@ -1,66 +1,13 @@ @import '../../assets/theme/color.less'; .select-verifier-wrapper { - .description { - width: 600px; + .portkey-ui-verifier-selector { + width: 350px; margin: auto; - font-size: 16px; - line-height: 22px; - text-align: center; - } - - /** popular */ - .popular-title { - line-height: 20px; - font-size: 14px; - margin-bottom: 16px; - color: @font-11; - padding-left: 5px; - } - .popular-content { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - row-gap: 5px; - text-align: center; - margin-bottom: 40px; - .popular-item-image { - border-radius: 54px; - display: block; - margin: 0 auto 8px; - } - .popular-item-name { - line-height: 16px; - font-size: 12px; - color: @font-13; - } } - .confirm-btn { - line-height: 44px; - border-radius: 22px; - } - - /** modal */ - .verify-confirm-modal { - .@{app-prefix}-modal-header { - padding: 0; - } - .@{app-prefix}-modal-title { - display: none; - } - .@{app-prefix}-modal-body { - padding: 0; - } - .modal-content { - padding: 24px; - line-height: 20px; - color: @font-13; - font-size: 14px; - text-align: center; - border-bottom: 1px solid @border-2; - .bold { - font-weight: bold; - } - } +.select-verifier-description { + width: 600px; + margin: auto; } } diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx index 9efcc07dcb..3e2f7149df 100644 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx +++ b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx @@ -1,87 +1,45 @@ import { setCurrentGuardianAction } from '@portkey-wallet/store/store-ca/guardians/actions'; -import { Button, message } from 'antd'; -import CommonModal from 'components/CommonModal'; -import { useCallback, useMemo, useState } from 'react'; +import { message } from 'antd'; +import { useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router'; -import { useAppDispatch, useLoginInfo, useGuardiansInfo, useLoading } from 'store/Provider/hooks'; +import { useAppDispatch, useGuardiansInfo, useLoginInfo } from 'store/Provider/hooks'; import PortKeyTitle from 'pages/components/PortKeyTitle'; -import BaseVerifierIcon from 'components/BaseVerifierIcon'; -import CommonSelect from 'components/CommonSelect1'; -import { useTranslation } from 'react-i18next'; -import { verifyErrorHandler } from 'utils/tryErrorHandler'; -import { verification } from 'utils/api'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; -import { handleError } from '@portkey-wallet/utils'; -import { useVerifyToken } from 'hooks/authentication'; import { setRegisterVerifierAction } from 'store/reducers/loginCache/actions'; -import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQueryResult'; -import InternalMessage from 'messages/InternalMessage'; -import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; +import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { VerifierSelect } from '@portkey/did-ui-react'; +import { VerifierItem } from '@portkey-wallet/types/verifier'; import './index.less'; -import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; + +interface ConfirmResultInfo { + verifier: VerifierItem; + verifierSessionId?: string; + verificationDoc?: string; + signature?: string; +} export default function SelectVerifier() { - const { verifierMap } = useGuardiansInfo(); const { loginAccount } = useLoginInfo(); - const [open, setOpen] = useState(); + const { verifierMap } = useGuardiansInfo(); const navigate = useNavigate(); const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const { setLoading } = useLoading(); const originChainId = useOriginChainId(); - const onManagerAddressAndQueryResult = useOnManagerAddressAndQueryResult('register'); - const { address: managerAddress } = useCurrentWalletInfo(); - - const handleChange = useCallback((value: string) => { - setSelectVal(value); - }, []); - - const selectOptions = useMemo( - () => - Object.values(verifierMap ?? {})?.map((item) => ({ - value: item.id, - verifierUrl: item.imageUrl ?? '', - label: item.name, - })), - [verifierMap], - ); - const [selectVal, setSelectVal] = useState(selectOptions?.[0]?.value); - - const selectItem = useMemo(() => verifierMap?.[selectVal], [selectVal, verifierMap]); - - const verifyToken = useVerifyToken(); - - const verifyHandler = useCallback(async () => { - try { - if (!loginAccount || !LoginType[loginAccount.loginType] || !loginAccount.guardianAccount) + const onConfirm = useCallback( + async (result: ConfirmResultInfo) => { + console.log(result, 'result==onConfirm'); + if (!loginAccount) return message.error('User registration information is invalid, please fill in the registration method again'); - if (!selectItem) return message.error('Can not get verification'); - - setLoading(true); - - const result = await verification.sendVerificationCode({ - params: { - guardianIdentifier: loginAccount.guardianAccount.replaceAll(' ', ''), - type: LoginType[loginAccount.loginType], - verifierId: selectItem.id, - chainId: originChainId, - operationType: OperationTypeEnum.register, - }, - }); - setLoading(false); if (result.verifierSessionId) { - const _key = `${loginAccount.guardianAccount}&${selectItem.name}`; + const _key = `${loginAccount.guardianAccount}&${result.verifier.name}`; dispatch( setCurrentGuardianAction({ isLoginAccount: true, - verifier: selectItem, + verifier: result.verifier, guardianAccount: loginAccount.guardianAccount, guardianType: loginAccount.loginType, verifierInfo: { sessionId: result.verifierSessionId, - endPoint: result.endPoint, }, key: _key, identifierHash: '', @@ -89,125 +47,48 @@ export default function SelectVerifier() { }), ); navigate('/register/verifier-account', { state: 'register' }); - } - } catch (error: any) { - setLoading(false); - console.log(error, 'verifyHandler'); - const _error = verifyErrorHandler(error); - message.error(_error); - } - }, [dispatch, loginAccount, navigate, originChainId, selectItem, setLoading]); - - const verifierShow = useMemo(() => Object.values(verifierMap ?? {}).slice(0, 3), [verifierMap]); - - const onConfirmAuth = useCallback(async () => { - try { - setLoading(true); - if (!loginAccount?.loginType) throw 'loginType is invalid'; - const rst = await verifyToken(loginAccount.loginType, { - accessToken: loginAccount.authenticationInfo?.[loginAccount.guardianAccount || ''], - id: loginAccount.guardianAccount, - verifierId: selectItem?.id, - chainId: originChainId, - operationType: OperationTypeEnum.register, - }); - dispatch( - setRegisterVerifierAction({ - verifierId: selectItem?.id as string, - verificationDoc: rst.verificationDoc, - signature: rst.signature, - }), - ); - const res = await InternalMessage.payload(PortkeyMessageTypes.CHECK_WALLET_STATUS).send(); - setLoading(false); - if (managerAddress && res.data.privateKey) { - onManagerAddressAndQueryResult(res.data.privateKey, { - verifierId: selectItem?.id as string, - verificationDoc: rst.verificationDoc, - signature: rst.signature, - }); - } else { + } else if (result.verificationDoc && result.signature) { + dispatch( + setRegisterVerifierAction({ + verifierId: result.verifier.id, + verificationDoc: result.verificationDoc, + signature: result.signature, + }), + ); navigate('/login/set-pin/register'); + } else { + message.error('Verification failed, please try again later'); } - } catch (error) { - const msg = handleError(error); - message.error(msg); - setLoading(false); - } - }, [ - dispatch, - loginAccount, - managerAddress, - navigate, - onManagerAddressAndQueryResult, - originChainId, - selectItem?.id, - setLoading, - verifyToken, - ]); + }, + [dispatch, loginAccount, navigate], + ); - const onConfirm = useCallback(async () => { - switch (loginAccount?.loginType) { - case LoginType.Apple: - case LoginType.Google: - onConfirmAuth(); - break; - default: { - setOpen(true); - break; - } - } - }, [loginAccount, onConfirmAuth]); + const authorized = useMemo( + () => loginAccount?.authenticationInfo?.[loginAccount?.guardianAccount || ''], + [loginAccount?.authenticationInfo, loginAccount?.guardianAccount], + ); + + const verifierList = useMemo(() => Object.values(verifierMap ?? {}), [verifierMap]); return ( -
    - navigate('/register/start/create')} /> -
    -
    -
    {t('Select verifier')}
    -
    -

    - {t( - 'Verifiers protect your account and help you recover your assets when they are subject to risks. Please note: The more diversified your verifiers are, the higher security your assets enjoy.', - )} -

    -
    - -

    {t('Popular')}

    -
      - {verifierShow?.map((item) => ( -
    • handleChange(item.id)}> - -

      {item.name}

      -
    • - ))} -
    - -
    -
    - {loginAccount && ( - setOpen(false)}> -

    - {`${t('verificationCodeTip1', { verifier: selectItem?.name })} `} - {loginAccount.guardianAccount} - {` ${t('verificationCodeTip2', { type: LoginType[loginAccount.loginType] })}`} -

    -
    - - -
    -
    - )} +
    + navigate('/register/start')} /> + + { + console.log(err, 'VerifierSelect==='); + }} + />
    ); } diff --git a/packages/web-extension-did/app/web/prompt.html b/packages/web-extension-did/app/web/prompt.html index a9d7b6036a..e0d5786c2c 100644 --- a/packages/web-extension-did/app/web/prompt.html +++ b/packages/web-extension-did/app/web/prompt.html @@ -16,12 +16,6 @@ -
    diff --git a/packages/web-extension-did/app/web/sandbox/README.md b/packages/web-extension-did/app/web/sandbox/README.md deleted file mode 100644 index f58dc64cfc..0000000000 --- a/packages/web-extension-did/app/web/sandbox/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Source - -For specific detailed code, please refer to packages/web-extension-did/app/web/sandboxUtil.ts diff --git a/packages/web-extension-did/app/web/sandbox/index.html b/packages/web-extension-did/app/web/sandbox/index.html deleted file mode 100644 index f65c55edf8..0000000000 --- a/packages/web-extension-did/app/web/sandbox/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - Document - - - - - diff --git a/packages/web-extension-did/app/web/sandboxUtil.ts b/packages/web-extension-did/app/web/sandboxUtil.ts index a95a4fea3f..629f9c091e 100644 --- a/packages/web-extension-did/app/web/sandboxUtil.ts +++ b/packages/web-extension-did/app/web/sandboxUtil.ts @@ -6,6 +6,7 @@ import { TokenItemType } from '@portkey-wallet/types/types-ca/token'; import { customFetch } from '@portkey-wallet/utils/fetch'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { ContractBasic } from '@portkey-wallet/contracts/utils/ContractBasic'; +import UISdkSandboxEventTypes from 'messages/UISdkSandboxEventTypes'; interface useBalancesProps { tokens: TokenItemType | TokenItemType[]; @@ -55,9 +56,11 @@ class SandboxUtil { SandboxUtil.getBalances(event, SandboxUtil.callback); break; case SandboxEventTypes.callViewMethod: + case UISdkSandboxEventTypes.callViewMethod: SandboxUtil.callViewMethod(event, SandboxUtil.callback); break; case SandboxEventTypes.callSendMethod: + case UISdkSandboxEventTypes.callSendMethod: SandboxUtil.callSendMethod(event, SandboxUtil.callback); break; case SandboxEventTypes.getTransactionFee: diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index b8500e4682..7f80e80895 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -20,7 +20,7 @@ "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", "@portkey/providers": "1.0.1-alpha.0", - "@portkey/did-ui-react": "1.0.0-alpha.8", + "@portkey/did-ui-react": "1.0.1", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", diff --git a/packages/web-extension-did/webpack.config.js b/packages/web-extension-did/webpack.config.js index 358aa2d973..499f0f3070 100644 --- a/packages/web-extension-did/webpack.config.js +++ b/packages/web-extension-did/webpack.config.js @@ -152,11 +152,6 @@ let config = { template: './app/web/sandbox.html', filename: `./${outputDir}/sandbox.html`, }), - new HtmlWebpackPlugin({ - chunks: [''], - template: './app/web/sandbox/index.html', - filename: `./${outputDir}/sandbox-index.html`, - }), new CopyWebpackPlugin({ patterns: [ { From 50fe102d737bdfaac25754fa8d3cf4a6a869bdac Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 11 Jul 2023 19:35:03 +0800 Subject: [PATCH 324/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20=20ignore?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6b0a533f2a..c7aafc1d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ ignore/ **/*.zip coverage + +# network +packages/constants/constants-ca/network.ts From 2ddd0baacb281062d9048c04a93b4175fb4bbb86 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 12 Jul 2023 10:21:24 +0800 Subject: [PATCH 325/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20adjust=20UI=20des?= =?UTF-8?q?ign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web-extension-did/app/web/pages/Token/Custom/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx index dd16f31c2d..5128b3b576 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx @@ -143,6 +143,7 @@ export default function CustomToken() { { const _value = e.target.value.replaceAll(' ', ''); setValue(_value); @@ -153,7 +154,7 @@ export default function CustomToken() {

    {t('Token Decimal')}

    - +
    From bc95bdb4b5c70c7667990369f3abe778c334a1b1 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 12 Jul 2023 10:24:19 +0800 Subject: [PATCH 326/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20change=20cr?= =?UTF-8?q?=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Discover/DiscoverSearch/index.tsx | 3 +-- .../js/pages/Token/CustomToken/index.tsx | 1 - .../js/pages/Token/ManageTokenList/index.tsx | 26 +++++++++++-------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 24ab45fecc..d98920ddb9 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -32,7 +32,6 @@ export default function DiscoverSearch() { const [filteredDiscoverList, setFilteredDiscoverList] = useState([]); const clearText = useCallback(() => setValue(''), []); - const navBack = useCallback(() => navigationService.goBack(), []); const flatList = useMemo((): DiscoverItem[] => { const list = [] as DiscoverItem[]; @@ -119,7 +118,7 @@ export default function DiscoverSearch() { rightIconContainerStyle={styles.rightIconContainerStyle} style={styles.rnInputStyle} /> - + {t('Cancel')} diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 288c056c8b..9be6c2cb64 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -74,7 +74,6 @@ const CustomToken: React.FC = () => { } } catch (err) { setBtnDisable(true); - Loading.hide(); CommonToast.fail(handleErrorMessage(err)); } finally { Loading.hide(); diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index 08852ab03c..9e7f001905 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -5,7 +5,7 @@ import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import CommonToast from 'components/CommonToast'; import { useLanguage } from 'i18n/hooks'; import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; @@ -88,7 +88,6 @@ const ManageTokenList: React.FC = () => { CommonToast.success('Success'); }, 800); } catch (err) { - console.log(err); Loading.hide(); CommonToast.fail('Fail'); } @@ -96,6 +95,19 @@ const ManageTokenList: React.FC = () => { [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch, fetchSearchedTokenList], ); + const RightDom = useMemo( + () => ( + { + navigationService.navigate('CustomToken'); + }}> + + + ), + [], + ); + useFocusEffect( useCallback(() => { fetchSearchedTokenList(); @@ -131,15 +143,7 @@ const ManageTokenList: React.FC = () => { { - navigationService.navigate('CustomToken'); - }}> - - - } + rightDom={RightDom} containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> From a77d7cfb7b25a574ec6ade4b5224d628f4c2798a Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 12 Jul 2023 11:16:34 +0800 Subject: [PATCH 327/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20tip=20when?= =?UTF-8?q?=20can=20not=20find=20custom=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Token/Custom/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx index 5128b3b576..01f23368d7 100644 --- a/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Custom/index.tsx @@ -65,11 +65,15 @@ export default function CustomToken() { if (symbol && id) { setCurToken(res); setValue(symbol); + setErrorMsg(''); + } else { + setCurToken({}); + setErrorMsg('Unable to recognize token'); } - setErrorMsg(''); } catch (error) { setCurToken({}); - console.log('filter search error', error); + setErrorMsg('Unable to recognize token'); + console.log('filter search token error', error); } finally { setLoading(false); } From 27fe0faddecec43928f6dbc9d736d061f246aefe Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 12 Jul 2023 11:27:37 +0800 Subject: [PATCH 328/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20unable=20to=20r?= =?UTF-8?q?ecognize=20warn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx index 9be6c2cb64..c7f10cfc04 100644 --- a/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/CustomToken/index.tsx @@ -71,6 +71,9 @@ const CustomToken: React.FC = () => { setTokenItem(pre => ({ ...pre, ...res })); setKeyword(symbol); setBtnDisable(false); + } else { + setErrorMessage('Unable to recognize token'); + setBtnDisable(true); } } catch (err) { setBtnDisable(true); From 0a4bb387496deb73e3ab76dd5fc9e3becbfd919e Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 12 Jul 2023 11:37:05 +0800 Subject: [PATCH 329/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20Guardian?= =?UTF-8?q?=20Type=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/verifier.ts | 21 +++++++++ .../pages/Guardian/VerifierDetails/index.tsx | 21 +-------- .../components/GuardianItem/index.tsx | 46 +++++-------------- 3 files changed, 35 insertions(+), 53 deletions(-) create mode 100644 packages/constants/constants-ca/verifier.ts diff --git a/packages/constants/constants-ca/verifier.ts b/packages/constants/constants-ca/verifier.ts new file mode 100644 index 0000000000..5595a2f602 --- /dev/null +++ b/packages/constants/constants-ca/verifier.ts @@ -0,0 +1,21 @@ +import { ApprovalType, OperationTypeEnum, VerificationType } from '@portkey-wallet/types/verifier'; + +export const APPROVAL_TO_OPERATION_MAP = { + [ApprovalType.communityRecovery]: OperationTypeEnum.communityRecovery, + [ApprovalType.addGuardian]: OperationTypeEnum.addGuardian, + [ApprovalType.editGuardian]: OperationTypeEnum.editGuardian, + [ApprovalType.deleteGuardian]: OperationTypeEnum.deleteGuardian, + [ApprovalType.removeOtherManager]: OperationTypeEnum.removeOtherManager, +}; + +export const VERIFICATION_TO_OPERATION_MAP = { + [VerificationType.register]: OperationTypeEnum.register, + [VerificationType.communityRecovery]: OperationTypeEnum.communityRecovery, + [VerificationType.addGuardian]: OperationTypeEnum.addGuardian, + [VerificationType.addGuardianByApprove]: OperationTypeEnum.addGuardian, + [VerificationType.deleteGuardian]: OperationTypeEnum.deleteGuardian, + [VerificationType.editGuardian]: OperationTypeEnum.editGuardian, + [VerificationType.removeOtherManager]: OperationTypeEnum.removeOtherManager, + [VerificationType.setLoginAccount]: OperationTypeEnum.setLoginAccount, + [VerificationType.addManager]: OperationTypeEnum.unknown, +}; diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index 0dc926de3d..7fe2d26235 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -31,6 +31,7 @@ import { verification } from 'utils/api'; import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; import { useOnRequestOrSetPin } from 'hooks/login'; import { usePin } from 'hooks/store'; +import { VERIFICATION_TO_OPERATION_MAP } from '@portkey-wallet/constants/constants-ca/verifier'; type RouterParams = { guardianItem?: UserGuardianItem; @@ -105,25 +106,7 @@ export default function VerifierDetails() { }, [caHash, getCurrentCAContract, guardianItem, managerAddress]); const operationType: OperationTypeEnum = useMemo(() => { - switch (verificationType) { - case VerificationType.register: - return OperationTypeEnum.register; - case VerificationType.communityRecovery: - return OperationTypeEnum.communityRecovery; - case VerificationType.addGuardian: - case VerificationType.addGuardianByApprove: - return OperationTypeEnum.addGuardian; - case VerificationType.deleteGuardian: - return OperationTypeEnum.deleteGuardian; - case VerificationType.editGuardian: - return OperationTypeEnum.editGuardian; - case VerificationType.removeOtherManager: - return OperationTypeEnum.removeOtherManager; - case VerificationType.setLoginAccount: - return OperationTypeEnum.setLoginAccount; - default: - return OperationTypeEnum.unknown; - } + return VERIFICATION_TO_OPERATION_MAP[verificationType as VerificationType] || OperationTypeEnum.unknown; }, [verificationType]); const onFinish = useLockCallback( diff --git a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx index a7f11cd6aa..6bbc269794 100644 --- a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx @@ -32,9 +32,18 @@ import { useVerifyToken } from 'hooks/authentication'; import { PRIVATE_GUARDIAN_ACCOUNT } from '@portkey-wallet/constants/constants-ca/guardian'; import myEvents from 'utils/deviceEvent'; import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { APPROVAL_TO_OPERATION_MAP } from '@portkey-wallet/constants/constants-ca/verifier'; export const AuthTypes = [LoginType.Apple, LoginType.Google]; +const APPROVAL_TO_VERIFICATION_MAP = { + [ApprovalType.addGuardian]: VerificationType.addGuardian, + [ApprovalType.editGuardian]: VerificationType.editGuardian, + [ApprovalType.deleteGuardian]: VerificationType.deleteGuardian, + [ApprovalType.removeOtherManager]: VerificationType.removeOtherManager, + [ApprovalType.communityRecovery]: VerificationType.communityRecovery, +}; + interface GuardianAccountItemProps { guardianItem: UserGuardianItem; isButtonHide?: boolean; @@ -64,46 +73,15 @@ function GuardianItemButton({ const { status, requestCodeResult } = itemStatus || {}; const verifyToken = useVerifyToken(); const guardianInfo = useMemo(() => { - let _verificationType: VerificationType; - switch (approvalType) { - case ApprovalType.addGuardian: - _verificationType = VerificationType.addGuardianByApprove; - break; - case ApprovalType.editGuardian: - _verificationType = VerificationType.editGuardian; - break; - case ApprovalType.deleteGuardian: - _verificationType = VerificationType.deleteGuardian; - break; - case ApprovalType.removeOtherManager: - _verificationType = VerificationType.removeOtherManager; - break; - case ApprovalType.communityRecovery: - default: - _verificationType = VerificationType.communityRecovery; - break; - } return { guardianItem, - verificationType: _verificationType, + verificationType: + APPROVAL_TO_VERIFICATION_MAP[approvalType as ApprovalType] || VerificationType.communityRecovery, }; }, [approvalType, guardianItem]); const operationType: OperationTypeEnum = useMemo(() => { - switch (approvalType) { - case ApprovalType.addGuardian: - return OperationTypeEnum.addGuardian; - case ApprovalType.editGuardian: - return OperationTypeEnum.editGuardian; - case ApprovalType.deleteGuardian: - return OperationTypeEnum.deleteGuardian; - case ApprovalType.removeOtherManager: - return OperationTypeEnum.removeOtherManager; - case ApprovalType.communityRecovery: - return OperationTypeEnum.communityRecovery; - default: - return OperationTypeEnum.unknown; - } + return APPROVAL_TO_OPERATION_MAP[approvalType as ApprovalType] || OperationTypeEnum.unknown; }, [approvalType]); const onSetGuardianStatus = useCallback( From 421220d0eb927963eef329db99286e00fa5baa0f Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 12 Jul 2023 11:53:33 +0800 Subject: [PATCH 330/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20Guardia?= =?UTF-8?q?n=20Type=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Guardian/VerifierDetails/index.tsx | 7 ++++--- .../js/pages/Guardian/components/GuardianItem/index.tsx | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx index 7fe2d26235..92185c6857 100644 --- a/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/VerifierDetails/index.tsx @@ -105,9 +105,10 @@ export default function VerifierDetails() { } }, [caHash, getCurrentCAContract, guardianItem, managerAddress]); - const operationType: OperationTypeEnum = useMemo(() => { - return VERIFICATION_TO_OPERATION_MAP[verificationType as VerificationType] || OperationTypeEnum.unknown; - }, [verificationType]); + const operationType: OperationTypeEnum = useMemo( + () => VERIFICATION_TO_OPERATION_MAP[verificationType as VerificationType] || OperationTypeEnum.unknown, + [verificationType], + ); const onFinish = useLockCallback( async (code: string) => { diff --git a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx index 6bbc269794..def0fd16fb 100644 --- a/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Guardian/components/GuardianItem/index.tsx @@ -37,7 +37,7 @@ import { APPROVAL_TO_OPERATION_MAP } from '@portkey-wallet/constants/constants-c export const AuthTypes = [LoginType.Apple, LoginType.Google]; const APPROVAL_TO_VERIFICATION_MAP = { - [ApprovalType.addGuardian]: VerificationType.addGuardian, + [ApprovalType.addGuardian]: VerificationType.addGuardianByApprove, [ApprovalType.editGuardian]: VerificationType.editGuardian, [ApprovalType.deleteGuardian]: VerificationType.deleteGuardian, [ApprovalType.removeOtherManager]: VerificationType.removeOtherManager, @@ -80,9 +80,10 @@ function GuardianItemButton({ }; }, [approvalType, guardianItem]); - const operationType: OperationTypeEnum = useMemo(() => { - return APPROVAL_TO_OPERATION_MAP[approvalType as ApprovalType] || OperationTypeEnum.unknown; - }, [approvalType]); + const operationType: OperationTypeEnum = useMemo( + () => APPROVAL_TO_OPERATION_MAP[approvalType as ApprovalType] || OperationTypeEnum.unknown, + [approvalType], + ); const onSetGuardianStatus = useCallback( (guardianStatus: GuardiansStatusItem) => { From ed197e906b57ad8c0207285c60156a89d2b96870 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 12 Jul 2023 13:53:39 +0800 Subject: [PATCH 331/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20adjust=20show=20f?= =?UTF-8?q?aucet=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/components/BalanceCard/index.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/components/BalanceCard/index.tsx b/packages/web-extension-did/app/web/pages/components/BalanceCard/index.tsx index 970f4ce375..a828d82e1c 100644 --- a/packages/web-extension-did/app/web/pages/components/BalanceCard/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/BalanceCard/index.tsx @@ -20,26 +20,24 @@ export default function BalanceCard({ onSend, onReceive, onBuy, isShowBuy }: Bal const renderBuy = useMemo( () => - !!isShowBuy && - isMainNet && ( + !!isShowBuy && ( {t('Buy')} ), - [isShowBuy, isMainNet, onBuy, t], + [isShowBuy, onBuy, t], ); const renderFaucet = useMemo( () => - !!isShowBuy && !isMainNet && ( {t('Faucet')} ), - [isShowBuy, isMainNet, onBuy, t], + [isMainNet, onBuy, t], ); return ( From f73e3d0a3681939d03b16c849af9e88296853fa0 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 12 Jul 2023 13:58:06 +0800 Subject: [PATCH 332/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20constant=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constants/constants-ca/payment/index.ts | 16 +++++++-------- .../app/web/i18n/languages/en.json | 1 - .../app/web/pages/Buy/Preview/index.tsx | 8 ++++---- .../app/web/pages/Buy/index.tsx | 20 +++++++++---------- .../pages/Home/components/MyBalance/index.tsx | 4 ++-- .../app/web/pages/Token/Detail/index.tsx | 4 ++-- 6 files changed, 25 insertions(+), 28 deletions(-) diff --git a/packages/constants/constants-ca/payment/index.ts b/packages/constants/constants-ca/payment/index.ts index 014c162bf9..c648f536da 100644 --- a/packages/constants/constants-ca/payment/index.ts +++ b/packages/constants/constants-ca/payment/index.ts @@ -29,21 +29,19 @@ export enum TransDirectEnum { export const ACH_MERCHANT_NAME = 'Alchemy'; -export const FaucetUrl = 'https://testnet-faucet.aelf.io/'; +export const FAUCET_URL = 'https://testnet-faucet.aelf.io/'; export const SELL_SOCKET_TIMEOUT = 20 * 1000; -export const buySoonText = 'On-ramp is currently not supported. It will be launched in the coming weeks.'; +export const BUY_SOON_TEXT = 'On-ramp is currently not supported. It will be launched in the coming weeks.'; -export const sellSoonText = 'Off-ramp is currently not supported. It will be launched in the coming weeks.'; +export const SELL_SOON_TEXT = 'Off-ramp is currently not supported. It will be launched in the coming weeks.'; -export const serviceUnavailableText = 'Sorry, the service you are using is temporarily unavailable.'; +export const SERVICE_UNAVAILABLE_TEXT = 'Sorry, the service you are using is temporarily unavailable.'; -export const soonText = 'On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.'; - -export const disclaimer = +export const DISCLAIMER_TEXT = 'AlchemyPay is a fiat-to-crypto platform independently operated by a third-party entity. Portkey shall not be held liable for any losses or damages suffered as a result of using AlchemyPay services.'; -export const InsufficientFundsText = 'Insufficient funds'; +export const INSUFFICIENT_FUNDS_TEXT = 'Insufficient funds'; -export const SynchronizingChainText = 'Synchronizing on-chain account information...'; +export const SYNCHRONIZING_CHAIN_TEXT = 'Synchronizing on-chain account information...'; diff --git a/packages/web-extension-did/app/web/i18n/languages/en.json b/packages/web-extension-did/app/web/i18n/languages/en.json index 8ae5765336..eaf787a6bc 100644 --- a/packages/web-extension-did/app/web/i18n/languages/en.json +++ b/packages/web-extension-did/app/web/i18n/languages/en.json @@ -419,7 +419,6 @@ "This guardian is the only login account and cannot be removed": "This guardian is the only login account and cannot be removed", "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian": "This guardian is set as a login account. Click \"Confirm\" to unset and remove this guardian", "Creating address on the chain...": "Creating address on the chain...", - "On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.": "On-ramp is not supported on the Testnet. The on-ramp service on Mainnet is coming soon.", "On-ramp is currently not supported. It will be launched in the coming weeks.": "On-ramp is currently not supported. It will be launched in the coming weeks.", "Off-ramp is currently not supported. It will be launched in the coming weeks.": "Off-ramp is currently not supported. It will be launched in the coming weeks.", "Initiating social recovery...": "Initiating social recovery...", diff --git a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx index e65fe0fa03..79799d22df 100644 --- a/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/Preview/index.tsx @@ -12,8 +12,8 @@ import PromptFrame from 'pages/components/PromptFrame'; import { ACH_MERCHANT_NAME, TransDirectEnum, - disclaimer, - serviceUnavailableText, + DISCLAIMER_TEXT, + SERVICE_UNAVAILABLE_TEXT, } from '@portkey-wallet/constants/constants-ca/payment'; import clsx from 'clsx'; import { useGetAchTokenInfo } from '@portkey-wallet/hooks/hooks-ca/payment'; @@ -109,7 +109,7 @@ export default function Preview() { // Compatible with the situation where the function is turned off when the user is on the page. if ((side === PaymentTypeEnum.BUY && !isBuySectionShow) || (side === PaymentTypeEnum.SELL && !isSellSectionShow)) { setLoading(false); - message.error(serviceUnavailableText); + message.error(SERVICE_UNAVAILABLE_TEXT); return navigate('/'); } @@ -177,7 +177,7 @@ export default function Preview() { content: ( <>
    Disclaimer
    - {t(disclaimer)} + {t(DISCLAIMER_TEXT)} ), }); diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 8b520efad5..f0766e7835 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -38,11 +38,11 @@ import CustomTipModal from 'pages/components/CustomModal'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useCheckManagerSyncState } from 'hooks/wallet'; import { - buySoonText, - InsufficientFundsText, - sellSoonText, - serviceUnavailableText, - SynchronizingChainText, + BUY_SOON_TEXT, + INSUFFICIENT_FUNDS_TEXT, + SELL_SOON_TEXT, + SERVICE_UNAVAILABLE_TEXT, + SYNCHRONIZING_CHAIN_TEXT, } from '@portkey-wallet/constants/constants-ca/payment'; export default function Buy() { @@ -286,13 +286,13 @@ export default function Buy() { // Compatible with the situation where the function is turned off when the user is on the page. if (side === PaymentTypeEnum.BUY && !isBuySectionShow) { CustomTipModal({ - content: t(buySoonText), + content: t(BUY_SOON_TEXT), }); return; } if (side === PaymentTypeEnum.SELL && !isSellSectionShow) { CustomTipModal({ - content: t(sellSoonText), + content: t(SELL_SOON_TEXT), }); return; } @@ -364,7 +364,7 @@ export default function Buy() { const setInsufficientFundsMsg = useCallback(() => { stopInterval(); - setErrMsg(InsufficientFundsText); + setErrMsg(INSUFFICIENT_FUNDS_TEXT); valueSaveRef.current.isShowErrMsg = true; setReceive(''); @@ -380,7 +380,7 @@ export default function Buy() { // Compatible with the situation where the function is turned off when the user is on the page. if ((side === PaymentTypeEnum.BUY && !isBuySectionShow) || (side === PaymentTypeEnum.SELL && !isSellSectionShow)) { setLoading(false); - message.error(serviceUnavailableText); + message.error(SERVICE_UNAVAILABLE_TEXT); return navigate('/'); } @@ -391,7 +391,7 @@ export default function Buy() { if (!_isManagerSynced) { setLoading(false); - setWarningMsg(SynchronizingChainText); + setWarningMsg(SYNCHRONIZING_CHAIN_TEXT); return; } else { setWarningMsg(''); diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index 34a73936a8..9f24a2b5a4 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -25,7 +25,7 @@ import { useFreshTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPric import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; import useVerifierList from 'hooks/useVerifierList'; import useGuardianList from 'hooks/useGuardianList'; -import { FaucetUrl } from '@portkey-wallet/constants/constants-ca/payment'; +import { FAUCET_URL } from '@portkey-wallet/constants/constants-ca/payment'; import { BalanceTab } from '@portkey-wallet/constants/constants-ca/assets'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; @@ -161,7 +161,7 @@ export default function MyBalance() { if (isMainNet) { navigate('/buy'); } else { - const openWinder = window.open(FaucetUrl, '_blank'); + const openWinder = window.open(FAUCET_URL, '_blank'); if (openWinder) { openWinder.opener = null; } diff --git a/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx b/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx index 8700a8b42a..dfe39f5cd2 100644 --- a/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx +++ b/packages/web-extension-did/app/web/pages/Token/Detail/index.tsx @@ -9,7 +9,7 @@ import clsx from 'clsx'; import { useCommonState } from 'store/Provider/hooks'; import PromptFrame from 'pages/components/PromptFrame'; import { useFreshTokenPrice, useAmountInUsdShow } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; -import { FaucetUrl } from '@portkey-wallet/constants/constants-ca/payment'; +import { FAUCET_URL } from '@portkey-wallet/constants/constants-ca/payment'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import './index.less'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; @@ -36,7 +36,7 @@ function TokenDetail() { if (isMainNet) { navigate('/buy', { state: { tokenInfo: currentToken } }); } else { - const openWinder = window.open(FaucetUrl, '_blank'); + const openWinder = window.open(FAUCET_URL, '_blank'); if (openWinder) { openWinder.opener = null; } From db8131d01cfc2efacbe17d0d93444c2f5051b366 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 12 Jul 2023 14:29:59 +0800 Subject: [PATCH 333/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20records?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TabsDrawer/TabsDrawerContent.tsx | 54 +++++++++-- .../mobile-app-did/js/hooks/useScanLink.ts | 0 .../js/pages/DashBoard/Card/style.ts | 3 +- .../js/pages/Discover/DiscoverHome/index.tsx | 52 ++++++++++- .../pages/Discover/DiscoverSearch/index.tsx | 5 +- .../js/pages/Discover/RecordsPage/index.tsx | 84 +++++++++++++++++ .../DiscoverCmsListSection/index.tsx | 6 +- .../DiscoverRecordListSection/index.tsx | 89 +++++++++++++++++++ .../index.tsx | 4 +- .../index.tsx | 6 +- 10 files changed, 281 insertions(+), 22 deletions(-) create mode 100644 packages/mobile-app-did/js/hooks/useScanLink.ts create mode 100644 packages/mobile-app-did/js/pages/Discover/RecordsPage/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx rename packages/mobile-app-did/js/pages/Discover/components/{RecordItem => SearchRecordItem}/index.tsx (94%) rename packages/mobile-app-did/js/pages/Discover/components/{RecordSection => SearchRecordSection}/index.tsx (94%) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 1a537608d5..5e789ee3ee 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -30,6 +30,7 @@ import { BrowserContext, IBrowserTab } from './context'; import { useHardwareBackPress } from '@portkey-wallet/hooks/mobile'; import Svg from 'components/Svg'; import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import ActionSheet from 'components/ActionSheet'; const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); @@ -108,6 +109,28 @@ const TabsDrawerContent: React.FC = () => { return tabs?.find(ele => ele.id === activeTabId); }, [activeTabId, tabs]); + const closeAll = useCallback(() => { + if (tabs?.length === 0) return; + + ActionSheet.alert({ + title: 'Close all tabs?', + buttons: [ + { + title: t('Cancel'), + type: 'outline', + }, + { + title: t('Confirm'), + type: 'solid', + onPress: () => { + dispatch(closeAllTabs({ networkType })); + dispatch(changeDrawerOpenStatus(false)); + }, + }, + ], + }); + }, [dispatch, networkType, t, tabs?.length]); + useHardwareBackPress( useMemo(() => { if (isDrawerOpen) { @@ -154,16 +177,20 @@ const TabsDrawerContent: React.FC = () => {
    - dispatch(closeAllTabs({ networkType }))}> + {t('Close All')} - dispatch(changeDrawerOpenStatus(false))}> + dispatch(changeDrawerOpenStatus(false))}> { - if (tabs?.length === 0) return; + if (tabs?.length === 0) return dispatch(changeDrawerOpenStatus(false)); if (tabs?.find(ele => ele.id === preActiveTabId)) { dispatch(setActiveTab({ id: preActiveTabId, networkType })); } else { @@ -241,14 +268,29 @@ const handleButtonStyle = StyleSheet.create({ alignItems: 'center', width: screenWidth, height: pTd(44), - paddingLeft: pTd(20), - paddingRight: pTd(20), position: 'absolute', bottom: 0, backgroundColor: defaultColors.bg1, }, handleItem: { flex: 1, + lineHeight: pTd(30), + paddingLeft: pTd(20), + paddingRight: pTd(20), + }, + close: { + textAlign: 'left', + }, + add: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + done: { + textAlign: 'right', + }, + noTap: { + color: 'red', }, }); diff --git a/packages/mobile-app-did/js/hooks/useScanLink.ts b/packages/mobile-app-did/js/hooks/useScanLink.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/mobile-app-did/js/pages/DashBoard/Card/style.ts b/packages/mobile-app-did/js/pages/DashBoard/Card/style.ts index 38cabef08a..2c198d611e 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/Card/style.ts +++ b/packages/mobile-app-did/js/pages/DashBoard/Card/style.ts @@ -25,8 +25,9 @@ export const styles = StyleSheet.create({ flex: 1, }, svgWrap: { - marginTop: -pTd(16), + marginTop: -pTd(32), padding: pTd(16), + paddingTop: pTd(40), }, usdtBalance: { ...fonts.mediumFont, diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index c76dc76eff..172b34704a 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -1,21 +1,62 @@ -import React from 'react'; -import { StyleSheet, View } from 'react-native'; +import React, { useCallback } from 'react'; +import { ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import navigationService from 'utils/navigationService'; import SimulatedInputBox from 'components/SimulatedInputBox'; import { DiscoverCmsListSection } from '../components/DiscoverCmsListSection'; +import { DiscoverRecordListSection } from '../components/DiscoverRecordListSection'; + import { defaultColors } from 'assets/theme'; import SafeAreaBox from 'components/SafeAreaBox'; import CustomHeader from 'components/CustomHeader'; import { BGStyles } from 'assets/theme/styles'; +import useQrScanPermission from 'hooks/useQrScanPermission'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import ActionSheet from 'components/ActionSheet'; +import { useLanguage } from 'i18n/hooks'; export default function DiscoverHome() { + const [, requestQrPermission] = useQrScanPermission(); + const { t } = useLanguage(); + + const showDialog = useCallback( + () => + ActionSheet.alert({ + title: t('Enable Camera Access'), + message: t('Cannot connect to the camera. Please make sure it is turned on'), + buttons: [ + { + title: t('Close'), + type: 'solid', + }, + ], + }), + [t], + ); + return ( - + { + if (!(await requestQrPermission())) return showDialog(); + }}> + + + } + themeType="blue" + titleDom={'Discover'} + /> navigationService.navigate('DiscoverSearch')} /> - + + + + ); @@ -29,4 +70,7 @@ const styles = StyleSheet.create({ inputContainer: { ...GStyles.paddingArg(8, 20), }, + svgWrap: { + padding: pTd(16), + }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 24ab45fecc..1691c8dbf0 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -11,7 +11,7 @@ import { TextM } from 'components/CommonText'; import { useFocusEffect } from '@react-navigation/native'; import Svg from 'components/Svg'; import fonts from 'assets/theme/fonts'; -import RecordSection from '../components/RecordSection'; +import RecordSection from '../components/SearchRecordSection'; import SearchDiscoverSection from '../components/SearchDiscoverSection'; import { isIOS } from '@rneui/base'; import { checkIsUrl, getHost, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; @@ -32,7 +32,6 @@ export default function DiscoverSearch() { const [filteredDiscoverList, setFilteredDiscoverList] = useState([]); const clearText = useCallback(() => setValue(''), []); - const navBack = useCallback(() => navigationService.goBack(), []); const flatList = useMemo((): DiscoverItem[] => { const list = [] as DiscoverItem[]; @@ -119,7 +118,7 @@ export default function DiscoverSearch() { rightIconContainerStyle={styles.rightIconContainerStyle} style={styles.rnInputStyle} /> - + {t('Cancel')} diff --git a/packages/mobile-app-did/js/pages/Discover/RecordsPage/index.tsx b/packages/mobile-app-did/js/pages/Discover/RecordsPage/index.tsx new file mode 100644 index 0000000000..7ed671be88 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/RecordsPage/index.tsx @@ -0,0 +1,84 @@ +import React, { useCallback, useMemo, useRef, useState } from 'react'; +import { StyleSheet, View } from 'react-native'; +import GStyles from 'assets/theme/GStyles'; +import { BGStyles } from 'assets/theme/styles'; +import PageContainer from 'components/PageContainer'; +import { useLanguage } from 'i18n/hooks'; +import { pTd } from 'utils/unit'; +import { TextM } from 'components/CommonText'; +import Svg from 'components/Svg'; +import fonts from 'assets/theme/fonts'; +import { clearRecordsList } from '@portkey-wallet/store/store-ca/discover/slice'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; +import CommonButton from 'components/CommonButton'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; + +export default function RecordsPage() { + const { t } = useLanguage(); + + const dispatch = useAppCommonDispatch(); + const { networkType } = useCurrentNetworkInfo(); + const { discoverMap } = useAppCASelector(state => state.discover); + + const showRecordList = useMemo(() => { + const recordsList = (discoverMap?.[networkType]?.recordsList as ITabItem[]) || []; + return recordsList.map(ele => ele).reverse(); + }, [discoverMap, networkType]); + + const clearRecord = useCallback(() => { + dispatch(clearRecordsList({ networkType })); + }, [dispatch, networkType]); + if (showRecordList?.length === 0) return null; + + return ( + + + {showRecordList.map((ele, index) => ( + {'111'} + ))} + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + paddingLeft: 0, + paddingRight: 0, + }, + inputContainer: { + ...GStyles.paddingArg(8, 20), + }, + inputStyle: { + width: pTd(280), + }, + sectionWrap: { + ...GStyles.paddingArg(24, 20), + }, + rnInputStyle: { + fontSize: pTd(14), + }, + headerWrap: { + height: pTd(22), + }, + header: { + ...fonts.mediumFont, + lineHeight: pTd(24), + }, + cancelButton: { + paddingLeft: pTd(12), + lineHeight: pTd(36), + }, + rightIconContainerStyle: { + marginRight: pTd(10), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index be524d3fe8..dfc7aab198 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -7,7 +7,7 @@ import { FontStyles } from 'assets/theme/styles'; import { TextM, TextS } from 'components/CommonText'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import React, { useCallback } from 'react'; -import { ScrollView, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; +import { StyleSheet, View, Image, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; @@ -30,7 +30,7 @@ export function DiscoverCmsListSection() { ); return ( - + {GroupList.map((group, index) => ( {group.title} @@ -49,7 +49,7 @@ export function DiscoverCmsListSection() { ))} - + ); } diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx new file mode 100644 index 0000000000..29e445d0c1 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx @@ -0,0 +1,89 @@ +import { useDiscoverGroupList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; +import { TextM, TextS } from 'components/CommonText'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import React, { useCallback } from 'react'; +import { ScrollView, StyleSheet, View, Image, TouchableOpacity } from 'react-native'; +import { pTd } from 'utils/unit'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import { useLanguage } from 'i18n/hooks'; + +export function DiscoverRecordListSection() { + const { t } = useLanguage(); + + return ( + + + {t('Records')} + {t('See All')} + + + + {[1, 2, 3].map(ele => ( + {ele} + ))} + + + {/* {[].map((group, index) => ( + + {group.title} + + {group.items.map((item, i) => ( + onClickJump(item)}> + + + + + {item.description} + + + + ))} + + + ))} */} + + ); +} + +const styles = StyleSheet.create({ + wrap: { + ...GStyles.paddingArg(8, 20), + flex: 1, + }, + header: { + display: 'flex', + }, + groupTitle: { + marginBottom: pTd(8), + marginTop: pTd(16), + }, + itemsGroup: { + borderRadius: pTd(6), + backgroundColor: defaultColors.bg1, + overflow: 'hidden', + }, + itemWrap: { + backgroundColor: defaultColors.bg1, + display: 'flex', + flexDirection: 'row', + ...GStyles.paddingArg(16, 12), + width: '100%', + }, + image: { + width: pTd(40), + height: pTd(40), + marginRight: pTd(16), + borderRadius: pTd(20), + }, + right: { + flex: 1, + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx similarity index 94% rename from packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx rename to packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx index e239eb1f7d..d5acbdbd73 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx @@ -14,7 +14,7 @@ type RecordListItemType = { onPress?: () => void; }; -const RecordItem: React.FC = props => { +const SearchRecordItem: React.FC = props => { const { item, onPress } = props; return ( @@ -32,7 +32,7 @@ const RecordItem: React.FC = props => { ); }; -export default memo(RecordItem); +export default memo(SearchRecordItem); const itemStyle = StyleSheet.create({ wrap: { diff --git a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx similarity index 94% rename from packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx rename to packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx index bf3a635002..b330ac4592 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/RecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx @@ -8,7 +8,7 @@ import { TextL, TextS } from 'components/CommonText'; import fonts from 'assets/theme/fonts'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import RecordItem from '../RecordItem'; +import SearchRecordItem from '../SearchRecordItem'; import { addRecordsItem, changeDrawerOpenStatus, @@ -18,7 +18,7 @@ import { import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -export default function RecordSection() { +export default function SearchRecordSection() { const { t } = useLanguage(); const dispatch = useAppCommonDispatch(); @@ -45,7 +45,7 @@ export default function RecordSection() { {(showRecordList ?? []).map((item, index) => ( - { From 26a487645be97cbf7cbf88fe4522a287dcb2a9a1 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 12 Jul 2023 14:36:15 +0800 Subject: [PATCH 334/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/index.ts | 1 + packages/contracts/utils/ContractBasic.ts | 6 +-- packages/hooks/hooks-ca/payment.ts | 9 ++-- .../components/TransactionOverlay/index.tsx | 2 +- .../ViewOnWebView/hooks/useHandleAchSell.ts | 46 +++++++++++-------- .../js/pages/Send/SendHome/index.tsx | 2 +- packages/types/types-ca/payment.ts | 8 +++- .../web-extension-did/app/web/sandboxUtil.ts | 2 +- 8 files changed, 45 insertions(+), 31 deletions(-) diff --git a/packages/api/api-did/payment/index.ts b/packages/api/api-did/payment/index.ts index 5f14e287bc..091116d949 100644 --- a/packages/api/api-did/payment/index.ts +++ b/packages/api/api-did/payment/index.ts @@ -16,4 +16,5 @@ export default { }, updateAchOrder: '/api/app/thirdPart/order/alchemy', updateAlchemyOrderTxHash: 'api/app/thirdPart/alchemy/txHash', + sendSellTransaction: 'api/app/thirdPart/alchemy/transaction', } as const; diff --git a/packages/contracts/utils/ContractBasic.ts b/packages/contracts/utils/ContractBasic.ts index bb50a49527..17688d9e00 100644 --- a/packages/contracts/utils/ContractBasic.ts +++ b/packages/contracts/utils/ContractBasic.ts @@ -119,15 +119,15 @@ export class AElfContractBasic { paramsOption, functionName: _functionName, }); - const raw = await encodedTx({ + const data = await encodedTx({ instance: this.aelfInstance, contract: this.aelfContract, paramsOption: _params, functionName: _functionName, }); - return raw; + return { data }; } catch (error) { - return handleContractError(error); + return { error: handleContractError(error) }; } }; } diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index afc5fc116b..18844c1551 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -84,15 +84,14 @@ export const useSellTransfer = () => { try { const result = await paymentSellTransfer(achTxAddressReceived); - if (result.error || !result.transactionId) { - throw new Error('Transaction failed.'); - } - await request.payment.updateAlchemyOrderTxHash({ + await request.payment.sendSellTransaction({ params: { merchantName: ACH_MERCHANT_NAME, orderId, - txHash: result.transactionId, + rawTransaction: result.rawTransaction, + signature: result.signature, + publicKey: result.publicKey, }, }); } catch (error) { diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 8e6b19687f..5875f14484 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -264,7 +264,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { const { TransactionFee } = await customFetch(`${chainInfo?.endPoint}/api/blockChain/calculateTransactionFee`, { method: 'POST', params: { - RawTransaction: raw, + RawTransaction: raw.data, }, }); diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index 8e4a9e0f97..8ac7d8885c 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -4,14 +4,15 @@ import { useAssets } from '@portkey-wallet/hooks/hooks-ca/assets'; import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useSellTransfer } from '@portkey-wallet/hooks/hooks-ca/payment'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { AchTxAddressReceivedType } from '@portkey-wallet/types/types-ca/payment'; +import { AchTxAddressReceivedType, PaymentSellTransferResult } from '@portkey-wallet/types/types-ca/payment'; import { timesDecimals } from '@portkey-wallet/utils/converter'; import CommonToast from 'components/CommonToast'; import Loading from 'components/Loading'; import { usePin } from 'hooks/store'; import { useCallback, useMemo } from 'react'; import { getManagerAccount } from 'utils/redux'; -import sameChainTransfer from 'utils/transfer/sameChainTransfer'; +import AElf from 'aelf-sdk'; +import SparkMD5 from 'spark-md5'; export const useHandleAchSell = () => { const sellTransfer = useSellTransfer(); @@ -25,7 +26,7 @@ export const useHandleAchSell = () => { const wallet = useCurrentWalletInfo(); const paymentSellTransfer = useCallback( - async (params: AchTxAddressReceivedType) => { + async (params: AchTxAddressReceivedType): Promise => { const { decimals, symbol, chainId } = aelfToken || {}; const { caContractAddress, endPoint } = chainInfo || {}; const { caHash } = wallet; @@ -51,25 +52,32 @@ export const useHandleAchSell = () => { rpcUrl: endPoint, account: account, }); - const amount = timesDecimals(params.cryptoAmount, decimals).toNumber(); - // return managerTransfer({ - // contract, - // paramsOption: { - // caHash, - // symbol: aelfToken.symbol, - // to: params.address, - // amount, - // }, - // }); - return await sameChainTransfer({ - contract, - tokenInfo: { ...aelfToken, address: aelfToken.tokenContractAddress || '' }, - caHash: caHash, - amount, - toAddress: `ELF_${params.address}_AELF`, + const rawResult = await contract.encodedTx('ManagerForwardCall', { + caHash, + contractAddress: aelfToken.tokenContractAddress || '', + methodName: 'Transfer', + args: { + symbol: aelfToken.symbol, + to: `ELF_${params.address}_AELF`, + amount, + memo: '', + }, }); + if (!rawResult || !rawResult.data) { + throw new Error('Failed to get raw transaction.'); + } + + const publicKey = (account.keyPair as any).getPublic('hex'); + + const message = SparkMD5.hash(`${params.orderId}${rawResult.data}`); + const signature = AElf.wallet.sign(message, account.keyPair).toString('hex'); + return { + rawTransaction: rawResult.data, + publicKey, + signature, + }; }, [aelfToken, chainInfo, pin, wallet], ); diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index f27b24c2c1..3068699e4a 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -108,7 +108,7 @@ const SendHome: React.FC = () => { const { TransactionFee } = await customFetch(`${chainInfo?.endPoint}/api/blockChain/calculateTransactionFee`, { method: 'POST', params: { - RawTransaction: raw, + RawTransaction: raw.data, }, }); diff --git a/packages/types/types-ca/payment.ts b/packages/types/types-ca/payment.ts index 23bc038aff..0b5faaa3e8 100644 --- a/packages/types/types-ca/payment.ts +++ b/packages/types/types-ca/payment.ts @@ -18,8 +18,14 @@ export interface AchTxAddressReceivedType { address: string; } +export interface PaymentSellTransferResult { + publicKey: string; + signature: string; // sign(md5(orderId + rawTransaction)) + rawTransaction: string; +} + export type SellTransferParams = Pick & { - paymentSellTransfer: (params: AchTxAddressReceivedType) => Promise; + paymentSellTransfer: (params: AchTxAddressReceivedType) => Promise; }; export enum PaymentTypeEnum { diff --git a/packages/web-extension-did/app/web/sandboxUtil.ts b/packages/web-extension-did/app/web/sandboxUtil.ts index a95a4fea3f..94b52a8d2e 100644 --- a/packages/web-extension-did/app/web/sandboxUtil.ts +++ b/packages/web-extension-did/app/web/sandboxUtil.ts @@ -287,7 +287,7 @@ class SandboxUtil { const transaction = await customFetch(`${rpcUrl}/api/blockChain/calculateTransactionFee`, { method: 'POST', params: { - RawTransaction: raw, + RawTransaction: raw.data, }, }); if (!transaction?.Success) throw 'Transaction failed'; From ee9308e22d5bb7208f61901deeea889c6de39e09 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Thu, 13 Jul 2023 14:48:47 +0800 Subject: [PATCH 335/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20link=20dapp=20sc?= =?UTF-8?q?heme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/AuthLoginOverlay/index.tsx | 5 +- .../js/components/BrowserTab/index.tsx | 29 ++++++++--- .../js/components/ProviderWebview/index.tsx | 31 ++++++++---- .../TabsDrawer/TabsDrawerContent.tsx | 22 +++++++-- .../mobile-app-did/js/constants/scheme.ts | 1 + .../js/dapp/dappMobileOperator.ts | 9 ++++ packages/mobile-app-did/js/hooks/discover.ts | 4 +- packages/mobile-app-did/js/hooks/useScheme.ts | 49 ++++++++++++++++++- .../js/pages/Activity/ViewOnWebView/index.tsx | 1 + .../mobile-app-did/js/store/rootReducer.ts | 2 +- packages/mobile-app-did/js/types/common.ts | 4 ++ packages/mobile-app-did/js/utils/scheme.ts | 22 --------- packages/store/store-ca/discover/slice.ts | 10 ++++ packages/store/store-ca/discover/type.ts | 1 + 14 files changed, 142 insertions(+), 48 deletions(-) diff --git a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx index 944a9020cb..8139c37b14 100644 --- a/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/AuthLoginOverlay/index.tsx @@ -43,7 +43,7 @@ function ThirdAuthImage({ websiteIcon, domain, style }: { websiteIcon?: string; function AuthLogin({ loginData, domain, extraData: websiteInfo }: AuthLoginOverlayPropsTypes) { const { t } = useLanguage(); - const { address: managerAddress, extraData: qrExtraData, deviceType } = loginData || {}; + const { address: managerAddress, extraData: qrExtraData } = loginData || {}; const { caHash, address } = useCurrentWalletInfo(); const { currentNetwork } = useWallet(); @@ -71,7 +71,7 @@ function AuthLogin({ loginData, domain, extraData: websiteInfo }: AuthLoginOverl try { setLoading(true); - const deviceInfo = getDeviceInfoFromQR(qrExtraData, deviceType); + const deviceInfo = getDeviceInfoFromQR(qrExtraData); const contract = await getCurrentCAContract(); const extraData = await extraDataEncode(deviceInfo || {}, true); @@ -92,7 +92,6 @@ function AuthLogin({ loginData, domain, extraData: websiteInfo }: AuthLoginOverl loading, managerAddress, qrExtraData, - deviceType, getCurrentCAContract, address, ]); diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 9370633671..875f0ae14a 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -1,7 +1,7 @@ -import React, { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; +import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; import ProviderWebview, { IWebView } from 'components/ProviderWebview'; -import { captureRef } from 'react-native-view-shot'; +import { CaptureOptions, captureRef } from 'react-native-view-shot'; import { IBrowserTab, useBrowser } from 'components/TabsDrawer/context'; import Progressbar, { IProgressbar } from 'components/Progressbar'; import HttpModal from './components/HttpModal'; @@ -9,18 +9,21 @@ import HttpModal from './components/HttpModal'; type BrowserTabProps = { isHidden: boolean; uri: string; + autoApprove?: boolean; + onLoadEnd?: () => void; }; -const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri }, forward) { +const Options: CaptureOptions = { quality: 0.2, format: 'jpg' }; + +const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri, onLoadEnd }, forward) { const viewRef = useRef(null); const webViewRef = useRef(null); const progressbarRef = useRef(null); - const { setTabRef } = useBrowser(); - + const isApproved = useRef(false); const options = useMemo( () => ({ - capture: () => captureRef(viewRef?.current, { quality: 0.2, format: 'jpg' }), + capture: () => captureRef(viewRef?.current, Options), reload: () => webViewRef.current?.reload(), }), [], @@ -33,6 +36,13 @@ const BrowserTab = forwardRef(function BrowserTab( // eslint-disable-next-line react-hooks/exhaustive-deps }, [isHidden, options]); + const onPageLoadEnd = useCallback(() => { + if (!isApproved.current) { + isApproved.current = true; + webViewRef.current?.autoApprove(); + } + onLoadEnd?.(); + }, [onLoadEnd]); return ( (function BrowserTab( progressbarRef.current?.changeInnerBarWidth(nativeEvent.progress)} /> @@ -50,7 +61,11 @@ const BrowserTab = forwardRef(function BrowserTab( }); export default memo(BrowserTab, (prevProps: BrowserTabProps, nextProps: BrowserTabProps) => { - return prevProps.isHidden === nextProps.isHidden && prevProps.uri === nextProps.uri; + return ( + prevProps.isHidden === nextProps.isHidden && + prevProps.uri === nextProps.uri && + prevProps.autoApprove === nextProps.autoApprove + ); }); export const styles = StyleSheet.create({ diff --git a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx index 8d15dde44a..ddbbdb81d0 100644 --- a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx +++ b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx @@ -19,6 +19,7 @@ export interface IWebView { postMessage: WebView['postMessage']; injectJavaScript: WebView['injectJavaScript']; goForward: WebView['goForward']; + autoApprove: () => void; } const ProviderWebview = forwardRef(function ProviderWebview(props, forward) { @@ -104,6 +105,10 @@ const ProviderWebview = forwardRef(function * Posts a message to WebView. */ postMessage: (message: string) => webViewRef.current?.postMessage(message), + /** + * auto approve + */ + autoApprove: () => operatorRef.current?.autoApprove(), }), [], ); @@ -114,17 +119,25 @@ const ProviderWebview = forwardRef(function style={styles.webView} decelerationRate="normal" injectedJavaScriptBeforeContentLoaded={isIos ? entryScriptWeb3 : undefined} - onMessage={({ nativeEvent }) => { - operatorRef.current?.handleRequestMessage(nativeEvent.data); - }} - onLoadStart={onLoadStart} - onLoad={handleUpdate} - onLoadProgress={({ nativeEvent }) => { - console.log(nativeEvent.progress, '=onLoadProgress'); - }} - onLoadEnd={handleUpdate} applicationNameForUserAgent={'WebView Portkey did Mobile'} {...props} + onLoadEnd={event => { + handleUpdate(event); + props.onLoadEnd?.(event); + }} + onLoadStart={event => { + onLoadStart(event); + props.onLoadStart?.(event); + }} + onLoad={event => { + handleUpdate(event); + props.onLoad?.(event); + }} + onMessage={event => { + const { nativeEvent } = event; + operatorRef.current?.handleRequestMessage(nativeEvent.data); + props.onMessage?.(event); + }} /> ); }); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 1a537608d5..46ea98033d 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -16,6 +16,7 @@ import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { changeDrawerOpenStatus, closeAllTabs, + removeAutoApproveItem, setActiveTab, updateTab, } from '@portkey-wallet/store/store-ca/discover/slice'; @@ -35,7 +36,13 @@ const TabsDrawerContent: React.FC = () => { const { t } = useLanguage(); const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { isDrawerOpen, discoverMap = {}, initializedList, activeTabId } = useAppCASelector(state => state.discover); + const { + isDrawerOpen, + discoverMap = {}, + initializedList, + activeTabId, + autoApproveMap, + } = useAppCASelector(state => state.discover); const { tabs } = discoverMap[networkType] ?? {}; @@ -91,9 +98,18 @@ const TabsDrawerContent: React.FC = () => { const isHidden = activeTabId !== ele.id; const initialized = initializedList?.has(ele.id); if (isHidden && !initialized) return; - return ; + const autoApprove = autoApproveMap?.[ele.id]; + return ( + dispatch(removeAutoApproveItem(ele.id)) : undefined} + /> + ); }); - }, [activeTabId, initializedList, tabs]); + }, [activeTabId, autoApproveMap, dispatch, initializedList, tabs]); const value = useMemo( () => ({ diff --git a/packages/mobile-app-did/js/constants/scheme.ts b/packages/mobile-app-did/js/constants/scheme.ts index 1b7f177739..e8db76c6d5 100644 --- a/packages/mobile-app-did/js/constants/scheme.ts +++ b/packages/mobile-app-did/js/constants/scheme.ts @@ -2,6 +2,7 @@ export const DID_SCHEME = 'portkey.did'; export enum SCHEME_ACTION { login = 'login', + linkDapp = 'linkDapp', } export enum AUTH_LOGIN_TYPE { diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index a424e06f99..76754bf987 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -300,6 +300,15 @@ export default class DappMobileOperator extends Operator { callBack: callBack!, }); }; + public autoApprove = () => { + // auto approve + this.handleSendRequest({ + origin: this.dapp.origin, + // no feedback required + eventName: 'event', + method: MethodsBase.REQUEST_ACCOUNTS, + }); + }; handleRequest = async (request: IRequestParams): Promise => { if (SEND_METHOD[request.method]) return this.handleSendRequest(request); diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 14d478b393..dfef4e0fa6 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -7,6 +7,7 @@ import { addRecordsItem, createNewTab, initNetworkDiscoverMap, + addAutoApproveItem, } from '@portkey-wallet/store/store-ca/discover/slice'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { useCallback } from 'react'; @@ -20,13 +21,14 @@ export const useDiscoverJumpWithNetWork = () => { const { discoverMap } = useAppCASelector(state => state.discover); - const discoverJump = ({ item }: { item: ITabItem }) => { + const discoverJump = ({ item, autoApprove }: { item: ITabItem; autoApprove?: boolean }) => { if (!discoverMap || !discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); dispatch(createNewTab({ ...item, networkType })); dispatch(setActiveTab({ ...item, networkType })); dispatch(addRecordsItem({ ...item, networkType })); dispatch(changeDrawerOpenStatus(true)); + if (autoApprove) dispatch(addAutoApproveItem(item.id)); }; return discoverJump; diff --git a/packages/mobile-app-did/js/hooks/useScheme.ts b/packages/mobile-app-did/js/hooks/useScheme.ts index 3141a329a8..00430dae27 100644 --- a/packages/mobile-app-did/js/hooks/useScheme.ts +++ b/packages/mobile-app-did/js/hooks/useScheme.ts @@ -3,12 +3,57 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { Linking } from 'react-native'; import { usePin } from './store'; import useEffectOnce from './useEffectOnce'; -import { handleParsedUrl, handleScheme } from 'utils/scheme'; +import { checkAuthLoginData, handleScheme } from 'utils/scheme'; +import { useDiscoverJumpWithNetWork } from './discover'; +import { SchemeParsedUrl } from 'types/common'; +import { SCHEME_ACTION } from 'constants/scheme'; +import { showAuthLogin } from 'components/AuthLoginOverlay'; +import { checkIsUrl, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; + +export function useHandleParsedUrl() { + const jumpToWebview = useDiscoverJumpWithNetWork(); + return useCallback( + (parsedUrl: SchemeParsedUrl) => { + const { domain, action, query } = parsedUrl; + try { + switch (action) { + case SCHEME_ACTION.login: { + let { extraData, data } = query as any; + extraData = JSON.parse(extraData); + data = JSON.parse(data); + if (checkAuthLoginData(extraData, data)) showAuthLogin({ loginData: data, extraData: extraData, domain }); + break; + } + case SCHEME_ACTION.linkDapp: { + const { url } = query; + if (typeof url !== 'string' || !checkIsUrl(url)) return; + const fixUrl = prefixUrlWithProtocol(url); + jumpToWebview({ + item: { + id: Date.now(), + name: fixUrl, + url: fixUrl, + }, + autoApprove: true, + }); + break; + } + + default: + console.log('this action is not supported'); + } + } catch (error) { + console.log(error); + } + }, + [jumpToWebview], + ); +} export default function useScheme() { const { address, caHash } = useCurrentWalletInfo(); const [schemeUrl, setSchemeUrl] = useState(); - + const handleParsedUrl = useHandleParsedUrl(); const pin = usePin(); const logged = useMemo(() => !!address && caHash, [address, caHash]); diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx index 28d4d7d8b9..b4243c3051 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/index.tsx @@ -88,6 +88,7 @@ const ViewOnWebView: React.FC = () => { // cacheEnabled={false} injectedJavaScript={injectedJavaScript} // incognito={incognito} + applicationNameForUserAgent={'View Only WebView Portkey did Mobile'} onLoadProgress={({ nativeEvent }) => progressBarRef.current?.changeInnerBarWidth(nativeEvent.progress)} /> diff --git a/packages/mobile-app-did/js/store/rootReducer.ts b/packages/mobile-app-did/js/store/rootReducer.ts index 115947913b..4ce514c96c 100644 --- a/packages/mobile-app-did/js/store/rootReducer.ts +++ b/packages/mobile-app-did/js/store/rootReducer.ts @@ -30,7 +30,7 @@ const userPersistConfig = { const discoverPersistConfig = { key: discoverSlice.name, storage: AsyncStorage, - blacklist: ['isDrawerOpen', 'initializedList', 'activeTabId'], + blacklist: ['isDrawerOpen', 'initializedList', 'activeTabId', 'autoApproveMap'], }; export const userReducer = persistReducer(userPersistConfig, userSlice.reducer); diff --git a/packages/mobile-app-did/js/types/common.ts b/packages/mobile-app-did/js/types/common.ts index 0979e6fadb..a650813c97 100644 --- a/packages/mobile-app-did/js/types/common.ts +++ b/packages/mobile-app-did/js/types/common.ts @@ -12,3 +12,7 @@ export type SchemeParsedUrl = { action: SCHEME_ACTION; query: ParsedQuery; }; + +export type LinkDappData = { + url: string; +}; diff --git a/packages/mobile-app-did/js/utils/scheme.ts b/packages/mobile-app-did/js/utils/scheme.ts index 9618a049b6..7911ba0a16 100644 --- a/packages/mobile-app-did/js/utils/scheme.ts +++ b/packages/mobile-app-did/js/utils/scheme.ts @@ -1,7 +1,6 @@ import { DID_SCHEME, SCHEME_ACTION } from 'constants/scheme'; import { parseUrl } from 'query-string'; import { SchemeParsedUrl } from 'types/common'; -import { showAuthLogin } from 'components/AuthLoginOverlay'; import { isAddress } from '@portkey-wallet/utils'; export function handleScheme(str: string): SchemeParsedUrl | undefined { @@ -16,27 +15,6 @@ export function handleScheme(str: string): SchemeParsedUrl | undefined { return { domain, action, query }; } -export function handleParsedUrl(parsedUrl: SchemeParsedUrl) { - const { domain, action, query } = parsedUrl; - - try { - switch (action) { - case SCHEME_ACTION.login: { - let { extraData, data } = query as any; - extraData = JSON.parse(extraData); - data = JSON.parse(data); - if (checkAuthLoginData(extraData, data)) showAuthLogin({ loginData: data, extraData: extraData, domain }); - break; - } - - default: - console.log('this action is not supported'); - } - } catch (error) { - console.log(error); - } -} - export function checkAuthLoginData(extraData: any, data: any) { const { type, address, netWorkType, chainType } = data; diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index fd879c44cc..96a548d927 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -119,6 +119,14 @@ export const discoverSlice = createSlice({ [payload]: JSON.parse(JSON.stringify(initNetworkData)), }; }, + addAutoApproveItem: (state, { payload }: { payload: number }) => { + if (!state.autoApproveMap) state.autoApproveMap = {}; + state.autoApproveMap = { ...state.autoApproveMap, [payload]: true }; + }, + removeAutoApproveItem: (state, { payload }: { payload: number }) => { + if (!state.autoApproveMap) state.autoApproveMap = {}; + if (state.autoApproveMap[payload]) delete state.autoApproveMap[payload]; + }, }, }); @@ -135,6 +143,8 @@ export const { setActiveTab, updateTab, changeDrawerOpenStatus, + addAutoApproveItem, + removeAutoApproveItem, } = discoverSlice.actions; export default discoverSlice; diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index a146e27307..7bcd9e480a 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -20,4 +20,5 @@ export interface IDiscoverStateType { }; initializedList?: Set; activeTabId?: number; + autoApproveMap?: { [id: number]: true }; } From 230dc3d8bf4c3edd557a1cb0e2bc108b71b67855 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 13 Jul 2023 15:15:22 +0800 Subject: [PATCH 336/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20ArchivePag?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/discover.ts | 41 +++++- .../js/pages/Buy/BuyHome/index.tsx | 2 +- .../Bookmark/components/BookmarkItem.tsx | 15 +- .../Bookmark/components/BookmarksSection.tsx | 64 +++++++++ .../js/pages/Discover/Bookmark/index.tsx | 132 ++++++++++++------ .../js/pages/Discover/DiscoverHome/index.tsx | 2 + .../DiscoverArchivedSection/index.tsx | 126 +++++++++++++++++ .../mobile-app-did/js/pages/Discover/types.ts | 4 + packages/mobile-app-did/js/pages/My/index.tsx | 5 + packages/store/store-ca/discover/slice.ts | 1 + packages/store/store-ca/discover/type.ts | 8 ++ 11 files changed, 346 insertions(+), 54 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Discover/types.ts diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 14d478b393..22af024754 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -9,7 +9,7 @@ import { initNetworkDiscoverMap, } from '@portkey-wallet/store/store-ca/discover/slice'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -import { useCallback } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; export const useIsDrawerOpen = () => useAppCASelector(state => state.discover.isDrawerOpen); @@ -56,3 +56,42 @@ export const useDiscoverWhiteList = () => { return { checkIsInWhiteList, upDateWhiteList }; }; + +export const useBookmarkList = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + const { discoverMap } = useAppCASelector(state => state.discover); + + useEffect(() => { + // + }, []); + + const bookmarkList = useMemo(() => discoverMap?.[networkType]?.bookmarkList || [], [discoverMap, networkType]); + + return [ + { + id: 1, + name: 'portkey1', + url: 'https://portkey.finance/', + sortWeight: 1, + }, + { + id: 2, + name: 'portkey2', + url: 'https://portkey.finance/', + sortWeight: 2, + }, + { + id: 3, + name: 'portkey3', + url: 'https://portkey.finance/', + sortWeight: 3, + }, + { + id: 4, + name: 'portkey4', + url: 'https://portkey.finance/', + sortWeight: 4, + }, + ]; +}; diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx index e1b246af5b..fc11a07f94 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/index.tsx @@ -94,7 +94,7 @@ export default function BuyHome() { ))} - {tabList.find(item => item.type === selectTab)?.component} + {tabList.find(item => item.type === selectTab)?.component} ); } diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 41aa6ec74a..6a0b411bb0 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -2,7 +2,7 @@ import GStyles from 'assets/theme/GStyles'; import { TextM } from 'components/CommonText'; import Touchable from 'components/Touchable'; import React, { memo, useCallback, useEffect, useRef } from 'react'; -import { StyleSheet } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; import { useBookmark } from '../context'; @@ -37,9 +37,8 @@ export default memo( swipeEnabled={false} snapPointsLeft={[80]} renderUnderlayLeft={renderUnderlayLeft}> - open )} + {item} - + {/* + drag + */} + ); @@ -65,7 +68,7 @@ export default memo( const styles = StyleSheet.create({ marginContainer: { - marginHorizontal: 16, + // marginHorizontal: 16, }, underlayLeftBox: { flex: 1, diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx new file mode 100644 index 0000000000..f8e581e395 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -0,0 +1,64 @@ +import React, { useCallback, useMemo, useState } from 'react'; +import PageContainer from 'components/PageContainer'; +import DraggableFlatList from 'react-native-draggable-flatlist'; +import GStyles from 'assets/theme/GStyles'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { BookmarkProvider, setEdit, useBookmark } from '../context'; +import CommonButton from 'components/CommonButton'; +import BookmarkItem from './BookmarkItem'; +import { FontStyles } from 'assets/theme/styles'; +import { ArchivedTabEnum } from 'pages/Discover/types'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import fonts from 'assets/theme/fonts'; +import { TextM } from 'components/CommonText'; + +const mockData = ['1', '2', '3', '4']; + +function BookmarksSection() { + const [list, setList] = useState(mockData); + const [{ isEdit }, dispatch] = useBookmark(); + + const BottomBox = useMemo( + () => ( + + {isEdit ? ( + <> + + dispatch(setEdit(false))} title="Done" type="primary" /> + + ) : ( + dispatch(setEdit(true))} title="Edit" type="primary" /> + )} + + ), + [dispatch, isEdit], + ); + + return ( + + + _item} + renderItem={props => } + onDragEnd={({ data }) => setList(data)} + /> + + {BottomBox} + + ); +} + +export default function Container() { + return ( + + + + ); +} + +const styles = StyleSheet.create({ + // remove padding to scale item + containerStyles: { paddingHorizontal: pTd(20), flex: 1, justifyContent: 'space-between' }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx index 96a716112c..5ce94553d9 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx @@ -1,61 +1,101 @@ -import React, { useMemo, useState } from 'react'; +import React, { useCallback, useState } from 'react'; import PageContainer from 'components/PageContainer'; -import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { StyleSheet, View } from 'react-native'; -import { BookmarkProvider, setEdit, useBookmark } from './context'; -import CommonButton from 'components/CommonButton'; -import BookmarkItem from './components/BookmarkItem'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { FontStyles } from 'assets/theme/styles'; +import { ArchivedTabEnum } from 'pages/Discover/types'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import fonts from 'assets/theme/fonts'; +import { TextM } from 'components/CommonText'; +import BookmarksSection from './components/BookmarksSection'; -const mockData = ['1', '2', '3', '4']; +type TabItemType = { + name: string; + type: ArchivedTabEnum; + component: JSX.Element; +}; -function Bookmark() { - const [list, setList] = useState(mockData); - const [{ isEdit }, dispatch] = useBookmark(); +const tabList: TabItemType[] = [ + { + name: 'Bookmarks', + type: ArchivedTabEnum.Bookmarks, + component: , + }, + { + name: 'History', + type: ArchivedTabEnum.History, + component: <>, + }, +]; + +export default function Bookmark() { + const [selectTab, setSelectTab] = useState(ArchivedTabEnum.Bookmarks); + + const onTabPress = useCallback((type: ArchivedTabEnum) => { + setSelectTab(type); + }, []); - const BottomBox = useMemo( - () => ( - - {isEdit ? ( - <> - - dispatch(setEdit(false))} title="Done" type="primary" /> - - ) : ( - dispatch(setEdit(true))} title="Edit" type="primary" /> - )} - - ), - [dispatch, isEdit], - ); return ( - - - _item} - renderItem={props => } - onDragEnd={({ data }) => setList(data)} - /> + + + + {tabList.map(tabItem => ( + { + onTabPress(tabItem.type); + }}> + + + {tabItem.name} + + + + ))} + - {BottomBox} + {tabList.find(item => item.type === selectTab)?.component} ); } -export default function Container() { - return ( - - - - ); -} - const styles = StyleSheet.create({ - // remove padding to scale item - containerStyles: { ...GStyles.paddingArg(0) }, - paddingContainer: { - paddingHorizontal: 16, + containerStyles: { ...GStyles.paddingArg(0), flex: 1, backgroundColor: defaultColors.bg1 }, + + tabHeader: { + width: pTd(214), + backgroundColor: defaultColors.bg6, + borderRadius: pTd(6), + flexDirection: 'row', + justifyContent: 'space-between', + ...GStyles.paddingArg(3), + marginVertical: pTd(16), + }, + tabWrap: { + width: pTd(100), + height: pTd(30), + borderRadius: pTd(6), + alignItems: 'center', + justifyContent: 'center', + }, + selectTabStyle: { + shadowColor: defaultColors.shadow1, + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.09, + shadowRadius: 4, + elevation: 2, + backgroundColor: defaultColors.bg1, + }, + selectTabTextStyle: { + color: defaultColors.font5, + ...fonts.mediumFont, }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 76db5509ec..4229ab2903 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -8,6 +8,7 @@ import { defaultColors } from 'assets/theme'; import SafeAreaBox from 'components/SafeAreaBox'; import CustomHeader from 'components/CustomHeader'; import { BGStyles } from 'assets/theme/styles'; +import { DiscoverArchivedSection } from '../components/DiscoverArchivedSection'; export default function DiscoverHome() { return ( @@ -15,6 +16,7 @@ export default function DiscoverHome() { navigationService.navigate('DiscoverSearch')} /> + diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx new file mode 100644 index 0000000000..78c5863add --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -0,0 +1,126 @@ +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { FontStyles } from 'assets/theme/styles'; +import { TextM, TextS } from 'components/CommonText'; +import { useBookmarkList } from 'hooks/discover'; +import React, { useCallback, useMemo } from 'react'; +import { StyleSheet, View, Easing } from 'react-native'; +import { pTd } from 'utils/unit'; +import { TabView } from '@rneui/base'; +import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import navigationService from 'utils/navigationService'; +import { ArchivedTabEnum } from 'pages/Discover/types'; + +export function DiscoverArchivedSection() { + const bookmarkListStore = useBookmarkList(); + const [index, setIndex] = React.useState( + bookmarkListStore.length ? ArchivedTabEnum.Bookmarks : ArchivedTabEnum.History, + ); + + const bookmarkList = useMemo(() => bookmarkListStore.slice(0, 4), [bookmarkListStore]); + + const onSeeAllPress = useCallback(() => { + if (index === ArchivedTabEnum.Bookmarks) { + navigationService.navigate('Bookmark'); + return; + } + // navigationService.navigate('Bookmark'); + }, [index]); + + return ( + <> + {true && ( + + + + setIndex(ArchivedTabEnum.Bookmarks)} + style={[ + GStyles.marginRight(24), + index === ArchivedTabEnum.Bookmarks ? FontStyles.weight500 : FontStyles.font3, + ]}> + Bookmarks + + setIndex(ArchivedTabEnum.History)} + style={index === ArchivedTabEnum.History ? FontStyles.weight500 : FontStyles.font3}> + History + + + + See All + + + + + + + + {bookmarkList.map((item, idx) => ( + + + + + {item.url} + + + + ))} + + + + History + + + + + )} + + ); +} + +const styles = StyleSheet.create({ + wrap: { + height: pTd(144), + ...GStyles.paddingArg(0, 20), + }, + headerWrap: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: pTd(8), + }, + archivedTabWrap: { + flexDirection: 'row', + alignItems: 'baseline', + }, + tabViewWrap: { + width: '100%', + overflow: 'hidden', + flex: 1, + }, + tabListWrap: { + width: '100%', + height: '100%', + backgroundColor: defaultColors.bg1, + borderRadius: pTd(6), + paddingHorizontal: pTd(12), + flexDirection: 'row', + }, + tabItemWrap: { + width: '25%', + justifyContent: 'center', + alignItems: 'center', + }, + tabItemContent: { + alignItems: 'center', + maxWidth: pTd(72), + height: pTd(76), + justifyContent: 'space-between', + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/types.ts b/packages/mobile-app-did/js/pages/Discover/types.ts new file mode 100644 index 0000000000..26783ea5c4 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/types.ts @@ -0,0 +1,4 @@ +export enum ArchivedTabEnum { + Bookmarks = 0, + History = 1, +} diff --git a/packages/mobile-app-did/js/pages/My/index.tsx b/packages/mobile-app-did/js/pages/My/index.tsx index 420f628ec9..67e11d12f4 100644 --- a/packages/mobile-app-did/js/pages/My/index.tsx +++ b/packages/mobile-app-did/js/pages/My/index.tsx @@ -42,6 +42,11 @@ const MenuList: Array = [ label: 'Wallet Security', icon: 'wallet-security', }, + { + name: 'Bookmark', + label: 'Bookmark', + icon: 'wallet-security', + }, ]; export default function MyMenu() { diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index fd879c44cc..d339e4ada7 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -9,6 +9,7 @@ const initNetworkData: IDiscoverNetworkStateType = { recordsList: [], whiteList: [], tabs: [], + bookmarkList: [], }; const initialState: IDiscoverStateType = { diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index a146e27307..c5e66d84c3 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -7,10 +7,18 @@ export interface ITabItem { screenShotUrl?: string; } +export interface IBookmarkItem { + id: number; + name: string; + url: string; + sortWeight: number; +} + export interface IDiscoverNetworkStateType { recordsList: ITabItem[]; whiteList: string[]; tabs: ITabItem[]; + bookmarkList?: IBookmarkItem[]; } export interface IDiscoverStateType { From 0b21f174e1e2a2f680be55f0652e0ed4619e216a Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 13 Jul 2023 15:17:50 +0800 Subject: [PATCH 337/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20remove=20test=20?= =?UTF-8?q?code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/My/index.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/mobile-app-did/js/pages/My/index.tsx b/packages/mobile-app-did/js/pages/My/index.tsx index 67e11d12f4..420f628ec9 100644 --- a/packages/mobile-app-did/js/pages/My/index.tsx +++ b/packages/mobile-app-did/js/pages/My/index.tsx @@ -42,11 +42,6 @@ const MenuList: Array = [ label: 'Wallet Security', icon: 'wallet-security', }, - { - name: 'Bookmark', - label: 'Bookmark', - icon: 'wallet-security', - }, ]; export default function MyMenu() { From 451a7fd3768af70ee10cb2865e1b2041b7bf845a Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 15:31:45 +0800 Subject: [PATCH 338/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20memo=20wrap=20com?= =?UTF-8?q?ponents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/components/TabsDrawer/index.tsx | 8 +++++--- .../js/components/TextWithProtocolIcon/index.tsx | 10 ++++++---- packages/mobile-app-did/js/hooks/useScanLink.ts | 0 3 files changed, 11 insertions(+), 7 deletions(-) delete mode 100644 packages/mobile-app-did/js/hooks/useScanLink.ts diff --git a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx index d4953cfe05..322e32c617 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/index.tsx @@ -1,6 +1,6 @@ import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { ScreenWidth } from '@rneui/base'; -import React from 'react'; +import React, { memo } from 'react'; import { Drawer } from 'react-native-drawer-layout'; import TabsDrawerContent from './TabsDrawerContent'; import { usePin } from 'hooks/store'; @@ -8,7 +8,7 @@ type TabsDrawerPropsType = { children: React.ReactNode; }; -export default function TabsDrawer(props: TabsDrawerPropsType) { +const TabsDrawer = (props: TabsDrawerPropsType) => { const { children } = props; const pin = usePin(); @@ -34,4 +34,6 @@ export default function TabsDrawer(props: TabsDrawerPropsType) { {children} ); -} +}; + +export default memo(TabsDrawer); diff --git a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx index 38ab564cd1..7904db9273 100644 --- a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx +++ b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx @@ -3,7 +3,7 @@ import { defaultColors } from 'assets/theme'; import { TextM } from 'components/CommonText'; import Svg from 'components/Svg'; -import React, { useMemo } from 'react'; +import React, { memo, useMemo } from 'react'; import { StyleSheet, View, ViewProps, StyleProp } from 'react-native'; import { pTd } from 'utils/unit'; @@ -17,7 +17,7 @@ interface ITextWithProtocolIconProps { location?: 'header' | 'other'; } -export default function TextWithProtocolIcon({ +const TextWithProtocolIcon = ({ title = '', url, textFontSize = pTd(14), @@ -25,7 +25,7 @@ export default function TextWithProtocolIcon({ iconSize = pTd(14), type = 'iconRight', location = 'other', -}: ITextWithProtocolIconProps) { +}: ITextWithProtocolIconProps) => { const isDanger = isDangerousLink(url); const textStyleObj: any = { @@ -61,7 +61,9 @@ export default function TextWithProtocolIcon({ {type === 'iconRight' && ProtocolIcon} ); -} +}; + +export default memo(TextWithProtocolIcon); const styles = StyleSheet.create({ wrap: { diff --git a/packages/mobile-app-did/js/hooks/useScanLink.ts b/packages/mobile-app-did/js/hooks/useScanLink.ts deleted file mode 100644 index e69de29bb2..0000000000 From dfb95fa0a374008bf63113e842840547985829c5 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 15:33:01 +0800 Subject: [PATCH 339/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20close=20all=20dis?= =?UTF-8?q?able?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/TabsDrawerContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 5e789ee3ee..d879232b14 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -290,7 +290,7 @@ const handleButtonStyle = StyleSheet.create({ textAlign: 'right', }, noTap: { - color: 'red', + opacity: 0.3, }, }); From b948417b7a8090d53bf20da96ba3c9ae953c6f6e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 15:34:21 +0800 Subject: [PATCH 340/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20qr=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/DiscoverHome/index.tsx | 1 + .../js/pages/QrScanner/index.tsx | 34 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 172b34704a..25a69fdedb 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -45,6 +45,7 @@ export default function DiscoverHome() { style={styles.svgWrap} onPress={async () => { if (!(await requestQrPermission())) return showDialog(); + navigationService.navigate('QrScanner'); }}> diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 4523eb0965..844872bdb5 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -18,14 +18,18 @@ import { isIos, screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/d import { Camera } from 'expo-camera'; import { expandQrData } from '@portkey-wallet/utils/qrCode'; +import { checkIsUrl } from '@portkey-wallet/utils/dapp/browser'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; + interface QrScannerProps { route?: any; - type?: 'login' | 'send'; } const QrScanner: React.FC = () => { const { t } = useLanguage(); const { currentNetwork } = useWallet(); + const jumpToWebview = useDiscoverJumpWithNetWork(); + const navigation = useNavigation(); const routesArr: RouteInfoType[] = navigation.getState().routes; const previousRouteInfo = routesArr[routesArr.length - 2]; @@ -40,21 +44,31 @@ const QrScanner: React.FC = () => { const handleBarCodeScanned = useCallback( ({ data = '' }) => { + if (typeof data !== 'string') return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); try { - if (typeof data === 'string') { - const qrCodeData = expandQrData(JSON.parse(data)); - - // if not currentNetwork - if (currentNetwork !== qrCodeData.netWorkType) return invalidQRCode(InvalidQRCodeText.DIFFERENT_NETWORK); - - handleQRCodeData(qrCodeData, previousRouteInfo, setRefresh); + // if is link + const str = data.replace(/("|')/g, ''); + if (checkIsUrl(str)) { + jumpToWebview({ + item: { + id: Date.now(), + name: str, + url: str, + }, + }); + return navigationService.goBack(); } + + const qrCodeData = expandQrData(JSON.parse(data)); + // if not currentNetwork + if (currentNetwork !== qrCodeData.netWorkType) return invalidQRCode(InvalidQRCodeText.DIFFERENT_NETWORK); + handleQRCodeData(qrCodeData, previousRouteInfo, setRefresh); } catch (error) { console.log(error); return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); } }, - [currentNetwork, previousRouteInfo], + [currentNetwork, jumpToWebview, previousRouteInfo], ); const selectImage = async () => { @@ -104,8 +118,6 @@ const QrScanner: React.FC = () => { export default QrScanner; -defaultColors; - export const PageStyle = StyleSheet.create({ wrapper: { width: '100%', From 9509f32b162e024392456a46a36698407b01e081 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 15:34:48 +0800 Subject: [PATCH 341/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Token/SearchTokenList/index.tsx | 62 ++++++++++++------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx index e6827e64ab..246cffe13c 100644 --- a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx @@ -4,7 +4,7 @@ import CommonInput from 'components/CommonInput'; import { StyleSheet, TouchableOpacity, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import CommonToast from 'components/CommonToast'; import { useLanguage } from 'i18n/hooks'; import useDebounce from 'hooks/useDebounce'; @@ -17,6 +17,8 @@ import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; import FilterTokenSection from '../components/FilterToken'; +import Lottie from 'lottie-react-native'; +import { handleErrorMessage } from '@portkey-wallet/utils'; interface ManageTokenListProps { route?: any; @@ -32,6 +34,7 @@ const SearchTokenList: React.FC = () => { const caAddressArray = useCaAddresses(); const caAddressInfos = useCaAddressInfoList(); + const [isSearching, setIsSearching] = useState(false); const [keyword, setKeyword] = useState(''); const [filterTokenList, setFilterTokenList] = useState([]); @@ -40,7 +43,8 @@ const SearchTokenList: React.FC = () => { const fetchSearchedTokenList = useCallback(async () => { try { if (!debounceWord) return; - Loading.showOnce(); + setIsSearching(true); + const list = await request.token.fetchTokenListBySearch({ params: { symbol: debounceWord, @@ -54,10 +58,11 @@ const SearchTokenList: React.FC = () => { userTokenId: item.id, })); setFilterTokenList(tmpToken); - Loading.hide(); + setIsSearching(false); } catch (error) { console.log('filter search error', error); Loading.hide(); + setIsSearching(false); } }, [chainIdList, debounceWord]); @@ -79,14 +84,40 @@ const SearchTokenList: React.FC = () => { CommonToast.success('Success'); }, 800); } catch (err) { - console.log(err); Loading.hide(); - CommonToast.fail('Fail'); + setIsSearching(false); + CommonToast.fail(handleErrorMessage(err)); } }, [caAddressArray, caAddressInfos, dispatch, fetchSearchedTokenList], ); + const IptRightIcon = useMemo(() => { + if (isSearching) + return ( + + ); + + return keyword ? ( + setKeyword('')}> + + + ) : undefined; + }, [isSearching, keyword]); + + const HeaderRightIcon = useMemo( + () => ( + { + navigationService.navigate('CustomToken'); + }}> + + + ), + [], + ); + useEffect(() => { setFilterTokenList([]); fetchSearchedTokenList(); @@ -107,15 +138,7 @@ const SearchTokenList: React.FC = () => { { - navigationService.navigate('CustomToken'); - }}> - - - } + rightDom={HeaderRightIcon} containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> @@ -126,13 +149,7 @@ const SearchTokenList: React.FC = () => { onChangeText={v => { setKeyword(v.trim()); }} - rightIcon={ - keyword ? ( - setKeyword('')}> - - - ) : undefined - } + rightIcon={IptRightIcon} /> @@ -154,4 +171,7 @@ export const pageStyles = StyleSheet.create({ list: { flex: 1, }, + loadingIcon: { + width: pTd(20), + }, }); From a3aa897bcac5d92d06b76c565d64357c4ef65609 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 15:35:08 +0800 Subject: [PATCH 342/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20remove=20records?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/discover/slice.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index fd879c44cc..fac095ffb0 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -62,6 +62,17 @@ export const discoverSlice = createSlice({ return item.url === payload.url ? { ...item, ...payload } : item; }); }, + removeRecordsItems: (state, { payload }: { payload: { ids: number[]; networkType: NetworkType } }) => { + const { networkType, ids = [] } = payload; + const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + + const idsObj: { [key: number]: number } = {}; + ids.forEach(id => { + idsObj[id] = id; + }); + + targetNetworkDiscover.recordsList = targetNetworkDiscover.recordsList.filter(ele => !idsObj?.[ele?.id]); + }, clearRecordsList: (state, { payload }: { payload: { networkType: NetworkType } }) => { const { networkType } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); @@ -127,6 +138,7 @@ export const { addUrlToWhiteList, addRecordsItem, upDateRecordsItem, + removeRecordsItems, clearRecordsList, resetDiscover, closeAllTabs, From c07d0248e00760265c15ec998d67c3ef6cf99270 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 15:35:35 +0800 Subject: [PATCH 343/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20token=20er?= =?UTF-8?q?ror?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Token/ManageTokenList/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index 08d7e03788..1482cbacc3 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -19,6 +19,7 @@ import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; import { useFocusEffect } from '@react-navigation/native'; import SimulatedInputBox from 'components/SimulatedInputBox'; +import { handleErrorMessage } from '@portkey-wallet/utils'; interface ManageTokenListProps { route?: any; @@ -54,9 +55,8 @@ const ManageTokenList: React.FC = () => { CommonToast.success('Success'); }, 800); } catch (err) { - console.log(err); Loading.hide(); - CommonToast.fail('Fail'); + CommonToast.fail(handleErrorMessage(err)); } }, [caAddressArray, caAddressInfos, chainIdList, dispatch], From 392bbe272c415815bb41bb6e9b6ed9b6755f7d2c Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 13 Jul 2023 17:23:18 +0800 Subject: [PATCH 344/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20fetch=20txFee=20?= =?UTF-8?q?&=20adjust=20devices=20logic=20&=20add=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/index.ts | 2 + packages/api/api-did/txFee/index.ts | 6 +++ packages/constants/constants-ca/wallet.ts | 6 +++ packages/hooks/hooks-ca/useTxFee.ts | 42 +++++++++++++++++++ packages/store/store-ca/txFee/actions.ts | 19 +++++++++ packages/store/store-ca/txFee/slice.ts | 32 ++++++++++++++ packages/store/store-ca/txFee/type.ts | 14 +++++++ packages/types/types-ca/store.ts | 3 ++ .../app/web/hooks/useLogout.ts | 2 + .../web-extension-did/app/web/manifest.json | 2 +- .../app/web/pages/Buy/index.tsx | 8 ++-- .../hooks/useRemoveOtherManage.ts | 6 ++- .../app/web/pages/ScreenOpening/index.less | 11 ++++- .../app/web/pages/ScreenOpening/index.tsx | 2 + .../components/AmountInput/TokenInput.tsx | 9 ++-- .../Send/components/SendPreview/index.tsx | 15 +++---- .../app/web/pages/Send/index.tsx | 7 ++-- .../ManageDevices/Devices/index.tsx | 36 ++++++++-------- .../app/web/store/Provider/Updater.tsx | 3 +- .../app/web/store/Provider/config.ts | 7 ++++ .../app/web/store/Provider/rootReducer.ts | 4 ++ .../app/web/store/utils/getStore.ts | 9 ++++ .../utils/sandboxUtil/crossChainTransfer.ts | 8 ++-- packages/web-extension-did/package.json | 2 +- 24 files changed, 210 insertions(+), 45 deletions(-) create mode 100644 packages/api/api-did/txFee/index.ts create mode 100644 packages/hooks/hooks-ca/useTxFee.ts create mode 100644 packages/store/store-ca/txFee/actions.ts create mode 100644 packages/store/store-ca/txFee/slice.ts create mode 100644 packages/store/store-ca/txFee/type.ts diff --git a/packages/api/api-did/index.ts b/packages/api/api-did/index.ts index f2625750cf..1954308b87 100644 --- a/packages/api/api-did/index.ts +++ b/packages/api/api-did/index.ts @@ -9,6 +9,7 @@ import paymentApi from './payment'; import deviceApi from './device'; import messageApi from './message'; import switchApi from './switch'; +import txFeeApi from './txFee'; import esApi from './es'; import myServer, { DidService } from './server'; @@ -48,6 +49,7 @@ export const EXPAND_APIS = { device: deviceApi, message: messageApi, switch: switchApi, + txFee: txFeeApi, }; export type BASE_REQ_TYPES = { diff --git a/packages/api/api-did/txFee/index.ts b/packages/api/api-did/txFee/index.ts new file mode 100644 index 0000000000..06f5e68a58 --- /dev/null +++ b/packages/api/api-did/txFee/index.ts @@ -0,0 +1,6 @@ +export default { + fetchTxFee: { + target: '/api/app/account/transactionFee', + config: { method: 'GET' }, + }, +} as const; diff --git a/packages/constants/constants-ca/wallet.ts b/packages/constants/constants-ca/wallet.ts index 43990bfc0f..41b26805a4 100644 --- a/packages/constants/constants-ca/wallet.ts +++ b/packages/constants/constants-ca/wallet.ts @@ -4,3 +4,9 @@ export const DEFAULT_FEE = '0.39'; export const CreateAddressLoading = 'Creating address on the chain...'; export const InitLoginLoading = 'Initiating social recovery'; + +export const InitialTxFee = { + ach: 0.39, + crossChain: 0.35, + max: 0.39, +}; diff --git a/packages/hooks/hooks-ca/useTxFee.ts b/packages/hooks/hooks-ca/useTxFee.ts new file mode 100644 index 0000000000..8a9dbaf517 --- /dev/null +++ b/packages/hooks/hooks-ca/useTxFee.ts @@ -0,0 +1,42 @@ +import { useAppCASelector } from './index'; +import { useCallback, useEffect, useMemo } from 'react'; +import { useAppCommonDispatch } from '../index'; +import { useCurrentNetwork } from './network'; +import { fetchTxFeeAsync } from '@portkey-wallet/store/store-ca/txFee/actions'; +import { ChainId } from '@portkey-wallet/types'; +import { TxFeeItem } from '@portkey-wallet/store/store-ca/txFee/type'; +import { InitialTxFee } from '@portkey-wallet/constants/constants-ca/wallet'; + +export const useFetchTxFee = () => { + const txFee = useAppCASelector(state => state.txFee); + const { chainInfo } = useAppCASelector(state => state.wallet); + const dispatch = useAppCommonDispatch(); + const currentNetwork = useCurrentNetwork(); + const chainIds = useMemo(() => chainInfo?.[currentNetwork]?.map(chain => chain.chainId), [chainInfo, currentNetwork]); + + useEffect(() => { + if (chainIds?.length) { + dispatch(fetchTxFeeAsync(chainIds)); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [chainIds]); + + return useCallback( + (chainId: ChainId) => { + const targetTxFee = txFee[currentNetwork]?.filter((txf: TxFeeItem) => txf.chainId === chainId); + return targetTxFee?.[0].transactionFee ?? InitialTxFee; + }, + [currentNetwork, txFee], + ); +}; + +export const useGetTxFee = (chainId: ChainId) => { + const txFee = useAppCASelector(state => state.txFee); + const currentNetwork = useCurrentNetwork(); + const targetTxFee = useMemo( + () => txFee[currentNetwork]?.filter((txf: TxFeeItem) => txf.chainId === chainId), + [chainId, currentNetwork, txFee], + ); + + return useMemo(() => targetTxFee?.[0].transactionFee ?? InitialTxFee, [targetTxFee]); +}; diff --git a/packages/store/store-ca/txFee/actions.ts b/packages/store/store-ca/txFee/actions.ts new file mode 100644 index 0000000000..f230e82c43 --- /dev/null +++ b/packages/store/store-ca/txFee/actions.ts @@ -0,0 +1,19 @@ +import { request } from '@portkey-wallet/api/api-did'; +import { ChainId, NetworkType } from '@portkey-wallet/types'; +import { createAsyncThunk, createAction } from '@reduxjs/toolkit'; +import { WalletState } from '../wallet/type'; + +export const fetchTxFeeAsync = createAsyncThunk('fetchTxFeeAsync', async (chainIds: ChainId[], { getState }) => { + const result = await request.txFee.fetchTxFee({ + params: { chainIds }, + }); + const { + wallet: { currentNetwork }, + } = getState() as { wallet: WalletState }; + return { + currentNetwork, + fee: result, + }; +}); + +export const resetTxFee = createAction('resetTxFee'); diff --git a/packages/store/store-ca/txFee/slice.ts b/packages/store/store-ca/txFee/slice.ts new file mode 100644 index 0000000000..16102cced0 --- /dev/null +++ b/packages/store/store-ca/txFee/slice.ts @@ -0,0 +1,32 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { fetchTxFeeAsync, resetTxFee } from './actions'; +import { TxFeeType } from './type'; + +const initialState: TxFeeType = {}; + +export const txFeeSlice = createSlice({ + name: 'txFee', + initialState, + reducers: {}, + extraReducers: builder => { + builder + .addCase(fetchTxFeeAsync.fulfilled, (state, action) => { + const { currentNetwork, fee } = action.payload; + state[currentNetwork] = fee; + }) + .addCase(fetchTxFeeAsync.rejected, () => { + console.log('get txFee error'); + }) + .addCase(resetTxFee, (state, action) => { + if (action.payload) { + const txFeeValue = Object.assign({}, state); + if (txFeeValue?.[action.payload]) delete txFeeValue[action.payload]; + state = txFeeValue; + } else { + state = {}; + } + }); + }, +}); + +export default txFeeSlice; diff --git a/packages/store/store-ca/txFee/type.ts b/packages/store/store-ca/txFee/type.ts new file mode 100644 index 0000000000..b0b7490e9a --- /dev/null +++ b/packages/store/store-ca/txFee/type.ts @@ -0,0 +1,14 @@ +import { ChainId, NetworkType } from '@portkey-wallet/types'; + +export type TxFeeItem = { + chainId: ChainId; + transactionFee: { + ach: number; + crossChain: number; + max: number; + }; +}; + +export type TxFeeType = { + [key in NetworkType]?: TxFeeItem[]; +}; diff --git a/packages/types/types-ca/store.ts b/packages/types/types-ca/store.ts index d91940451a..73742c1b7a 100644 --- a/packages/types/types-ca/store.ts +++ b/packages/types/types-ca/store.ts @@ -25,6 +25,8 @@ import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; import { IDappStoreState } from '@portkey-wallet/store/store-ca/dapp/type'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { CMSState } from '@portkey-wallet/store/store-ca/cms/types'; +import txFeeSlice from '@portkey-wallet/store/store-ca/txFee/slice'; +import { TxFeeType } from '@portkey-wallet/store/store-ca/txFee/type'; export type CACommonState = RootCommonState & { [tokenManagementSlice.name]: TokenState; @@ -41,4 +43,5 @@ export type CACommonState = RootCommonState & { [miscSlice.name]: MiscState; [dappSlice.name]: IDappStoreState; [cmsSlice.name]: CMSState; + [txFeeSlice.name]: TxFeeType; }; diff --git a/packages/web-extension-did/app/web/hooks/useLogout.ts b/packages/web-extension-did/app/web/hooks/useLogout.ts index e3fd899980..54912496c8 100644 --- a/packages/web-extension-did/app/web/hooks/useLogout.ts +++ b/packages/web-extension-did/app/web/hooks/useLogout.ts @@ -25,6 +25,7 @@ import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import { useNavigate } from 'react-router'; import { getWalletInfo, isCurrentCaHash } from 'store/utils/getStore'; import { resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { resetTxFee } from '@portkey-wallet/store/store-ca/txFee/actions'; export default function useLogOut() { const dispatch = useAppDispatch(); @@ -47,6 +48,7 @@ export default function useLogOut() { dispatch(resetLoginInfoAction()); } dispatch(resetDappList(currentNetwork)); + dispatch(resetTxFee(currentNetwork)); if (!isPrompt) { InternalMessage.payload(PortkeyMessageTypes.LOGIN_WALLET).send(); diff --git a/packages/web-extension-did/app/web/manifest.json b/packages/web-extension-did/app/web/manifest.json index 46e22ec2f7..f551923ff5 100644 --- a/packages/web-extension-did/app/web/manifest.json +++ b/packages/web-extension-did/app/web/manifest.json @@ -4,7 +4,7 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "name": "Portkey: DID & Crypto & NFT", - "version": "1.3.2", + "version": "1.3.4", "description": "Identity System for Social Recover and Asset Management Tool", "icons": { "16": "assets/images/extension_logo.png", diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index f0766e7835..7dbe5b29ac 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -28,7 +28,7 @@ import { getBalance } from 'utils/sandboxUtil/getBalance'; import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; +import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import BuyForm from './components/BuyForm'; import SellForm from './components/SellForm'; import { useEffectOnce } from 'react-use'; @@ -65,6 +65,7 @@ export default function Buy() { const [rateUpdateTime, setRateUpdateTime] = useState(MAX_UPDATE_TIME); const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); const checkManagerSyncState = useCheckManagerSyncState(); + const getTxFeeByChainId = useFetchTxFee(); const disabled = useMemo(() => !!errMsg || !amount, [errMsg, amount]); const showRateText = useMemo( @@ -372,6 +373,7 @@ export default function Buy() { }, [stopInterval]); const handleNext = useCallback(async () => { + const { ach: achFee } = getTxFeeByChainId('AELF'); const { side } = valueSaveRef.current; setLoading(true); const result = await refreshBuyButton(); @@ -410,9 +412,7 @@ export default function Buy() { setLoading(false); const balance = result.result.balance; - if ( - ZERO.plus(divDecimals(balance, 8)).isLessThanOrEqualTo(ZERO.plus(DEFAULT_FEE).plus(valueSaveRef.current.amount)) - ) { + if (ZERO.plus(divDecimals(balance, 8)).isLessThanOrEqualTo(ZERO.plus(achFee).plus(valueSaveRef.current.amount))) { setInsufficientFundsMsg(); return; } diff --git a/packages/web-extension-did/app/web/pages/GuardianApproval/hooks/useRemoveOtherManage.ts b/packages/web-extension-did/app/web/pages/GuardianApproval/hooks/useRemoveOtherManage.ts index 03b8aa6859..a6c90d507f 100644 --- a/packages/web-extension-did/app/web/pages/GuardianApproval/hooks/useRemoveOtherManage.ts +++ b/packages/web-extension-did/app/web/pages/GuardianApproval/hooks/useRemoveOtherManage.ts @@ -12,6 +12,7 @@ import { contractErrorHandler } from 'utils/tryErrorHandler'; import { formatGuardianValue } from '../utils/formatGuardianValue'; import qs from 'query-string'; import ModalTip from 'pages/components/ModalTip'; +import { sleep } from '@portkey-wallet/utils'; export const useRemoveOtherManage = () => { const { setLoading } = useLoading(); @@ -59,8 +60,9 @@ export const useRemoveOtherManage = () => { setLoading(false); ModalTip({ content: 'Requested successfully', - onClose: () => { - navigate('/setting/wallet-security/manage-devices', { state: 'update' }); + onClose: async () => { + await sleep(1000); + navigate('/setting/wallet-security/manage-devices'); }, }); } catch (error: any) { diff --git a/packages/web-extension-did/app/web/pages/ScreenOpening/index.less b/packages/web-extension-did/app/web/pages/ScreenOpening/index.less index 72d0d063c0..828b64a105 100644 --- a/packages/web-extension-did/app/web/pages/ScreenOpening/index.less +++ b/packages/web-extension-did/app/web/pages/ScreenOpening/index.less @@ -1,9 +1,10 @@ @import '../../assets/theme/color.less'; .open-page-wrapper { - height: 100%; + min-height: 600px; background: url('./bg.svg') no-repeat; background-size: cover; display: flex; + flex-direction: column; align-items: center; justify-content: center; .open-page-content { @@ -35,4 +36,12 @@ font-size: 16px; } } + .version { + position: absolute; + bottom: 48px; + font-size: 14px; + line-height: 20px; + font-weight: 400; + color: @font-5; + } } diff --git a/packages/web-extension-did/app/web/pages/ScreenOpening/index.tsx b/packages/web-extension-did/app/web/pages/ScreenOpening/index.tsx index b0dba665d0..4cc62f50ab 100644 --- a/packages/web-extension-did/app/web/pages/ScreenOpening/index.tsx +++ b/packages/web-extension-did/app/web/pages/ScreenOpening/index.tsx @@ -5,6 +5,7 @@ import './index.less'; export default function ScreenOpeningPage() { const navigate = useNavigate(); + const version = process.env.SDK_VERSION?.replace('v', ''); // return navigate('/register/start')} />; return (
    @@ -14,6 +15,7 @@ export default function ScreenOpeningPage() {

    Your key to play and earn in Web 3

    +
    {`v ${version}`}
    ); } diff --git a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx index e5a235f540..d2ef49e852 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/AmountInput/TokenInput.tsx @@ -10,9 +10,9 @@ import { getBalance } from 'utils/sandboxUtil/getBalance'; import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { ChainId } from '@portkey-wallet/types'; import { useCurrentNetworkInfo, useIsTestnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import { useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import CustomSvg from 'components/CustomSvg'; -import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import { useCheckManagerSyncState } from 'hooks/wallet'; @@ -45,6 +45,7 @@ export default function TokenInput({ const amountInUsdShow = useAmountInUsdShow(); const checkManagerSyncState = useCheckManagerSyncState(); const [isManagerSynced, setIsManagerSynced] = useState(true); + const { max: maxFee } = useGetTxFee(token.chainId); const amountInUsd = useMemo( () => amountInUsdShow(value || amount, 0, token.symbol), @@ -78,7 +79,7 @@ export default function TokenInput({ return; } if (token.symbol === 'ELF') { - if (ZERO.plus(divDecimals(balance, token.decimals)).isLessThanOrEqualTo(ZERO.plus(DEFAULT_FEE))) { + if (ZERO.plus(divDecimals(balance, token.decimals)).isLessThanOrEqualTo(ZERO.plus(maxFee))) { setMaxAmount(divDecimals(balance, token.decimals).toString()); return; } @@ -89,12 +90,12 @@ export default function TokenInput({ if (fee) { setMaxAmount(divDecimals(balance, token.decimals).toString()); } else { - setMaxAmount(ZERO.plus(divDecimals(balance, token.decimals)).minus(DEFAULT_FEE).toString()); + setMaxAmount(ZERO.plus(divDecimals(balance, token.decimals)).minus(maxFee).toString()); } } else { setMaxAmount(divDecimals(balance, token.decimals).toString()); } - }, [balance, checkManagerSyncState, getTranslationInfo, token.chainId, token.decimals, token.symbol]); + }, [balance, checkManagerSyncState, getTranslationInfo, maxFee, token.chainId, token.decimals, token.symbol]); useEffect(() => { getTokenBalance(); diff --git a/packages/web-extension-did/app/web/pages/Send/components/SendPreview/index.tsx b/packages/web-extension-did/app/web/pages/Send/components/SendPreview/index.tsx index 7ec436c861..0943bd0636 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/SendPreview/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/SendPreview/index.tsx @@ -5,7 +5,7 @@ import { useWalletInfo } from 'store/Provider/hooks'; // import { useTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import './index.less'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { CROSS_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; +import { useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import { formatAmountShow } from '@portkey-wallet/utils/converter'; import { getEntireDIDAelfAddress, isAelfAddress } from '@portkey-wallet/utils/aelf'; import { ChainId } from '@portkey-wallet/types'; @@ -41,6 +41,7 @@ export default function SendPreview({ const isTestNet = useIsTestnet(); const amountInUsdShow = useAmountInUsdShow(); useFreshTokenPrice(); + const { crossChain: crossChainFee } = useGetTxFee(chainId); const toChain = useMemo(() => { const arr = toAccount.address.split('_'); @@ -54,7 +55,7 @@ export default function SendPreview({ [chainId, wallet], ); const renderEstimateAmount = useMemo(() => { - if (ZERO.plus(amount).isLessThanOrEqualTo(ZERO.plus(CROSS_FEE))) { + if (ZERO.plus(amount).isLessThanOrEqualTo(ZERO.plus(crossChainFee))) { return ( <> {!isTestNet && '$0'}0 @@ -64,13 +65,13 @@ export default function SendPreview({ return ( <> - {!isTestNet && amountInUsdShow(ZERO.plus(amount).minus(ZERO.plus(CROSS_FEE)).toString(), 0, symbol)} + {!isTestNet && amountInUsdShow(ZERO.plus(amount).minus(ZERO.plus(crossChainFee)).toString(), 0, symbol)} - {formatAmountShow(ZERO.plus(amount).minus(ZERO.plus(CROSS_FEE)))} + {formatAmountShow(ZERO.plus(amount).minus(ZERO.plus(crossChainFee)))} ); } - }, [amount, amountInUsdShow, isTestNet, symbol]); + }, [amount, amountInUsdShow, crossChainFee, isTestNet, symbol]); return (
    @@ -138,8 +139,8 @@ export default function SendPreview({ Cross-chain Transaction fee

    - {!isTestNet && amountInUsdShow(CROSS_FEE, 0, symbol)} - {` ${formatAmountShow(CROSS_FEE)} ELF`} + {!isTestNet && amountInUsdShow(crossChainFee, 0, symbol)} + {` ${formatAmountShow(crossChainFee)} ELF`}

    diff --git a/packages/web-extension-did/app/web/pages/Send/index.tsx b/packages/web-extension-did/app/web/pages/Send/index.tsx index ce940ad6af..c21fd092bb 100644 --- a/packages/web-extension-did/app/web/pages/Send/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/index.tsx @@ -27,7 +27,7 @@ import { ZERO } from '@portkey-wallet/constants/misc'; import { TransactionError } from '@portkey-wallet/constants/constants-ca/assets'; import { the2ThFailedActivityItemType } from '@portkey-wallet/types/types-ca/activity'; import { contractErrorHandler } from 'utils/tryErrorHandler'; -import { CROSS_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; +import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import PromptFrame from 'pages/components/PromptFrame'; import clsx from 'clsx'; import { AddressCheckError } from '@portkey-wallet/store/store-ca/assets/type'; @@ -72,7 +72,7 @@ export default function Send() { const checkManagerSyncState = useCheckManagerSyncState(); const [txFee, setTxFee] = useState(); const currentChain = useCurrentChain(state.chainId); - + const getTxFeeByChainId = useFetchTxFee(); const tokenInfo = useMemo( () => ({ chainId: state.chainId, @@ -195,6 +195,7 @@ export default function Send() { const handleCheckPreview = useCallback(async () => { try { setLoading(true); + const { crossChain: crossChainFee } = getTxFeeByChainId(state.chainId); if (!ZERO.plus(amount).toNumber()) return 'Please input amount'; const _isManagerSynced = await checkManagerSyncState(state.chainId); if (!_isManagerSynced) { @@ -205,7 +206,7 @@ export default function Send() { return TransactionError.TOKEN_NOT_ENOUGH; } if (isCrossChain(toAccount.address, chainInfo?.chainId ?? 'AELF') && symbol === 'ELF') { - if (ZERO.plus(CROSS_FEE).isGreaterThanOrEqualTo(ZERO.plus(amount))) { + if (ZERO.plus(crossChainFee).isGreaterThanOrEqualTo(ZERO.plus(amount))) { return TransactionError.CROSS_NOT_ENOUGH; } } diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx index 0f73493852..0ac4bdbc1f 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx @@ -1,9 +1,8 @@ import { useTranslation } from 'react-i18next'; -import { useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { useCallback, useEffect } from 'react'; +import { IDeviceList, useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { useCallback, useEffect, useState } from 'react'; import { DeviceItemType } from '@portkey-wallet/types/types-ca/device'; -import { useLocation, useNavigate } from 'react-router'; -import { sleep } from '@portkey-wallet/utils'; +import { useNavigate } from 'react-router'; import { useLoading } from 'store/Provider/hooks'; import DevicesPopup from './Popup'; import DevicesPrompt from './Prompt'; @@ -12,23 +11,24 @@ import { useCommonState } from 'store/Provider/hooks'; export default function Devices() { const { t } = useTranslation(); const navigate = useNavigate(); - const { state } = useLocation(); - const { deviceList, refetch } = useDeviceList(); + const { deviceList, refetch, loading } = useDeviceList(); + const [devices, setDevices] = useState([]); const { setLoading } = useLoading(); const { isNotLessThan768 } = useCommonState(); - const handleRefresh = useCallback(async () => { - setLoading(true); - await sleep(2000); - setLoading(false); - refetch(); - }, [refetch, setLoading]); - useEffect(() => { - if (state === 'update') { - handleRefresh(); + if (!loading) { + setDevices(deviceList); + setLoading(false); } - }, [handleRefresh, refetch, state]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loading]); + + useEffect(() => { + setLoading(true); + refetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const handleClick = useCallback( (item: DeviceItemType) => { @@ -43,8 +43,8 @@ export default function Devices() { }, [navigate]); return isNotLessThan768 ? ( - + ) : ( - + ); } diff --git a/packages/web-extension-did/app/web/store/Provider/Updater.tsx b/packages/web-extension-did/app/web/store/Provider/Updater.tsx index ece8200471..988d70b03d 100644 --- a/packages/web-extension-did/app/web/store/Provider/Updater.tsx +++ b/packages/web-extension-did/app/web/store/Provider/Updater.tsx @@ -15,6 +15,7 @@ import { useCheckManagerOnLogout } from 'hooks/useLogout'; import { useCheckManager } from '@portkey-wallet/hooks/hooks-ca/graphql'; import { useCheckUpdate } from 'hooks/useCheckUpdate'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; +import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import { useLocation } from 'react-router'; import { useBuyButton, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; @@ -39,7 +40,7 @@ export default function Updater() { const apiUrl = useCurrentApiUrl(); useCheckManager(checkManagerOnLogout); - + useFetchTxFee(); useEffect(() => { checkUpdate(); }, [checkUpdate]); diff --git a/packages/web-extension-did/app/web/store/Provider/config.ts b/packages/web-extension-did/app/web/store/Provider/config.ts index e1a0971820..021d869ebf 100644 --- a/packages/web-extension-did/app/web/store/Provider/config.ts +++ b/packages/web-extension-did/app/web/store/Provider/config.ts @@ -20,6 +20,7 @@ import recentSlice from '@portkey-wallet/store/store-ca/recent/slice'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; +import { txFeeSlice } from '@portkey-wallet/store/store-ca/txFee/slice'; interface ThunkOptions { extraArgument: E; @@ -65,6 +66,11 @@ export const dappPersistConfig = { storage: localStorage, }; +export const txFeePersistConfig = { + key: txFeeSlice.name, + storage: localStorage, +}; + export const loginPersistConfig = { key: loginSlice.name, storage: localStorage, @@ -119,6 +125,7 @@ const reduxPersistConfig = { contactSlice.name, guardiansSlice.name, dappSlice.name, + txFeeSlice.name, ], // More info here: https://shift.infinite.red/shipping-persistant-reducers-7341691232b1 // transforms: [SetTokenTransform], diff --git a/packages/web-extension-did/app/web/store/Provider/rootReducer.ts b/packages/web-extension-did/app/web/store/Provider/rootReducer.ts index 11b9712001..29e9da2c5e 100644 --- a/packages/web-extension-did/app/web/store/Provider/rootReducer.ts +++ b/packages/web-extension-did/app/web/store/Provider/rootReducer.ts @@ -25,12 +25,14 @@ import { paymentPersistConfig, cmsPersistConfig, dappPersistConfig, + txFeePersistConfig, } from './config'; import { miscSlice } from '@portkey-wallet/store/store-ca/misc/slice'; import { guardiansSlice } from '@portkey-wallet/store/store-ca/guardians/slice'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; +import { txFeeSlice } from '@portkey-wallet/store/store-ca/txFee/slice'; export const tokenReducer = persistReducer(tokenPersistConfig, tokenSlice.reducer); export const assetReducer = persistReducer(assetPersistConfig, assetsSlice.reducer); @@ -44,6 +46,7 @@ export const miscReducer = persistReducer(miscPersistConfig, miscSlice.reducer); export const paymentReducer = persistReducer(paymentPersistConfig, paymentSlice.reducer); export const cmsReducer = persistReducer(cmsPersistConfig, cmsSlice.reducer); export const dappReducer = persistReducer(dappPersistConfig, dappSlice.reducer); +export const txFeeReducer = persistReducer(txFeePersistConfig, txFeeSlice.reducer); const rootReducer = customCombineReducers({ [walletSlice.name]: walletReducer, @@ -63,6 +66,7 @@ const rootReducer = customCombineReducers({ [paymentSlice.name]: paymentReducer, [cmsSlice.name]: cmsReducer, [dappSlice.name]: dappReducer, + [txFeeSlice.name]: txFeeReducer, }); export default rootReducer; diff --git a/packages/web-extension-did/app/web/store/utils/getStore.ts b/packages/web-extension-did/app/web/store/utils/getStore.ts index 9273435c51..2e3f22c223 100644 --- a/packages/web-extension-did/app/web/store/utils/getStore.ts +++ b/packages/web-extension-did/app/web/store/utils/getStore.ts @@ -1,4 +1,7 @@ import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; +import { InitialTxFee } from '@portkey-wallet/constants/constants-ca/wallet'; +import { TxFeeItem } from '@portkey-wallet/store/store-ca/txFee/type'; +import { ChainId } from '@portkey/provider-types'; import { store } from 'store/Provider/store'; export const getStoreState = () => { @@ -16,3 +19,9 @@ export const isCurrentCaHash = (caHash: string) => { const originChainId = wallet.originChainId || caInfo?.originChainId; return caInfo?.[originChainId || DefaultChainId]?.caHash === caHash; }; + +export const getTxFee = (chainId: ChainId) => { + const currentNetwork = getStoreState().wallet.currentNetwork; + const targetTxFee = getStoreState().txFee[currentNetwork]?.filter((txf: TxFeeItem) => txf.chainId === chainId); + return targetTxFee?.[0].transactionFee ?? InitialTxFee; +}; diff --git a/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts b/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts index af23708504..96b6a98e31 100644 --- a/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts +++ b/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts @@ -1,14 +1,14 @@ import { ChainItemType } from '@portkey-wallet/store/store-ca/wallet/type'; -import { ChainType } from '@portkey-wallet/types'; +import { ChainId, ChainType } from '@portkey-wallet/types'; import { BaseToken } from '@portkey-wallet/types/types-ca/token'; import { getChainIdByAddress } from '@portkey-wallet/utils'; import { crossChainTransferToCa } from './crossChainTransferToCa'; import { managerTransfer } from './managerTransfer'; import { getChainNumber } from '@portkey-wallet/utils/aelf'; import { ZERO } from '@portkey-wallet/constants/misc'; -import { CROSS_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; import { timesDecimals } from '@portkey-wallet/utils/converter'; import { the2ThFailedActivityItemType } from '@portkey-wallet/types/types-ca/activity'; +import { getTxFee } from 'store/utils/getStore'; const nativeToken = { symbol: 'ELF', @@ -117,8 +117,10 @@ const crossChainTransfer = async ({ // return; // TODO Only support chainType: aelf let _amount = amount; + const toChainId = getChainIdByAddress(toAddress, chainType); + const { crossChain: crossChainFee } = getTxFee(toChainId as ChainId); if (tokenInfo.symbol === nativeToken.symbol) { - _amount = ZERO.plus(amount).minus(timesDecimals(CROSS_FEE, 8)).toNumber(); + _amount = ZERO.plus(amount).minus(timesDecimals(crossChainFee, 8)).toNumber(); } const crossChainTransferParams = { chainInfo, diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index b8500e4682..e9a1520d21 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -1,6 +1,6 @@ { "name": "web-extension-did", - "version": "1.3.2", + "version": "1.3.4", "description": "web-extension-did", "private": true, "dependencies": { From edb3667215a334583d1179568a2ea7895a725254 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 13 Jul 2023 17:32:16 +0800 Subject: [PATCH 345/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20back=20to=201=20a?= =?UTF-8?q?dd=20token=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/SimulatedInputBox/index.tsx | 12 +- .../js/pages/Token/ManageTokenList/index.tsx | 125 ++++++++++++++---- .../mobile-app-did/js/pages/Token/index.ts | 2 - 3 files changed, 108 insertions(+), 31 deletions(-) diff --git a/packages/mobile-app-did/js/components/SimulatedInputBox/index.tsx b/packages/mobile-app-did/js/components/SimulatedInputBox/index.tsx index 2d4c0fe98e..27b686a21e 100644 --- a/packages/mobile-app-did/js/components/SimulatedInputBox/index.tsx +++ b/packages/mobile-app-did/js/components/SimulatedInputBox/index.tsx @@ -8,16 +8,20 @@ import { StyleSheet, View, TouchableWithoutFeedback } from 'react-native'; import { pTd } from 'utils/unit'; interface ISimulatedInputBoxProps { - onClickInput: () => void; + placeholder?: string; + onClickInput?: () => void; } -export default function SimulatedInputBox({ onClickInput }: ISimulatedInputBoxProps) { +export default function SimulatedInputBox({ + placeholder = 'Search Dapp or enter URL', + onClickInput, +}: ISimulatedInputBoxProps) { return ( - + onClickInput?.()}> - Search Dapp or enter URL + {placeholder} diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index 6ccf63ef9d..5c79379d2b 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -1,24 +1,27 @@ import PageContainer from 'components/PageContainer'; import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; +import CommonInput from 'components/CommonInput'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; -import { StyleSheet, TouchableOpacity } from 'react-native'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; import gStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import CommonToast from 'components/CommonToast'; import { useLanguage } from 'i18n/hooks'; import { fetchAllTokenListAsync } from '@portkey-wallet/store/store-ca/tokenManagement/action'; +import useDebounce from 'hooks/useDebounce'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { request } from '@portkey-wallet/api/api-did'; import { useCaAddresses, useCaAddressInfoList, useChainIdList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { fetchTokenListAsync } from '@portkey-wallet/store/store-ca/assets/slice'; import Loading from 'components/Loading'; +import FilterTokenSection from '../components/FilterToken'; import PopularTokenSection from '../components/PopularToken'; import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; import { useFocusEffect } from '@react-navigation/native'; -import SimulatedInputBox from 'components/SimulatedInputBox'; +import Lottie from 'lottie-react-native'; import { handleErrorMessage } from '@portkey-wallet/utils'; interface ManageTokenListProps { @@ -28,6 +31,8 @@ const ManageTokenList: React.FC = () => { const { t } = useLanguage(); const timerRef = useRef(null); + const [isSearching, setIsSearching] = useState(false); + const chainIdList = useChainIdList(); const dispatch = useAppCommonDispatch(); @@ -36,6 +41,36 @@ const ManageTokenList: React.FC = () => { const { tokenDataShowInMarket } = useAppCASelector(state => state.tokenManagement); + const [keyword, setKeyword] = useState(''); + const [filterTokenList, setFilterTokenList] = useState([]); + + const debounceWord = useDebounce(keyword, 800); + + const fetchSearchedTokenList = useCallback(async () => { + try { + if (!debounceWord) return; + setIsSearching(true); + + const list = await request.token.fetchTokenListBySearch({ + params: { + symbol: debounceWord, + chainIds: chainIdList, + }, + }); + + const tmpToken: TokenItemShowType[] = list.map((item: any) => ({ + ...item, + isAdded: item.isDisplay, + userTokenId: item.id, + })); + setFilterTokenList(tmpToken); + } catch (error) { + CommonToast.fail(handleErrorMessage(error)); + } finally { + setIsSearching(false); + } + }, [chainIdList, debounceWord]); + const onHandleTokenItem = useCallback( async (item: TokenItemShowType, isDisplay: boolean) => { Loading.showOnce(); @@ -49,8 +84,11 @@ const ManageTokenList: React.FC = () => { }); timerRef.current = setTimeout(async () => { dispatch(fetchTokenListAsync({ caAddresses: caAddressArray, caAddressInfos })); - await dispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray: chainIdList })); - + if (debounceWord) { + await fetchSearchedTokenList(); + } else { + await dispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray: chainIdList })); + } Loading.hide(); CommonToast.success('Success'); }, 800); @@ -59,37 +97,31 @@ const ManageTokenList: React.FC = () => { CommonToast.fail(handleErrorMessage(err)); } }, - [caAddressArray, caAddressInfos, chainIdList, dispatch], - ); - - const RightDom = useMemo( - () => ( - { - navigationService.navigate('CustomToken'); - }}> - - - ), - [], + [caAddressArray, caAddressInfos, chainIdList, debounceWord, dispatch, fetchSearchedTokenList], ); useFocusEffect( useCallback(() => { + fetchSearchedTokenList(); dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); - }, [chainIdList, dispatch]), + }, [chainIdList, dispatch, fetchSearchedTokenList]), ); useEffect(() => { if (tokenDataShowInMarket.length) return; - dispatch(fetchAllTokenListAsync({ keyword: '', chainIdArray: chainIdList })); + dispatch(fetchAllTokenListAsync({ keyword: debounceWord, chainIdArray: chainIdList })); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { - dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); - }, [chainIdList, dispatch]); + if (debounceWord) { + // get filter token + setFilterTokenList([]); + fetchSearchedTokenList(); + } else { + dispatch(fetchAllTokenListAsync({ chainIdArray: chainIdList })); + } + }, [chainIdList, debounceWord, dispatch, fetchSearchedTokenList]); // clear timer useEffect( @@ -99,6 +131,32 @@ const ManageTokenList: React.FC = () => { [], ); + const RightDom = useMemo( + () => ( + { + navigationService.navigate('CustomToken'); + }}> + + + ), + [], + ); + + const IptRightIcon = useMemo(() => { + if (isSearching) + return ( + + ); + + return keyword ? ( + setKeyword('')}> + + + ) : undefined; + }, [isSearching, keyword]); + return ( = () => { rightDom={RightDom} containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: true }}> - navigationService.navigate('SearchTokenList')} /> - + + { + setKeyword(v.trim()); + }} + /> + + + {debounceWord ? ( + + ) : ( + + )} ); }; @@ -126,4 +198,7 @@ export const pageStyles = StyleSheet.create({ list: { flex: 1, }, + loadingIcon: { + width: pTd(20), + }, }); diff --git a/packages/mobile-app-did/js/pages/Token/index.ts b/packages/mobile-app-did/js/pages/Token/index.ts index 8dd799c8a7..9eb08d984a 100644 --- a/packages/mobile-app-did/js/pages/Token/index.ts +++ b/packages/mobile-app-did/js/pages/Token/index.ts @@ -1,5 +1,4 @@ import ManageTokenList from './ManageTokenList'; -import SearchTokenList from './SearchTokenList'; import TokenDetail from './TokenDetail/index'; import CustomToken from './CustomToken/index'; @@ -7,7 +6,6 @@ const stackNav = [ { name: 'ManageTokenList', component: ManageTokenList }, { name: 'TokenDetail', component: TokenDetail }, { name: 'CustomToken', component: CustomToken }, - { name: 'SearchTokenList', component: SearchTokenList }, ] as const; export default stackNav; From 8a0794cf5efe15993360c62b051cec4a2f2c86dd Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 13 Jul 2023 18:23:28 +0800 Subject: [PATCH 346/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20adjust=20txF?= =?UTF-8?q?ee=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/useTxFee.ts | 25 +++++-------------- packages/store/store-ca/txFee/actions.ts | 8 +++++- packages/store/store-ca/txFee/slice.ts | 2 +- packages/store/store-ca/txFee/type.ts | 5 ++-- .../app/web/pages/Buy/index.tsx | 7 +++--- .../app/web/pages/Send/index.tsx | 7 +++--- .../app/web/store/utils/getStore.ts | 5 ++-- .../app/web/utils/errorHandler.ts | 14 +++++------ 8 files changed, 33 insertions(+), 40 deletions(-) diff --git a/packages/hooks/hooks-ca/useTxFee.ts b/packages/hooks/hooks-ca/useTxFee.ts index 8a9dbaf517..7b75e60a48 100644 --- a/packages/hooks/hooks-ca/useTxFee.ts +++ b/packages/hooks/hooks-ca/useTxFee.ts @@ -1,18 +1,16 @@ import { useAppCASelector } from './index'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import { useAppCommonDispatch } from '../index'; import { useCurrentNetwork } from './network'; import { fetchTxFeeAsync } from '@portkey-wallet/store/store-ca/txFee/actions'; import { ChainId } from '@portkey-wallet/types'; -import { TxFeeItem } from '@portkey-wallet/store/store-ca/txFee/type'; import { InitialTxFee } from '@portkey-wallet/constants/constants-ca/wallet'; +import { useCurrentChainList } from './chainList'; export const useFetchTxFee = () => { - const txFee = useAppCASelector(state => state.txFee); - const { chainInfo } = useAppCASelector(state => state.wallet); const dispatch = useAppCommonDispatch(); - const currentNetwork = useCurrentNetwork(); - const chainIds = useMemo(() => chainInfo?.[currentNetwork]?.map(chain => chain.chainId), [chainInfo, currentNetwork]); + const chainList = useCurrentChainList(); + const chainIds = useMemo(() => chainList?.map(chain => chain.chainId), [chainList]); useEffect(() => { if (chainIds?.length) { @@ -20,23 +18,12 @@ export const useFetchTxFee = () => { } // eslint-disable-next-line react-hooks/exhaustive-deps }, [chainIds]); - - return useCallback( - (chainId: ChainId) => { - const targetTxFee = txFee[currentNetwork]?.filter((txf: TxFeeItem) => txf.chainId === chainId); - return targetTxFee?.[0].transactionFee ?? InitialTxFee; - }, - [currentNetwork, txFee], - ); }; export const useGetTxFee = (chainId: ChainId) => { const txFee = useAppCASelector(state => state.txFee); const currentNetwork = useCurrentNetwork(); - const targetTxFee = useMemo( - () => txFee[currentNetwork]?.filter((txf: TxFeeItem) => txf.chainId === chainId), - [chainId, currentNetwork, txFee], - ); + const targetTxFee = useMemo(() => txFee[currentNetwork]?.[chainId], [chainId, currentNetwork, txFee]); - return useMemo(() => targetTxFee?.[0].transactionFee ?? InitialTxFee, [targetTxFee]); + return useMemo(() => targetTxFee ?? InitialTxFee, [targetTxFee]); }; diff --git a/packages/store/store-ca/txFee/actions.ts b/packages/store/store-ca/txFee/actions.ts index f230e82c43..bab0ccda1d 100644 --- a/packages/store/store-ca/txFee/actions.ts +++ b/packages/store/store-ca/txFee/actions.ts @@ -10,9 +10,15 @@ export const fetchTxFeeAsync = createAsyncThunk('fetchTxFeeAsync', async (chainI const { wallet: { currentNetwork }, } = getState() as { wallet: WalletState }; + + const fee: any = {}; + result?.forEach((item: any) => { + fee[item.chainId] = item.transactionFee; + }); + return { currentNetwork, - fee: result, + fee, }; }); diff --git a/packages/store/store-ca/txFee/slice.ts b/packages/store/store-ca/txFee/slice.ts index 16102cced0..b78d31e4ff 100644 --- a/packages/store/store-ca/txFee/slice.ts +++ b/packages/store/store-ca/txFee/slice.ts @@ -23,7 +23,7 @@ export const txFeeSlice = createSlice({ if (txFeeValue?.[action.payload]) delete txFeeValue[action.payload]; state = txFeeValue; } else { - state = {}; + state = initialState; } }); }, diff --git a/packages/store/store-ca/txFee/type.ts b/packages/store/store-ca/txFee/type.ts index b0b7490e9a..88a2255e8e 100644 --- a/packages/store/store-ca/txFee/type.ts +++ b/packages/store/store-ca/txFee/type.ts @@ -1,8 +1,7 @@ import { ChainId, NetworkType } from '@portkey-wallet/types'; export type TxFeeItem = { - chainId: ChainId; - transactionFee: { + [key in ChainId]?: { ach: number; crossChain: number; max: number; @@ -10,5 +9,5 @@ export type TxFeeItem = { }; export type TxFeeType = { - [key in NetworkType]?: TxFeeItem[]; + [key in NetworkType]?: TxFeeItem; }; diff --git a/packages/web-extension-did/app/web/pages/Buy/index.tsx b/packages/web-extension-did/app/web/pages/Buy/index.tsx index 7dbe5b29ac..ea70ae1955 100644 --- a/packages/web-extension-did/app/web/pages/Buy/index.tsx +++ b/packages/web-extension-did/app/web/pages/Buy/index.tsx @@ -28,7 +28,7 @@ import { getBalance } from 'utils/sandboxUtil/getBalance'; import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; +import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import BuyForm from './components/BuyForm'; import SellForm from './components/SellForm'; import { useEffectOnce } from 'react-use'; @@ -65,7 +65,8 @@ export default function Buy() { const [rateUpdateTime, setRateUpdateTime] = useState(MAX_UPDATE_TIME); const { isBuySectionShow, isSellSectionShow, refreshBuyButton } = useBuyButtonShow(); const checkManagerSyncState = useCheckManagerSyncState(); - const getTxFeeByChainId = useFetchTxFee(); + useFetchTxFee(); + const { ach: achFee } = useGetTxFee('AELF'); const disabled = useMemo(() => !!errMsg || !amount, [errMsg, amount]); const showRateText = useMemo( @@ -373,7 +374,6 @@ export default function Buy() { }, [stopInterval]); const handleNext = useCallback(async () => { - const { ach: achFee } = getTxFeeByChainId('AELF'); const { side } = valueSaveRef.current; setLoading(true); const result = await refreshBuyButton(); @@ -433,6 +433,7 @@ export default function Buy() { }); }, [ accountTokenList, + achFee, checkManagerSyncState, currentChain, currentNetwork.walletType, diff --git a/packages/web-extension-did/app/web/pages/Send/index.tsx b/packages/web-extension-did/app/web/pages/Send/index.tsx index c21fd092bb..80f7763fba 100644 --- a/packages/web-extension-did/app/web/pages/Send/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/index.tsx @@ -27,7 +27,7 @@ import { ZERO } from '@portkey-wallet/constants/misc'; import { TransactionError } from '@portkey-wallet/constants/constants-ca/assets'; import { the2ThFailedActivityItemType } from '@portkey-wallet/types/types-ca/activity'; import { contractErrorHandler } from 'utils/tryErrorHandler'; -import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; +import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import PromptFrame from 'pages/components/PromptFrame'; import clsx from 'clsx'; import { AddressCheckError } from '@portkey-wallet/store/store-ca/assets/type'; @@ -72,7 +72,8 @@ export default function Send() { const checkManagerSyncState = useCheckManagerSyncState(); const [txFee, setTxFee] = useState(); const currentChain = useCurrentChain(state.chainId); - const getTxFeeByChainId = useFetchTxFee(); + useFetchTxFee(); + const { crossChain: crossChainFee } = useGetTxFee(state.chainId); const tokenInfo = useMemo( () => ({ chainId: state.chainId, @@ -195,7 +196,6 @@ export default function Send() { const handleCheckPreview = useCallback(async () => { try { setLoading(true); - const { crossChain: crossChainFee } = getTxFeeByChainId(state.chainId); if (!ZERO.plus(amount).toNumber()) return 'Please input amount'; const _isManagerSynced = await checkManagerSyncState(state.chainId); if (!_isManagerSynced) { @@ -243,6 +243,7 @@ export default function Send() { toAccount.address, chainInfo?.chainId, symbol, + crossChainFee, ]); const sendHandler = useCallback(async () => { diff --git a/packages/web-extension-did/app/web/store/utils/getStore.ts b/packages/web-extension-did/app/web/store/utils/getStore.ts index 2e3f22c223..590cd11852 100644 --- a/packages/web-extension-did/app/web/store/utils/getStore.ts +++ b/packages/web-extension-did/app/web/store/utils/getStore.ts @@ -1,6 +1,5 @@ import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; import { InitialTxFee } from '@portkey-wallet/constants/constants-ca/wallet'; -import { TxFeeItem } from '@portkey-wallet/store/store-ca/txFee/type'; import { ChainId } from '@portkey/provider-types'; import { store } from 'store/Provider/store'; @@ -22,6 +21,6 @@ export const isCurrentCaHash = (caHash: string) => { export const getTxFee = (chainId: ChainId) => { const currentNetwork = getStoreState().wallet.currentNetwork; - const targetTxFee = getStoreState().txFee[currentNetwork]?.filter((txf: TxFeeItem) => txf.chainId === chainId); - return targetTxFee?.[0].transactionFee ?? InitialTxFee; + const targetTxFee = getStoreState().txFee?.[currentNetwork]?.[chainId]; + return targetTxFee ?? InitialTxFee; }; diff --git a/packages/web-extension-did/app/web/utils/errorHandler.ts b/packages/web-extension-did/app/web/utils/errorHandler.ts index 15478b8386..6772e00680 100644 --- a/packages/web-extension-did/app/web/utils/errorHandler.ts +++ b/packages/web-extension-did/app/web/utils/errorHandler.ts @@ -66,7 +66,13 @@ export default function errorHandler(code: keyof typeof errorMap, error?: any | }; if (code === 0) { // success - } else if (error && typeof error !== 'string') { + } else if (typeof error === 'string') { + output = { + ...output, + name: 'customError', + message: error, + }; + } else if (typeof error === 'object') { console.log(error, 'errorHandler'); output = { ...output, @@ -75,12 +81,6 @@ export default function errorHandler(code: keyof typeof errorMap, error?: any | stack: error.stack, data: error.data, }; - } else if (typeof error === 'string') { - output = { - ...output, - name: 'customError', - message: error, - }; } else { output = { ...output, From bf7615fad1ba9973835ba4d1a614cb8c345150e0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 10:08:04 +0800 Subject: [PATCH 347/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20bookmarks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/assets/image/svgs.js | 2 +- .../js/assets/image/svgs/no-bookmarks.svg | 6 + .../js/assets/image/svgs/no-records.svg | 6 + .../js/assets/image/svgs/red-delete.svg | 3 + .../components/TextWithProtocolIcon/index.tsx | 6 +- packages/mobile-app-did/js/hooks/discover.ts | 34 +++-- .../Bookmark/components/BookmarkItem.tsx | 51 +++++-- .../Bookmark/components/BookmarksSection.tsx | 27 +++- .../js/pages/Discover/Bookmark/index.tsx | 16 ++- .../js/pages/Discover/DiscoverHome/index.tsx | 41 +++--- .../DiscoverArchivedSection/index.tsx | 132 ++++++++++-------- .../DiscoverCmsListSection/index.tsx | 15 +- .../components/NoDiscoverData/index.tsx | 33 +++++ 13 files changed, 251 insertions(+), 121 deletions(-) create mode 100644 packages/mobile-app-did/js/assets/image/svgs/no-bookmarks.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/no-records.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/red-delete.svg create mode 100644 packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index aec3f18a62..68235915a9 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/no-bookmarks.svg b/packages/mobile-app-did/js/assets/image/svgs/no-bookmarks.svg new file mode 100644 index 0000000000..916481de4f --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/no-bookmarks.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/no-records.svg b/packages/mobile-app-did/js/assets/image/svgs/no-records.svg new file mode 100644 index 0000000000..ef0b1f370f --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/no-records.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/red-delete.svg b/packages/mobile-app-did/js/assets/image/svgs/red-delete.svg new file mode 100644 index 0000000000..ace5a5baf0 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/red-delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx index 7904db9273..08cd06fa5e 100644 --- a/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx +++ b/packages/mobile-app-did/js/components/TextWithProtocolIcon/index.tsx @@ -21,8 +21,8 @@ const TextWithProtocolIcon = ({ title = '', url, textFontSize = pTd(14), - wrapStyle = {}, iconSize = pTd(14), + wrapStyle = {}, type = 'iconRight', location = 'other', }: ITextWithProtocolIconProps) => { @@ -32,8 +32,6 @@ const TextWithProtocolIcon = ({ fontSize: textFontSize, }; - console.log(isDanger); - const ProtocolIcon = useMemo(() => { if (isDanger) { return ; @@ -67,6 +65,8 @@ export default memo(TextWithProtocolIcon); const styles = StyleSheet.create({ wrap: { + width: '100%', + paddingRight: pTd(16), display: 'flex', flexDirection: 'row', justifyContent: 'flex-start', diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index e10453d56e..9e01e0b713 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -21,20 +21,23 @@ export const useDiscoverJumpWithNetWork = () => { const { discoverMap } = useAppCASelector(state => state.discover); - const discoverJump = ({ item, autoApprove }: { item: ITabItem; autoApprove?: boolean }) => { - if (!discoverMap || !discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); + const discoverJump = useCallback( + ({ item, autoApprove }: { item: ITabItem; autoApprove?: boolean }) => { + if (!discoverMap || !discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); - dispatch(createNewTab({ ...item, networkType })); - dispatch(setActiveTab({ ...item, networkType })); - dispatch(addRecordsItem({ ...item, networkType })); - dispatch(changeDrawerOpenStatus(true)); - if (autoApprove) dispatch(addAutoApproveItem(item.id)); - }; + dispatch(createNewTab({ ...item, networkType })); + dispatch(setActiveTab({ ...item, networkType })); + dispatch(addRecordsItem({ ...item, networkType })); + dispatch(changeDrawerOpenStatus(true)); + if (autoApprove) dispatch(addAutoApproveItem(item.id)); + }, + [discoverMap, dispatch, networkType], + ); return discoverJump; }; -// discover whiteList +// discover whiteList (http) export const useDiscoverWhiteList = () => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); @@ -97,3 +100,16 @@ export const useBookmarkList = () => { }, ]; }; + +export const useRecordsList = (isReverse: boolean): ITabItem[] => { + const { networkType } = useCurrentNetworkInfo(); + const { discoverMap } = useAppCASelector(state => state.discover); + + const list = useMemo(() => { + return isReverse + ? (discoverMap?.[networkType]?.recordsList as ITabItem[]).reverse() + : (discoverMap?.[networkType]?.recordsList as ITabItem[]); + }, [discoverMap, isReverse, networkType]); + + return list || []; +}; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 6a0b411bb0..7d3a661c12 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -1,5 +1,5 @@ import GStyles from 'assets/theme/GStyles'; -import { TextM } from 'components/CommonText'; +import { TextM, TextS } from 'components/CommonText'; import Touchable from 'components/Touchable'; import React, { memo, useCallback, useEffect, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; @@ -7,7 +7,12 @@ import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlis import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; import { useBookmark } from '../context'; import usePrevious from 'hooks/usePrevious'; -import { BGStyles } from 'assets/theme/styles'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import { defaultColors } from 'assets/theme'; export default memo( function BookmarkItem(props: RenderItemParams) { @@ -22,7 +27,7 @@ export default memo( const renderUnderlayLeft = useCallback( () => ( swipeableRef.current?.close()}> - close + Delete ), [], @@ -34,25 +39,31 @@ export default memo( key={item} item={props} ref={swipeableRef} - swipeEnabled={false} + swipeEnabled={true} snapPointsLeft={[80]} renderUnderlayLeft={renderUnderlayLeft}> {isEdit && ( - swipeableRef.current?.open(OpenDirection.LEFT)}> - open + swipeableRef.current?.open(OpenDirection.LEFT)}> + )} - {item} + + + + dddddd + + {/* drag */} @@ -67,20 +78,30 @@ export default memo( ); const styles = StyleSheet.create({ - marginContainer: { - // marginHorizontal: 16, - }, + marginContainer: {}, underlayLeftBox: { flex: 1, - paddingRight: 30, + paddingRight: pTd(12), flexDirection: 'row', alignItems: 'center', marginHorizontal: 16, justifyContent: 'flex-end', + backgroundColor: defaultColors.bg17, + color: defaultColors.font1, + textAlign: 'center', }, itemRow: { - padding: 20, - height: 80, - marginVertical: 10, + padding: pTd(12), + height: pTd(72), + // marginVertical: 10, + }, + deleteIconWrap: { + marginRight: pTd(16), + }, + websiteIconStyle: { + marginRight: pTd(16), + }, + infoWrap: { + flex: 1, }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index f8e581e395..d56a6c6f92 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -2,18 +2,18 @@ import React, { useCallback, useMemo, useState } from 'react'; import PageContainer from 'components/PageContainer'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; -import { FontStyles } from 'assets/theme/styles'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; import { ArchivedTabEnum } from 'pages/Discover/types'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; -const mockData = ['1', '2', '3', '4']; +const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; function BookmarksSection() { const [list, setList] = useState(mockData); @@ -21,7 +21,7 @@ function BookmarksSection() { const BottomBox = useMemo( () => ( - + {isEdit ? ( <> @@ -37,9 +37,12 @@ function BookmarksSection() { return ( - + } + ListFooterComponent={} keyExtractor={_item => _item} renderItem={props => } onDragEnd={({ data }) => setList(data)} @@ -60,5 +63,17 @@ export default function Container() { const styles = StyleSheet.create({ // remove padding to scale item - containerStyles: { paddingHorizontal: pTd(20), flex: 1, justifyContent: 'space-between' }, + containerStyles: { + flex: 1, + justifyContent: 'space-between', + borderRadius: pTd(6), + paddingVertical: pTd(8), + }, + listWrap: { + paddingVertical: pTd(8), + paddingHorizontal: pTd(20), + }, + buttonGroupWrap: { + paddingHorizontal: pTd(20), + }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx index 5ce94553d9..978fd76a3b 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx @@ -9,6 +9,7 @@ import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; import BookmarksSection from './components/BookmarksSection'; +import { RouteProp, useRoute } from '@react-navigation/native'; type TabItemType = { name: string; @@ -23,21 +24,26 @@ const tabList: TabItemType[] = [ component: , }, { - name: 'History', + name: 'Records', type: ArchivedTabEnum.History, component: <>, }, ]; export default function Bookmark() { - const [selectTab, setSelectTab] = useState(ArchivedTabEnum.Bookmarks); + const { + params: { type = ArchivedTabEnum.Bookmarks }, + } = useRoute>(); - const onTabPress = useCallback((type: ArchivedTabEnum) => { - setSelectTab(type); + const [selectTab, setSelectTab] = useState(type); + + const onTabPress = useCallback((tabType: ArchivedTabEnum) => { + setSelectTab(tabType); }, []); return ( ( + { + if (!(await requestQrPermission())) return showDialog(); + navigationService.navigate('QrScanner'); + }}> + + + ), + [requestQrPermission, showDialog], + ); + return ( - - { - if (!(await requestQrPermission())) return showDialog(); - navigationService.navigate('QrScanner'); - }}> - - - } - themeType="blue" - titleDom={'Discover'} - /> - navigationService.navigate('DiscoverSearch')} /> + + navigationService.navigate('DiscoverSearch')} /> + - + ); } @@ -63,6 +63,7 @@ export default function DiscoverHome() { const styles = StyleSheet.create({ container: { backgroundColor: defaultColors.bg4, + paddingTop: pTd(24), flex: 1, }, inputContainer: { diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 78c5863add..bbe41433e4 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -2,7 +2,7 @@ import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { TextM, TextS } from 'components/CommonText'; -import { useBookmarkList } from 'hooks/discover'; +import { useBookmarkList, useRecordsList } from 'hooks/discover'; import React, { useCallback, useMemo } from 'react'; import { StyleSheet, View, Easing } from 'react-native'; import { pTd } from 'utils/unit'; @@ -11,82 +11,100 @@ import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import navigationService from 'utils/navigationService'; import { ArchivedTabEnum } from 'pages/Discover/types'; +import NoDiscoverData from '../NoDiscoverData'; export function DiscoverArchivedSection() { const bookmarkListStore = useBookmarkList(); + const recordsListStore = useRecordsList(true); + const [index, setIndex] = React.useState( bookmarkListStore.length ? ArchivedTabEnum.Bookmarks : ArchivedTabEnum.History, ); const bookmarkList = useMemo(() => bookmarkListStore.slice(0, 4), [bookmarkListStore]); + const recordsList = useMemo(() => recordsListStore.slice(0, 4), [recordsListStore]); const onSeeAllPress = useCallback(() => { - if (index === ArchivedTabEnum.Bookmarks) { - navigationService.navigate('Bookmark'); - return; - } - // navigationService.navigate('Bookmark'); + navigationService.navigate('Bookmark', { type: index }); }, [index]); - return ( - <> - {true && ( - - - - setIndex(ArchivedTabEnum.Bookmarks)} - style={[ - GStyles.marginRight(24), - index === ArchivedTabEnum.Bookmarks ? FontStyles.weight500 : FontStyles.font3, - ]}> - Bookmarks - - setIndex(ArchivedTabEnum.History)} - style={index === ArchivedTabEnum.History ? FontStyles.weight500 : FontStyles.font3}> - History - - - - See All - - + const isShowArchivedSections = false; - - - - - {bookmarkList.map((item, idx) => ( - - - - - {item.url} - - - - ))} - - - - History - - - + if (isShowArchivedSections) return null; + return ( + + + + setIndex(ArchivedTabEnum.Bookmarks)} + style={[ + GStyles.marginRight(24), + index === ArchivedTabEnum.Bookmarks ? FontStyles.weight500 : FontStyles.font3, + ]}> + Bookmarks + + setIndex(ArchivedTabEnum.History)} + style={index === ArchivedTabEnum.History ? FontStyles.weight500 : FontStyles.font3}> + History + - )} - + + See All + + + + + + {bookmarkList?.length === 0 ? ( + + ) : ( + + {bookmarkList.map((item, idx) => ( + + + + + {item.url} + + + + ))} + + )} + + + {bookmarkList?.length === 0 ? ( + + ) : ( + + {recordsList.map((item, idx) => ( + + + + + {item.url} + + + + ))} + + )} + + + + ); } const styles = StyleSheet.create({ wrap: { height: pTd(144), + marginBottom: pTd(16), ...GStyles.paddingArg(0, 20), }, headerWrap: { diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index dfc7aab198..184917d645 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -10,12 +10,15 @@ import React, { useCallback } from 'react'; import { StyleSheet, View, Image, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import fonts from 'assets/theme/fonts'; export function DiscoverCmsListSection() { const GroupList = useDiscoverGroupList(); const { s3Url } = useCurrentNetworkInfo(); const discoverJump = useDiscoverJumpWithNetWork(); + console.log('===GroupListGroupList=================================', GroupList); + const onClickJump = useCallback( (i: DiscoverItem) => { discoverJump({ @@ -32,8 +35,8 @@ export function DiscoverCmsListSection() { return ( {GroupList.map((group, index) => ( - - {group.title} + + {group.title} {group.items.map((item, i) => ( onClickJump(item)}> @@ -55,12 +58,14 @@ export function DiscoverCmsListSection() { const styles = StyleSheet.create({ wrap: { - ...GStyles.paddingArg(8, 20), - flex: 1, + ...GStyles.paddingArg(0, 20), + marginBottom: pTd(16), + }, + groupWrap: { + marginBottom: pTd(16), }, groupTitle: { marginBottom: pTd(8), - marginTop: pTd(16), }, itemsGroup: { borderRadius: pTd(6), diff --git a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx new file mode 100644 index 0000000000..185bab1682 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx @@ -0,0 +1,33 @@ +import { TextS } from 'components/CommonText'; +import React, { memo } from 'react'; +import { View, StyleSheet } from 'react-native'; + +import Svg from 'components/Svg'; +import GStyles from 'assets/theme/GStyles'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import { pTd } from 'utils/unit'; + +export interface INoDiscoverDataProps { + type?: 'noBookmarks' | 'noRecords'; +} + +const NoDiscoverData = (props: INoDiscoverDataProps) => { + const { type = 'noBookmarks' } = props; + const iconName = type === 'noBookmarks' ? 'no-bookmarks' : 'no-records'; + const noDataText = type === 'noBookmarks' ? 'No Bookmarks' : 'No Records'; + + return ( + + + {noDataText} + + ); +}; + +export default memo(NoDiscoverData); + +const styles = StyleSheet.create({ + icon: { + marginBottom: pTd(8), + }, +}); From 3d62ca0647664e89c802a8a3254c6c497ebfac87 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 10:09:05 +0800 Subject: [PATCH 348/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20scan=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/QrScanner/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 24af93466a..314ef1e4c4 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -109,7 +109,7 @@ const QrScanner: React.FC = () => { - {t('Receive code / Login code')} + {t('Receive code / Login code / URL code')} From bd646a0dbd5c56cc76ac0a40dc20f2afab252546 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 10:10:38 +0800 Subject: [PATCH 349/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20cms=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index 72f8b9c77f..511ad17959 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -57,13 +57,13 @@ export function useDiscoverGroupList(isInit = false) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - if (!isInit) { - dispatch(getDiscoverGroupAsync(networkType)); - } - }, [dispatch, isInit, networkType]); + // useEffect(() => { + // if (!isInit) { + // dispatch(getDiscoverGroupAsync(networkType)); + // } + // }, [dispatch, isInit, networkType]); - return discoverGroupList; + return discoverGroupList || []; } export const useBuyButton = (isInit = false) => { From d1d0b2f3778a76ea93fa8d33aa673d38903ee8f1 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 14 Jul 2023 10:37:29 +0800 Subject: [PATCH 350/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20check=20base58?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/utils/contacts/index.ts | 4 ++-- packages/utils/aelf.ts | 7 +++---- packages/utils/reg.ts | 4 ++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/mobile-app-did/js/utils/contacts/index.ts b/packages/mobile-app-did/js/utils/contacts/index.ts index 6ab84da34a..47132a09f8 100644 --- a/packages/mobile-app-did/js/utils/contacts/index.ts +++ b/packages/mobile-app-did/js/utils/contacts/index.ts @@ -1,6 +1,7 @@ import AElf from 'aelf-sdk'; import { isAddress as web3IsAddress } from 'web3-utils'; import { ChainType } from '@portkey-wallet/types'; +import { isValidBase58 } from '@portkey-wallet/utils/reg'; export type ContactItemTypes = { id: string; @@ -30,8 +31,7 @@ export const isAddress = (address: string, chainType: ChainType) => { }; export const isAelfAddress = (value?: string) => { - if (!value) return false; - if (/[\u4e00-\u9fa5]/.test(value)) return false; + if (!value || !isValidBase58(value)) return false; try { return !!AElf.utils.decodeAddressRep(value); } catch { diff --git a/packages/utils/aelf.ts b/packages/utils/aelf.ts index 8210e66cda..3acf492693 100644 --- a/packages/utils/aelf.ts +++ b/packages/utils/aelf.ts @@ -2,6 +2,7 @@ import AElf from 'aelf-sdk'; import { COMMON_PRIVATE } from '@portkey-wallet/constants'; import { AElfInterface } from '@portkey-wallet/types/aelf'; import { ChainId } from '@portkey-wallet/types'; +import { isValidBase58 } from './reg'; const Wallet = AElf.wallet; export function isEqAddress(a1?: string, a2?: string) { @@ -9,8 +10,7 @@ export function isEqAddress(a1?: string, a2?: string) { } export function isAelfAddress(value?: string) { - if (!value) return false; - if (/[\u4e00-\u9fa5]/.test(value)) return false; + if (!value || !isValidBase58(value)) return false; if (value.includes('_') && value.split('_').length < 3) return false; try { return !!AElf.utils.decodeAddressRep(value); @@ -24,8 +24,7 @@ export const getChainNumber = (chainId: string) => { }; export function isDIDAelfAddress(value?: string) { - if (!value) return false; - if (/[\u4e00-\u9fa5]/.test(value)) return false; + if (!value || !isValidBase58(value)) return false; if (value.includes('_') && value.split('_').length === 2) { const arr = value.split('_'); const res = arr[0].length > arr[1].length ? arr[0] : arr[1]; diff --git a/packages/utils/reg.ts b/packages/utils/reg.ts index 6a74248b13..7d0d29fc5d 100644 --- a/packages/utils/reg.ts +++ b/packages/utils/reg.ts @@ -83,3 +83,7 @@ export const isValidUrl = (url: string) => { var urlRegex = /^(https?|ftp):\/\/(-\.)?([^\s/?\.#]+\.?)+(\/[^\s]*)?$/i; return urlRegex.test(url); }; + +export const isValidBase58 = (str: string) => { + return !/[\u4e00-\u9fa5\u3000-\u303f\uff01-\uff5e]/.test(str); +}; From 9d708bd1b03eead192e2d6ff4e2880a99e4956a6 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 11:05:27 +0800 Subject: [PATCH 351/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20useDevi?= =?UTF-8?q?ceList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/wallet.ts | 108 +++++++++++------- .../js/pages/My/WalletSecurity/Dapp/index.tsx | 18 +-- .../pages/My/WalletSecurity/Device/index.tsx | 12 +- .../js/pages/My/WalletSecurity/index.tsx | 2 +- .../app/web/pages/WalletSecurity/index.tsx | 4 +- 5 files changed, 84 insertions(+), 60 deletions(-) diff --git a/packages/hooks/hooks-ca/wallet.ts b/packages/hooks/hooks-ca/wallet.ts index 370733833b..38885acdf1 100644 --- a/packages/hooks/hooks-ca/wallet.ts +++ b/packages/hooks/hooks-ca/wallet.ts @@ -14,7 +14,9 @@ import { DeviceInfoType } from '@portkey-wallet/types/types-ca/device'; import { extraDataListDecode } from '@portkey-wallet/utils/device'; import { ChainId } from '@portkey-wallet/types'; import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; -import useEffectOnce from 'hooks/useEffectOnce'; + +import { getCAHolderManagerInfo } from '@portkey-wallet/graphql/contract/queries'; +import { ManagerInfo, Maybe } from '@portkey-wallet/graphql/contract/__generated__/types'; export interface CurrentWalletType extends WalletInfoType, CAInfoType { caHash?: string; @@ -76,44 +78,31 @@ export const useCurrentWallet = () => { }, [originChainId, wallet]); }; -export const useDeviceList = (isInitLoad = true) => { +interface IUseDeviceListConfig { + isInit?: boolean; + isAmountOnly?: boolean; + onError?: (error: any) => void; +} +const defaultUseDeviceListConfig: IUseDeviceListConfig = { + isInit: true, + isAmountOnly: false, +}; +export const useDeviceList = (config?: IUseDeviceListConfig) => { + const { isInit, isAmountOnly, onError } = { ...defaultUseDeviceListConfig, ...config }; + const networkInfo = useCurrentNetworkInfo(); const walletInfo = useCurrentWalletInfo(); const originChainId = useOriginChainId(); const chainInfo = useCurrentChain(originChainId); - - const [load, { data, error, loading, refetch }] = useCaHolderManagerInfoLazyQuery({ - client: getApolloClient(networkInfo.networkType), - variables: { - dto: { - chainId: chainInfo?.chainId, - caHash: walletInfo.caHash, - skipCount: 0, - maxResultCount: 100, - }, - }, - fetchPolicy: 'no-cache', - }); + const [loading, setLoading] = useState(false); const [deviceList, setDeviceList] = useState([]); const [deviceAmount, setDeviceAmount] = useState(0); - const [decodeLoading, setDecodeLoading] = useState(false); - const getDeviceList = useCallback(async () => { - if (error || !data || !data.caHolderManagerInfo || data.caHolderManagerInfo.length < 1) { - setDeviceList([]); - setDeviceAmount(0); - return; - } - - const caHolderManagerInfo = data.caHolderManagerInfo[0]; - const managers = caHolderManagerInfo?.managerInfos || []; - // console.log('managers===', managers); - setDeviceAmount(managers.length); - - setDecodeLoading(true); - const extraDataList = await extraDataListDecode(managers.map(item => item?.extraData || '')); - const _deviceList = managers + const getDeviceList = useCallback(async (managerInfos: Array>) => { + const extraDataStrList = managerInfos.map(item => item?.extraData || ''); + const extraDataList = await extraDataListDecode(extraDataStrList); + const _deviceList = managerInfos .map((item, idx) => { return { ...extraDataList[idx], @@ -123,18 +112,57 @@ export const useDeviceList = (isInitLoad = true) => { .reverse(); setDeviceList(_deviceList); - setDecodeLoading(false); - }, [error, data]); + }, []); + + const refresh = useCallback(async () => { + setLoading(true); + try { + const result = await getCAHolderManagerInfo(networkInfo.networkType, { + dto: { + chainId: chainInfo?.chainId, + caHash: walletInfo.caHash, + skipCount: 0, + maxResultCount: 200, + }, + }); + const { data } = result || {}; + + if (!data || !data.caHolderManagerInfo || data.caHolderManagerInfo.length < 1) { + throw new Error('no data'); + } + const caHolderManagerInfo = data.caHolderManagerInfo[0]; + const managersInfos = caHolderManagerInfo?.managerInfos || []; + + setDeviceAmount(managersInfos.length); + if (!isAmountOnly) { + await getDeviceList(managersInfos); + } + } catch (error) { + setDeviceList([]); + setDeviceAmount(0); + onError?.(error); + } + }, [chainInfo?.chainId, getDeviceList, isAmountOnly, networkInfo.networkType, onError, walletInfo.caHash]); + + // const [load, { data, error, loading }] = useCaHolderManagerInfoLazyQuery({ + // client: getApolloClient(networkInfo.networkType), + // variables: { + // dto: { + // chainId: chainInfo?.chainId, + // caHash: walletInfo.caHash, + // skipCount: 0, + // maxResultCount: 100, + // }, + // }, + // fetchPolicy: 'no-cache', + // }); useEffect(() => { - getDeviceList(); - }, [getDeviceList]); - - useEffectOnce(() => { - isInitLoad && load(); - }); + isInit && refresh(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - return { load, deviceList, refetch, deviceAmount, loading: loading || decodeLoading }; + return { refresh, deviceList, deviceAmount, loading }; }; export const useSetWalletName = () => { diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx index 8c68957c1d..4f4b6a4d4e 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -4,11 +4,9 @@ import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { TextL, TextM, TextS } from 'components/CommonText'; -import { useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; -// import DeviceItem from './components/DeviceItem'; + import { FontStyles } from 'assets/theme/styles'; import { pTd } from 'utils/unit'; -import myEvents from 'utils/deviceEvent'; import { useCurrentDappList } from '@portkey-wallet/hooks/hooks-ca/dapp'; import { useAppDispatch } from 'store/hooks'; @@ -20,22 +18,12 @@ import NoData from 'components/NoData'; import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; -const DeviceList: React.FC = () => { - const { refetch } = useDeviceList(); +const DappList: React.FC = () => { const currentNetwork = useCurrentNetworkInfo(); const dispatch = useAppDispatch(); const dappList = useCurrentDappList(); - useEffect(() => { - const listener = myEvents.refreshDeviceList.addListener(() => { - refetch(); - }); - return () => { - listener.remove(); - }; - }, [refetch]); - return ( { - const { deviceList, refetch, loading: isRefreshing } = useDeviceList(false); + const { + deviceList, + refresh, + loading: isRefreshing, + } = useDeviceList({ + isInit: false, + }); const walletInfo = useCurrentWalletInfo(); const isLoadingRef = useRef(false); const getDeviceList = useCallback(async () => { if (isLoadingRef.current) return; isLoadingRef.current = true; - await refetch(); + await refresh(); isLoadingRef.current = false; - }, [refetch]); + }, [refresh]); useEffectOnce(() => { const timer = setTimeout(() => { diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx index c0b68ae22c..99a0d1c32d 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx @@ -11,7 +11,7 @@ import { useCurrentDappList } from '@portkey-wallet/hooks/hooks-ca/dapp'; import { pTd } from 'utils/unit'; const WalletSecurity: React.FC = () => { - const { deviceAmount } = useDeviceList(); + const { deviceAmount } = useDeviceList({ isAmountOnly: true }); const dappList = useCurrentDappList(); return ( diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx index 47669c0401..1d562da17b 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/index.tsx @@ -16,7 +16,9 @@ export default function WalletSecurity() { const { t } = useTranslation(); const navigate = useNavigate(); const { isNotLessThan768 } = useCommonState(); - const { deviceAmount } = useDeviceList(); + const { deviceAmount } = useDeviceList({ + isAmountOnly: true, + }); const { currentNetwork } = useWalletInfo(); const { dappMap } = useDapp(); const currentDapp = useMemo(() => dappMap[currentNetwork] || [], [currentNetwork, dappMap]); From be341b00025d29fe2c73265b10fe2fa8d4f6d41f Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 14 Jul 2023 11:41:04 +0800 Subject: [PATCH 352/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20useDefaultToken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/wallet.ts | 8 +++ packages/hooks/hooks-ca/chainList.ts | 6 ++ packages/store/store-ca/wallet/type.ts | 8 +++ packages/utils/converter.ts | 4 +- .../app/web/pages/SendTransactions/index.tsx | 69 ++++++++++++------- 5 files changed, 68 insertions(+), 27 deletions(-) diff --git a/packages/constants/constants-ca/wallet.ts b/packages/constants/constants-ca/wallet.ts index 43990bfc0f..d5c4fce79a 100644 --- a/packages/constants/constants-ca/wallet.ts +++ b/packages/constants/constants-ca/wallet.ts @@ -4,3 +4,11 @@ export const DEFAULT_FEE = '0.39'; export const CreateAddressLoading = 'Creating address on the chain...'; export const InitLoginLoading = 'Initiating social recovery'; + +export const DEFAULT_TOKEN = { + address: 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE', + decimals: '8', + imageUrl: 'https://portkey-did.s3.ap-northeast-1.amazonaws.com/img/aelf_token_logo.png', + name: 'AELF', + symbol: 'ELF', +}; diff --git a/packages/hooks/hooks-ca/chainList.ts b/packages/hooks/hooks-ca/chainList.ts index 20955f473a..655ae8b300 100644 --- a/packages/hooks/hooks-ca/chainList.ts +++ b/packages/hooks/hooks-ca/chainList.ts @@ -3,6 +3,7 @@ import { useAppCommonDispatch } from '../index'; import { getChainListAsync } from '@portkey-wallet/store/store-ca/wallet/actions'; import { useCurrentWallet, useOriginChainId, useWallet } from './wallet'; import { ChainId } from '@portkey-wallet/types'; +import { DEFAULT_TOKEN } from '@portkey-wallet/constants/constants-ca/wallet'; export function useChainListFetch() { const { currentNetwork } = useWallet(); @@ -24,6 +25,11 @@ export function useCurrentChain(_chainId?: ChainId) { return useMemo(() => currentChainList?.find(chain => chain.chainId === chainId), [currentChainList, chainId]); } +export function useDefaultToken(_chainId?: ChainId) { + const chainInfo = useCurrentChain(_chainId); + return chainInfo?.defaultToken || DEFAULT_TOKEN; +} + export function useIsValidSuffix() { const currentChainList = useCurrentChainList(); const chainIdArr = useMemo(() => currentChainList?.map(chain => chain.chainId as string) || [], [currentChainList]); diff --git a/packages/store/store-ca/wallet/type.ts b/packages/store/store-ca/wallet/type.ts index 5e40cc9018..942aef15a1 100644 --- a/packages/store/store-ca/wallet/type.ts +++ b/packages/store/store-ca/wallet/type.ts @@ -15,12 +15,20 @@ export enum BaseWalletError { } export const WalletError = Object.assign({}, BaseWalletError, PinErrorMessage); +export type DefaultToken = { + address: string; + decimals: string; + imageUrl: string; + name: string; + symbol: string; +}; export interface ChainItemType { chainId: ChainId; chainName: string; endPoint: string; explorerUrl: string; caContractAddress: string; + defaultToken: DefaultToken; } export interface WalletState { diff --git a/packages/utils/converter.ts b/packages/utils/converter.ts index 29534dc458..66bc35288e 100644 --- a/packages/utils/converter.ts +++ b/packages/utils/converter.ts @@ -133,10 +133,10 @@ export function formatWithCommas({ export const formatAmountShow = ( count: number | BigNumber | string, - decimal = 4, + decimal: string | number = 4, roundingMode: BigNumber.RoundingMode = BigNumber.ROUND_DOWN, ) => { const bigCount = BigNumber.isBigNumber(count) ? count : new BigNumber(count || ''); if (bigCount.isNaN()) return '0'; - return bigCount.decimalPlaces(decimal, roundingMode).toFormat(); + return bigCount.decimalPlaces(typeof decimal !== 'number' ? Number(decimal) : decimal, roundingMode).toFormat(); }; diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index 33f346e6e9..b8efb81b2e 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -1,7 +1,6 @@ -import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentChain, useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { ChainId } from '@portkey-wallet/types'; -import { ZERO } from '@portkey-wallet/constants/misc'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { formatChainInfoToShow, handleErrorMessage } from '@portkey-wallet/utils'; @@ -49,6 +48,7 @@ export default function SendTransactions() { const [errMsg, setErrMsg] = useState(''); const [loading, setLoading] = useState(true); const [tokenDecimals, setTokenDecimals] = useState(0); + const defaultToken = useDefaultToken(payload?.chainId); const isCAContract = useMemo(() => chainInfo?.caContractAddress === payload?.contractAddress, [chainInfo, payload]); const privateKey = useMemo( () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), @@ -61,13 +61,13 @@ export default function SendTransactions() { const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { const value = amountInUsdShow(amount, decimals, symbol); - if (symbol === 'ELF') { + if (symbol === defaultToken.symbol) { return value === '$ 0' ? '<$ 0.01' : value; } else { return value; } }, - [amountInUsdShow], + [amountInUsdShow, defaultToken.symbol], ); const getFee = useCallback( @@ -148,12 +148,12 @@ export default function SendTransactions() { useEffect(() => { const symbol = txParams.paramsOption?.symbol; if (!symbol || !isMainnet) return; - if (symbol === 'ELF') { + if (symbol === defaultToken.symbol) { getTokenPrice(symbol); } else { - getTokensPrice([symbol, 'ELF']); + getTokensPrice([symbol, defaultToken.symbol]); } - }, [getTokenPrice, getTokensPrice, payload, isMainnet, txParams.paramsOption?.symbol]); + }, [getTokenPrice, getTokensPrice, payload, isMainnet, txParams.paramsOption?.symbol, defaultToken.symbol]); const renderAccountInfo = useMemo(() => { if (payload?.contractAddress || typeof payload?.contractAddress !== 'string') return <>; @@ -171,7 +171,7 @@ export default function SendTransactions() { const renderTransfer = useMemo(() => { const { symbol, amount } = txParams.paramsOption || {}; - const decimals = symbol === 'ELF' ? 8 : tokenDecimals; + const decimals = symbol === defaultToken.symbol ? defaultToken.symbol : tokenDecimals; return (
    @@ -180,7 +180,13 @@ export default function SendTransactions() {
    Amount
    - {loading ? : `${formatAmountShow(divDecimals(amount, decimals), 8)}`} + + {loading ? ( + + ) : ( + `${formatAmountShow(divDecimals(amount, decimals), defaultToken.decimals)}` + )} +  {symbol}
    {isMainnet &&
    {formatAmountInUsdShow(amount, decimals, symbol)}
    } @@ -190,44 +196,48 @@ export default function SendTransactions() {
    Transaction Fee
    - {loading ? : `${formatAmountShow(fee, 8)}`} -  ELF + {loading ? : `${formatAmountShow(fee, defaultToken.decimals)}`} +  {defaultToken.symbol}
    - {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    } + {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, defaultToken.symbol)}
    }
    Total (Amount + Transaction Fee)
    - {symbol === 'ELF' ? ( + {symbol === defaultToken.symbol ? (
    {loading ? ( ) : ( - `${formatAmountShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee), 8)}` + `${formatAmountShow(divDecimals(amount, decimals).plus(fee), defaultToken.decimals)}` )}  {symbol}
    {isMainnet && ( -
    - {formatAmountInUsdShow(ZERO.plus(divDecimals(amount, decimals)).plus(fee).toNumber(), 0, symbol)} -
    +
    {formatAmountInUsdShow(divDecimals(amount, decimals).plus(fee).toNumber(), 0, symbol)}
    )}
    ) : ( <>
    - {loading ? : `${formatAmountShow(fee, 8)}`} -  ELF + {loading ? : `${formatAmountShow(fee, defaultToken.decimals)}`} +  {defaultToken.symbol}
    - {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    } + {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, defaultToken.symbol)}
    }
    - {loading ? : `${formatAmountShow(divDecimals(amount, decimals), 8)}`} + + {loading ? ( + + ) : ( + `${formatAmountShow(divDecimals(amount, decimals), defaultToken.decimals)}` + )} +  {symbol}
    {isMainnet &&
    {formatAmountInUsdShow(amount, 0, symbol)}
    } @@ -237,7 +247,16 @@ export default function SendTransactions() {
    ); - }, [txParams.paramsOption, tokenDecimals, loading, isMainnet, formatAmountInUsdShow, fee]); + }, [ + txParams.paramsOption, + defaultToken.symbol, + defaultToken.decimals, + tokenDecimals, + loading, + isMainnet, + formatAmountInUsdShow, + fee, + ]); const renderMessage = useMemo(() => { const params = txParams.paramsOption || {}; @@ -261,14 +280,14 @@ export default function SendTransactions() {
    {loading ? : `${formatAmountShow(fee)}`} -  ELF +  {defaultToken.symbol}
    - {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, 'ELF')}
    } + {isMainnet &&
    {fee === '0' ? '$ 0' : formatAmountInUsdShow(fee, 0, defaultToken.symbol)}
    }
    ); - }, [txParams.paramsOption, loading, fee, isMainnet, formatAmountInUsdShow]); + }, [txParams.paramsOption, loading, fee, defaultToken.symbol, isMainnet, formatAmountInUsdShow]); const sendHandler = useCallback(async () => { try { From 10bc83f53fa66b5cbad66bea87f5b3ead81e8f6f Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 11:44:17 +0800 Subject: [PATCH 353/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20useDevi?= =?UTF-8?q?ceList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/wallet.ts | 2 ++ .../js/pages/My/WalletSecurity/Device/index.tsx | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/hooks/hooks-ca/wallet.ts b/packages/hooks/hooks-ca/wallet.ts index 38885acdf1..eec82881c0 100644 --- a/packages/hooks/hooks-ca/wallet.ts +++ b/packages/hooks/hooks-ca/wallet.ts @@ -138,10 +138,12 @@ export const useDeviceList = (config?: IUseDeviceListConfig) => { await getDeviceList(managersInfos); } } catch (error) { + console.log('useDeviceList: error', error); setDeviceList([]); setDeviceAmount(0); onError?.(error); } + setLoading(false); }, [chainInfo?.chainId, getDeviceList, isAmountOnly, networkInfo.networkType, onError, walletInfo.caHash]); // const [load, { data, error, loading }] = useCaHolderManagerInfoLazyQuery({ diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx index cf27f6356e..9e3f162a69 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Device/index.tsx @@ -11,14 +11,20 @@ import { FontStyles } from 'assets/theme/styles'; import { pTd } from 'utils/unit'; import myEvents from 'utils/deviceEvent'; import useEffectOnce from 'hooks/useEffectOnce'; +import CommonToast from 'components/CommonToast'; const DeviceList: React.FC = () => { + const onError = useCallback(() => { + CommonToast.failError(`Loading failed. Please retry.`); + }, []); + const { deviceList, refresh, loading: isRefreshing, } = useDeviceList({ isInit: false, + onError, }); const walletInfo = useCurrentWalletInfo(); @@ -69,6 +75,7 @@ const DeviceList: React.FC = () => { titleDom={'Login Devices'} safeAreaColor={['blue', 'gray']} containerStyles={pageStyles.pageWrap} + hideTouchable={true} scrollViewProps={{ disabled: true }}> Date: Fri, 14 Jul 2023 13:25:05 +0800 Subject: [PATCH 354/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20bookmark?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/discover/index.ts | 12 +++++ packages/api/api-did/index.ts | 2 + .../components/TabsOverlay/index.tsx | 1 + .../js/components/Updater/index.tsx | 1 + packages/mobile-app-did/js/hooks/discover.ts | 54 +++++++++---------- .../mobile-app-did/js/hooks/useInitData.ts | 13 ++++- .../Bookmark/components/BookmarkItem.tsx | 2 +- .../Bookmark/components/BookmarksSection.tsx | 24 +++++++-- .../js/pages/Discover/Bookmark/index.tsx | 1 + .../DiscoverArchivedSection/index.tsx | 2 +- packages/store/store-ca/discover/slice.ts | 23 +++++++- packages/store/store-ca/discover/type.ts | 4 +- 12 files changed, 100 insertions(+), 39 deletions(-) create mode 100644 packages/api/api-did/discover/index.ts diff --git a/packages/api/api-did/discover/index.ts b/packages/api/api-did/discover/index.ts new file mode 100644 index 0000000000..fa37fba46f --- /dev/null +++ b/packages/api/api-did/discover/index.ts @@ -0,0 +1,12 @@ +export default { + addBookmark: '/api/app/bookmarks', + deleteAllBookmark: { + target: '/api/app/bookmarks', + config: { method: 'DELETE' }, + }, + deleteBookmark: '/api/app/bookmarks/modify', + getBookmarks: { + target: '/api/app/bookmarks', + config: { method: 'GET' }, + }, +} as const; diff --git a/packages/api/api-did/index.ts b/packages/api/api-did/index.ts index f2625750cf..fa32acd77b 100644 --- a/packages/api/api-did/index.ts +++ b/packages/api/api-did/index.ts @@ -9,6 +9,7 @@ import paymentApi from './payment'; import deviceApi from './device'; import messageApi from './message'; import switchApi from './switch'; +import discoverApi from './discover'; import esApi from './es'; import myServer, { DidService } from './server'; @@ -48,6 +49,7 @@ export const EXPAND_APIS = { device: deviceApi, message: messageApi, switch: switchApi, + discover: discoverApi, }; export type BASE_REQ_TYPES = { diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index f256522438..32470dff1b 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -18,6 +18,7 @@ import { useAppCASelector } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import { request } from '@portkey-wallet/api/api-did'; enum HANDLE_TYPE { REFRESH = 'Refresh', diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index c4ad462cde..b67af1f140 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -19,6 +19,7 @@ import { useBuyButton, useDiscoverGroupList, useSocialMediaList } from '@portkey import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; +import { useBookmarkList } from 'hooks/discover'; request.setExceptionManager(exceptionManager); export default function Updater() { diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 22af024754..a9ce8d9da0 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -1,3 +1,4 @@ +import { request } from '@portkey-wallet/api/api-did'; import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { @@ -7,6 +8,8 @@ import { addRecordsItem, createNewTab, initNetworkDiscoverMap, + cleanBookmarkList, + addBookmarkList, } from '@portkey-wallet/store/store-ca/discover/slice'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { useCallback, useEffect, useMemo } from 'react'; @@ -57,41 +60,34 @@ export const useDiscoverWhiteList = () => { return { checkIsInWhiteList, upDateWhiteList }; }; +const DISCOVER_BOOKMARK_MAX_COUNT = 30; + export const useBookmarkList = () => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); const { discoverMap } = useAppCASelector(state => state.discover); - useEffect(() => { - // - }, []); + const refresh = useCallback( + async (pager = 0) => { + const result = await request.discover.getBookmarks({ + params: { + skipCount: pager * DISCOVER_BOOKMARK_MAX_COUNT, + maxResultCount: DISCOVER_BOOKMARK_MAX_COUNT, + }, + }); + if (pager === 0) { + dispatch(cleanBookmarkList(networkType)); + } + dispatch(addBookmarkList({ networkType, list: result.items || [] })); + return result; + }, + [dispatch, networkType], + ); const bookmarkList = useMemo(() => discoverMap?.[networkType]?.bookmarkList || [], [discoverMap, networkType]); - return [ - { - id: 1, - name: 'portkey1', - url: 'https://portkey.finance/', - sortWeight: 1, - }, - { - id: 2, - name: 'portkey2', - url: 'https://portkey.finance/', - sortWeight: 2, - }, - { - id: 3, - name: 'portkey3', - url: 'https://portkey.finance/', - sortWeight: 3, - }, - { - id: 4, - name: 'portkey4', - url: 'https://portkey.finance/', - sortWeight: 4, - }, - ]; + return { + refresh, + bookmarkList, + }; }; diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index 2846c3bc3f..10a086deec 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -8,6 +8,7 @@ import { useAppDispatch } from 'store/hooks'; import { useGetCurrentCAViewContract } from './contract'; import { useGetGuardiansInfoWriteStore, useGetVerifierServers } from './guardian'; import useEffectOnce from './useEffectOnce'; +import { useBookmarkList } from './discover'; export default function useInitData() { const dispatch = useAppDispatch(); @@ -17,6 +18,7 @@ export default function useInitData() { const getGuardiansInfoWriteStore = useGetGuardiansInfoWriteStore(); const isMainNetwork = useIsMainnet(); + const { refresh: loadBookmarkList } = useBookmarkList(); const init = useCallback(async () => { try { @@ -28,6 +30,7 @@ export default function useInitData() { getCurrentCAViewContract(); dispatch(getWalletNameAsync()); dispatch(getSymbolImagesAsync()); + loadBookmarkList(); // getGuardiansInfoWriteStore after getVerifierServers await getVerifierServers(); getGuardiansInfoWriteStore({ @@ -36,7 +39,15 @@ export default function useInitData() { } catch (error) { console.log(error, '====error'); } - }, [caHash, dispatch, getCurrentCAViewContract, getGuardiansInfoWriteStore, getVerifierServers, isMainNetwork]); + }, [ + caHash, + dispatch, + getCurrentCAViewContract, + getGuardiansInfoWriteStore, + getVerifierServers, + isMainNetwork, + loadBookmarkList, + ]); useEffectOnce(() => { // init data after transition animation const timer = setTimeout(init, 500); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 6a0b411bb0..e50acd3910 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -2,7 +2,7 @@ import GStyles from 'assets/theme/GStyles'; import { TextM } from 'components/CommonText'; import Touchable from 'components/Touchable'; import React, { memo, useCallback, useEffect, useRef } from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet, TouchableWithoutFeedback, View } from 'react-native'; import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; import { useBookmark } from '../context'; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index f8e581e395..ae0d5b7b12 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import PageContainer from 'components/PageContainer'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { FlatList, StyleSheet, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; @@ -12,12 +12,24 @@ import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; +import { RefreshControl } from 'react-native-gesture-handler'; -const mockData = ['1', '2', '3', '4']; +const mockData = ['1', '2', '3', '4', '5', '6', '7']; function BookmarksSection() { const [list, setList] = useState(mockData); const [{ isEdit }, dispatch] = useBookmark(); + const [isLoading, setIsLoading] = useState(false); + + const getBookmarkList = useCallback(async (isInit: any) => { + setTimeout(() => { + console.log(123); + + // setIsLoading(true); + }, 100); + + // const { data, maxResultCount = 10, skipCount = 0, totalRecordCount = 0 } = currentActivity; + }, []); const BottomBox = useMemo( () => ( @@ -39,10 +51,14 @@ function BookmarksSection() { _item} renderItem={props => } - onDragEnd={({ data }) => setList(data)} + refreshControl={ + getBookmarkList(true)} refreshing={isLoading} /> + } + onEndReached={() => getBookmarkList(123)} /> {BottomBox} @@ -60,5 +76,5 @@ export default function Container() { const styles = StyleSheet.create({ // remove padding to scale item - containerStyles: { paddingHorizontal: pTd(20), flex: 1, justifyContent: 'space-between' }, + containerStyles: { flex: 1, justifyContent: 'space-between' }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx index 5ce94553d9..d562a72c2e 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx @@ -40,6 +40,7 @@ export default function Bookmark() { diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 78c5863add..eefd8c65ce 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -13,7 +13,7 @@ import navigationService from 'utils/navigationService'; import { ArchivedTabEnum } from 'pages/Discover/types'; export function DiscoverArchivedSection() { - const bookmarkListStore = useBookmarkList(); + const { bookmarkList: bookmarkListStore } = useBookmarkList(); const [index, setIndex] = React.useState( bookmarkListStore.length ? ArchivedTabEnum.Bookmarks : ArchivedTabEnum.History, ); diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index d339e4ada7..79c5b66d5e 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -1,5 +1,5 @@ import { createSlice } from '@reduxjs/toolkit'; -import { IDiscoverStateType, IDiscoverNetworkStateType, ITabItem } from './type'; +import { IDiscoverStateType, IDiscoverNetworkStateType, ITabItem, IBookmarkItem } from './type'; import { NetworkType } from '@portkey-wallet/types'; import { enableMapSet } from 'immer'; import { RECORD_LIMIT, TAB_LIMIT } from '@portkey-wallet/constants/constants-ca/discover'; @@ -120,6 +120,25 @@ export const discoverSlice = createSlice({ [payload]: JSON.parse(JSON.stringify(initNetworkData)), }; }, + cleanBookmarkList: (state, { payload }: { payload: NetworkType }) => { + state.discoverMap = { + ...(state.discoverMap || {}), + [payload]: { + ...(state.discoverMap?.[payload] || {}), + bookmarkList: [], + }, + }; + }, + addBookmarkList: (state, { payload }: { payload: { networkType: NetworkType; list: IBookmarkItem[] } }) => { + const preBookmarkList = state.discoverMap?.[payload.networkType]?.bookmarkList || []; + state.discoverMap = { + ...(state.discoverMap || {}), + [payload.networkType]: { + ...(state.discoverMap?.[payload.networkType] || {}), + bookmarkList: preBookmarkList.concat(payload.list), + }, + }; + }, }, }); @@ -136,6 +155,8 @@ export const { setActiveTab, updateTab, changeDrawerOpenStatus, + cleanBookmarkList, + addBookmarkList, } = discoverSlice.actions; export default discoverSlice; diff --git a/packages/store/store-ca/discover/type.ts b/packages/store/store-ca/discover/type.ts index c5e66d84c3..1516d6afa4 100644 --- a/packages/store/store-ca/discover/type.ts +++ b/packages/store/store-ca/discover/type.ts @@ -8,10 +8,10 @@ export interface ITabItem { } export interface IBookmarkItem { - id: number; + id: string; name: string; url: string; - sortWeight: number; + index: number; } export interface IDiscoverNetworkStateType { From d79cf927278768ec909e8142aa51ae705e5ff2b7 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 13:45:26 +0800 Subject: [PATCH 355/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20records=20and=20?= =?UTF-8?q?bookmarks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/BookmarkItem.tsx | 2 +- .../Bookmark/components/BookmarksSection.tsx | 33 ++++-- .../Bookmark/components/RecordsSection.tsx | 100 ++++++++++++++++++ .../bookmarksContext.tsx} | 0 .../Bookmark/context/recordsContext.tsx | 45 ++++++++ .../{RecordsPage => Records}/index.tsx | 0 .../DiscoverArchivedSection/index.tsx | 2 +- .../components/NoDiscoverData/index.tsx | 23 +++- 8 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx rename packages/mobile-app-did/js/pages/Discover/Bookmark/{context.tsx => context/bookmarksContext.tsx} (100%) create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/context/recordsContext.tsx rename packages/mobile-app-did/js/pages/Discover/{RecordsPage => Records}/index.tsx (100%) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 7d3a661c12..371bfccc8c 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -5,7 +5,7 @@ import React, { memo, useCallback, useEffect, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; -import { useBookmark } from '../context'; +import { useBookmark } from '../context/bookmarksContext'; import usePrevious from 'hooks/usePrevious'; import { BGStyles, FontStyles } from 'assets/theme/styles'; import Svg from 'components/Svg'; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index d56a6c6f92..d096800aa4 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -3,7 +3,7 @@ import PageContainer from 'components/PageContainer'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; -import { BookmarkProvider, setEdit, useBookmark } from '../context'; +import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; import { BGStyles, FontStyles } from 'assets/theme/styles'; @@ -12,6 +12,7 @@ import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; +import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; @@ -24,8 +25,13 @@ function BookmarksSection() { {isEdit ? ( <> - dispatch(setEdit(false))} title="Done" type="primary" /> + ) : ( dispatch(setEdit(true))} title="Edit" type="primary" /> @@ -35,14 +41,16 @@ function BookmarksSection() { [dispatch, isEdit], ); + if (list.length === 0) return ; + return ( } - ListFooterComponent={} + ListHeaderComponent={} + ListFooterComponent={} keyExtractor={_item => _item} renderItem={props => } onDragEnd={({ data }) => setList(data)} @@ -67,13 +75,26 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'space-between', borderRadius: pTd(6), - paddingVertical: pTd(8), }, listWrap: { - paddingVertical: pTd(8), paddingHorizontal: pTd(20), }, buttonGroupWrap: { paddingHorizontal: pTd(20), }, + deleteAll: { + marginTop: pTd(10), + }, + headerBlank: { + borderTopLeftRadius: pTd(6), + borderTopRightRadius: pTd(6), + height: pTd(8), + backgroundColor: defaultColors.bg1, + }, + footerBlank: { + borderBottomLeftRadius: pTd(6), + borderBottomRightRadius: pTd(6), + height: pTd(8), + backgroundColor: defaultColors.bg1, + }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx new file mode 100644 index 0000000000..d096800aa4 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -0,0 +1,100 @@ +import React, { useCallback, useMemo, useState } from 'react'; +import PageContainer from 'components/PageContainer'; +import DraggableFlatList from 'react-native-draggable-flatlist'; +import GStyles from 'assets/theme/GStyles'; +import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; +import CommonButton from 'components/CommonButton'; +import BookmarkItem from './BookmarkItem'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import { ArchivedTabEnum } from 'pages/Discover/types'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import fonts from 'assets/theme/fonts'; +import { TextM } from 'components/CommonText'; +import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; + +const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; + +function BookmarksSection() { + const [list, setList] = useState(mockData); + const [{ isEdit }, dispatch] = useBookmark(); + + const BottomBox = useMemo( + () => ( + + {isEdit ? ( + <> + dispatch(setEdit(false))} title="Done" type="primary" /> + + + ) : ( + dispatch(setEdit(true))} title="Edit" type="primary" /> + )} + + ), + [dispatch, isEdit], + ); + + if (list.length === 0) return ; + + return ( + + + } + ListFooterComponent={} + keyExtractor={_item => _item} + renderItem={props => } + onDragEnd={({ data }) => setList(data)} + /> + + {BottomBox} + + ); +} + +export default function Container() { + return ( + + + + ); +} + +const styles = StyleSheet.create({ + // remove padding to scale item + containerStyles: { + flex: 1, + justifyContent: 'space-between', + borderRadius: pTd(6), + }, + listWrap: { + paddingHorizontal: pTd(20), + }, + buttonGroupWrap: { + paddingHorizontal: pTd(20), + }, + deleteAll: { + marginTop: pTd(10), + }, + headerBlank: { + borderTopLeftRadius: pTd(6), + borderTopRightRadius: pTd(6), + height: pTd(8), + backgroundColor: defaultColors.bg1, + }, + footerBlank: { + borderBottomLeftRadius: pTd(6), + borderBottomRightRadius: pTd(6), + height: pTd(8), + backgroundColor: defaultColors.bg1, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/context.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/context/bookmarksContext.tsx similarity index 100% rename from packages/mobile-app-did/js/pages/Discover/Bookmark/context.tsx rename to packages/mobile-app-did/js/pages/Discover/Bookmark/context/bookmarksContext.tsx diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/context/recordsContext.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/context/recordsContext.tsx new file mode 100644 index 0000000000..3e64738fb2 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/context/recordsContext.tsx @@ -0,0 +1,45 @@ +import { basicActions } from 'contexts/utils'; +import React, { ReactNode, createContext, useContext, useMemo, useReducer } from 'react'; + +export interface RecordState { + isEdit?: boolean; +} +export const RecordContext = createContext<[RecordState, React.Dispatch] | []>([]); + +export function useBookmark() { + return useContext(RecordContext) as [RecordState, React.Dispatch]; +} + +export enum RecordActions { + setEdit = 'setEdit', +} + +// reducer +export function reducer(state: RecordState, { type, payload }: any) { + switch (type) { + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +} + +// actions +export const basicBookmarkActions = { + setEdit: (isEdit: boolean) => basicActions(RecordActions.setEdit, { isEdit }), +}; + +export const { setEdit } = basicBookmarkActions; + +// provider +export const INITIAL_STATE = {}; + +export function RecordsProvider({ children }: { children?: ReactNode | undefined }) { + const [state, dispatch] = useReducer(reducer, INITIAL_STATE); + + const value = useMemo(() => [state, dispatch], [state]); + return ( + ]}>{children} + ); +} diff --git a/packages/mobile-app-did/js/pages/Discover/RecordsPage/index.tsx b/packages/mobile-app-did/js/pages/Discover/Records/index.tsx similarity index 100% rename from packages/mobile-app-did/js/pages/Discover/RecordsPage/index.tsx rename to packages/mobile-app-did/js/pages/Discover/Records/index.tsx diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index bbe41433e4..8ee800fc0d 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -78,7 +78,7 @@ export function DiscoverArchivedSection() { )} - {bookmarkList?.length === 0 ? ( + {recordsList?.length === 0 ? ( ) : ( diff --git a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx index 185bab1682..62449c5dc5 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx @@ -6,20 +6,28 @@ import Svg from 'components/Svg'; import GStyles from 'assets/theme/GStyles'; import { BGStyles, FontStyles } from 'assets/theme/styles'; import { pTd } from 'utils/unit'; +import { defaultColors } from 'assets/theme'; export interface INoDiscoverDataProps { type?: 'noBookmarks' | 'noRecords'; + size?: 'small' | 'large'; + location?: 'top' | 'center'; + backgroundColor?: string; } const NoDiscoverData = (props: INoDiscoverDataProps) => { - const { type = 'noBookmarks' } = props; + const { type = 'noBookmarks', size = 'small', location = 'center', backgroundColor = defaultColors.bg1 } = props; const iconName = type === 'noBookmarks' ? 'no-bookmarks' : 'no-records'; const noDataText = type === 'noBookmarks' ? 'No Bookmarks' : 'No Records'; + const wrapStyle: any = { + backgroundColor, + }; + return ( - - - {noDataText} + + + {noDataText} ); }; @@ -30,4 +38,11 @@ const styles = StyleSheet.create({ icon: { marginBottom: pTd(8), }, + topNoDataStyle: { + justifyContent: 'flex-start', + paddingTop: pTd(142), + }, + largeText: { + fontSize: pTd(16), + }, }); From 12598f1781dc01710043afc93af3fc9193c6d1f8 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 14 Jul 2023 14:19:45 +0800 Subject: [PATCH 356/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20adjust=20get=20ic?= =?UTF-8?q?on=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web-extension-did/app/web/content.ts b/packages/web-extension-did/app/web/content.ts index 18ef694926..7841e9eec2 100644 --- a/packages/web-extension-did/app/web/content.ts +++ b/packages/web-extension-did/app/web/content.ts @@ -13,7 +13,7 @@ import { ResponseCode, } from '@portkey/provider-types'; import { generateErrorResponse, generateNormalResponse } from '@portkey/provider-utils'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { isMethodsBase, isMethodsUnimplemented } from '@portkey/providers'; import { isMethodsWalletMessage } from 'messages/utils'; /** @@ -164,7 +164,7 @@ class Content { contentListener(input: IRequestParams) { const URL = getUrl(); - const icon = `https://api.faviconkit.com/${getHost(URL.href)}/50`; + const icon = getFaviconUrl(URL.href, 50); const message = Object.assign({}, input, { hostname: URL.hostname, From 26c56fc6e4c64b9c3450e27ef3ad9a6d0d877ee3 Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 14 Jul 2023 14:46:52 +0800 Subject: [PATCH 357/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20github=20a?= =?UTF-8?q?ction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 26 ++++ jest.config.js | 58 ++++---- packages/hooks/hooks-ca/misc.test.ts | 132 ++++++++++++++++++ packages/store/store-ca/misc/index.test.ts | 28 ---- packages/store/store-ca/recent/index.test.ts | 2 +- .../store/reducers/loginCache/index.test.ts | 8 +- test/data/miscState.ts | 30 ++++ 7 files changed, 223 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 packages/hooks/hooks-ca/misc.test.ts delete mode 100644 packages/store/store-ca/misc/index.test.ts create mode 100644 test/data/miscState.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..0ab56f4573 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Unit Test + +on: + push: + branches: + - master + pull_request: {} + +jobs: + test: + name: Coverage + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 16 + cache: yarn + - run: yarn install + - run: yarn run test-cov + - name: Reporter + uses: vebr/jest-lcov-reporter@v0.2.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + lcov-file: coverage/lcov.info diff --git a/jest.config.js b/jest.config.js index 7b00a25cc8..3ab4b94184 100644 --- a/jest.config.js +++ b/jest.config.js @@ -20,7 +20,15 @@ module.exports = { '!**/node_modules/**', '!**/*.test.{ts,tsx}', ], - coverageReporters: ['json-summary'], + coverageReporters: ['text', 'json-summary', 'json', 'lcov'], + coverageThreshold: { + global: { + branches: 95, + functions: 95, + lines: 95, + statements: 95, + }, + }, moduleNameMapper: { '\\.(css|less)$': 'identity-obj-proxy', }, @@ -57,7 +65,7 @@ module.exports = { '/packages/web-extension-did/app/web/controllers/approval/*.test.ts', '/packages/web-extension-did/app/web/controllers/methodController/*.test.ts', '/packages/web-extension-did/app/web/utils/errorHandle.test.ts', - '/packages/web-extension-did/app/web/utils/device/*.test.ts', + '/packages/web-extension-did/app/web/utils/device.test.ts', ], testEnvironment: 'jsdom', transform: { @@ -79,28 +87,28 @@ module.exports = { }, coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/hooks-ca/', '/utils/'], }, - { - displayName: 'mobile-app-did', - roots: ['/packages/mobile-app-did'], - preset: 'react-native', - transform: { - '^.+\\.(ts|tsx)$': [`ts-jest`, { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }], - }, - transformIgnorePatterns: ['/node_modules/(?!((jest-)?react-native|@react-native(-community)?)/)'], - testEnvironment: 'react-native', - globals: { - __DEV__: true, - }, - setupFilesAfterEnv: ['./packages/mobile-app-did/jest-setup/mockAsyncStorage.ts'], - moduleNameMapper: { - '^react$': '/node_modules/react', - '^utils/(.*)$': '/packages/mobile-app-did/js/utils/$1', - '^store/(.*)$': '/packages/mobile-app-did/js/store/$1', - '^@portkey-wallet/store/(.*)$': '/packages/store/$1', - store: '/packages/mobile-app-did/js/store/index', - '^dapp/(.*)$': '/packages/mobile-app-did/js/dapp/$1', - }, - coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/Test/', '/utils/'], - }, + // { + // displayName: 'mobile-app-did', + // roots: ['/packages/mobile-app-did'], + // preset: 'react-native', + // transform: { + // '^.+\\.(ts|tsx)$': [`ts-jest`, { isolatedModules: true, tsconfig: './packages/mobile-app-did/tsconfig.json' }], + // }, + // transformIgnorePatterns: ['/node_modules/(?!((jest-)?react-native|@react-native(-community)?)/)'], + // testEnvironment: 'react-native', + // globals: { + // __DEV__: true, + // }, + // setupFilesAfterEnv: ['./packages/mobile-app-did/jest-setup/mockAsyncStorage.ts'], + // moduleNameMapper: { + // '^react$': '/node_modules/react', + // '^utils/(.*)$': '/packages/mobile-app-did/js/utils/$1', + // '^store/(.*)$': '/packages/mobile-app-did/js/store/$1', + // '^@portkey-wallet/store/(.*)$': '/packages/store/$1', + // store: '/packages/mobile-app-did/js/store/index', + // '^dapp/(.*)$': '/packages/mobile-app-did/js/dapp/$1', + // }, + // coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/Test/', '/utils/'], + // }, ], }; diff --git a/packages/hooks/hooks-ca/misc.test.ts b/packages/hooks/hooks-ca/misc.test.ts new file mode 100644 index 0000000000..036032990c --- /dev/null +++ b/packages/hooks/hooks-ca/misc.test.ts @@ -0,0 +1,132 @@ +import { MiscState } from '../../../test/data/miscState'; +import { renderHookWithProvider } from '../../../test/utils/render'; +import { setupStore } from '../../../test/utils/setup'; +import { useMisc, useSetLocalPhoneCountryCode, usePhoneCountryCode } from './misc'; +import { useAppCommonDispatch } from '../index'; +import { renderHook } from '@testing-library/react'; +import { CountryItem } from '@portkey-wallet/types/types-ca/country'; +import * as MiscActions from '@portkey-wallet/store/store-ca/misc/actions'; +import * as indexHook from '.'; +import * as networkHook from '@portkey-wallet/hooks/hooks-ca/network'; +import { MainnetNetworkInfo, TestnetNetworkInfo } from '../../../test/data/networkState'; +import { DefaultCountry } from '@portkey-wallet/constants/constants-ca/country'; + +jest.mock('../index', () => ({ + useAppCommonDispatch: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + jest.restoreAllMocks(); +}); + +describe('useMisc', () => { + test('get misc data successfully', () => { + const { result } = renderHookWithProvider(useMisc, setupStore(MiscState)); + + expect(result.current).toHaveProperty('phoneCountryCodeListChainMap'); + expect(result.current).toHaveProperty('defaultPhoneCountryCode'); + expect(result.current.defaultPhoneCountryCode).toEqual(MiscState.misc.defaultPhoneCountryCode); + }); + test('failed to get misc data', () => { + const { result } = renderHookWithProvider(useMisc, setupStore({})); + + expect(result.current).toBeUndefined(); + }); +}); + +describe('useSetLocalPhoneCountryCode', () => { + it('should call useAppCommonDispatch with setLocalPhoneCountryCodeAction when called', () => { + const dispatchMock = jest.fn(); + const countryItemMock: CountryItem = { country: 'US', code: '+1', iso: 'US' }; + + (useAppCommonDispatch as jest.Mock).mockReturnValue(dispatchMock); + jest.spyOn(MiscActions, 'setLocalPhoneCountryCodeAction').mockImplementation(jest.fn()); + + const { result } = renderHook(() => useSetLocalPhoneCountryCode()); + const setLocalPhoneCountryCode = result.current; + + setLocalPhoneCountryCode(countryItemMock); + + expect(useAppCommonDispatch).toHaveBeenCalled(); + expect(MiscActions.setLocalPhoneCountryCodeAction).toHaveBeenCalled(); + }); +}); + +describe('usePhoneCountryCode', () => { + test('should return correct values', () => { + const dispatchMock = jest.fn(); + const useAppCASelectorMock = jest.fn().mockReturnValue(MiscState.misc); + + jest.spyOn(indexHook, 'useAppCASelector').mockImplementation(useAppCASelectorMock); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + (useAppCommonDispatch as jest.Mock).mockReturnValue(dispatchMock); + jest.spyOn(MiscActions, 'getPhoneCountryCode').mockImplementation(jest.fn()); + + const { result } = renderHook(() => usePhoneCountryCode(true)); + + expect(result.current.phoneCountryCodeList).toHaveLength(8); + expect(result.current.phoneCountryCodeIndex).toHaveLength(7); + expect(result.current.localPhoneCountryCode).toEqual(MiscState.misc.localPhoneCountryCode); + }); + + test('not localPhoneCountryCode, and return correct values', () => { + const dispatchMock = jest.fn(); + const useAppCASelectorMock = jest.fn().mockReturnValue({ + phoneCountryCodeListChainMap: [MiscState.misc.phoneCountryCodeListChainMap?.MAIN], + defaultPhoneCountryCode: MiscState.misc.defaultPhoneCountryCode, + }); + + jest.spyOn(indexHook, 'useAppCASelector').mockImplementation(useAppCASelectorMock); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + (useAppCommonDispatch as jest.Mock).mockReturnValue(dispatchMock); + jest.spyOn(MiscActions, 'getPhoneCountryCode').mockImplementation(jest.fn()); + + const { result } = renderHook(() => usePhoneCountryCode(true)); + + expect(result.current.phoneCountryCodeList).toHaveLength(0); + expect(result.current.phoneCountryCodeIndex).toHaveLength(0); + expect(result.current.localPhoneCountryCode).toEqual(MiscState.misc.defaultPhoneCountryCode); + }); + + test('not localPhoneCountryCode and defaultPhoneCountryCode, and return correct values', () => { + const dispatchMock = jest.fn(); + const useAppCASelectorMock = jest.fn().mockReturnValue({ + phoneCountryCodeListChainMap: [MiscState.misc.phoneCountryCodeListChainMap?.MAIN], + }); + + jest.spyOn(indexHook, 'useAppCASelector').mockImplementation(useAppCASelectorMock); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + (useAppCommonDispatch as jest.Mock).mockReturnValue(dispatchMock); + jest.spyOn(MiscActions, 'getPhoneCountryCode').mockImplementation(jest.fn()); + + const { result } = renderHook(() => usePhoneCountryCode(true)); + + expect(result.current.phoneCountryCodeList).toHaveLength(0); + expect(result.current.phoneCountryCodeIndex).toHaveLength(0); + expect(result.current.localPhoneCountryCode).toEqual(DefaultCountry); + }); + + test('should dispatch getPhoneCountryCode only for current networkType when not initializing', () => { + const dispatchMock = jest.fn(); + const useAppCASelectorMock = jest.fn().mockReturnValue(MiscState.misc); + + jest.spyOn(indexHook, 'useAppCASelector').mockImplementation(useAppCASelectorMock); + jest.spyOn(networkHook, 'useCurrentNetworkInfo').mockReturnValue(TestnetNetworkInfo); + jest.spyOn(networkHook, 'useNetworkList').mockReturnValue([TestnetNetworkInfo, MainnetNetworkInfo]); + (useAppCommonDispatch as jest.Mock).mockReturnValue(dispatchMock); + jest.spyOn(MiscActions, 'getPhoneCountryCode').mockImplementation(jest.fn()); + + const { result, rerender } = renderHook(usePhoneCountryCode); + + rerender(true); + + expect(result.current.phoneCountryCodeList).toHaveLength(8); + expect(result.current.phoneCountryCodeIndex).toHaveLength(7); + expect(result.current.localPhoneCountryCode).toEqual(MiscState.misc.localPhoneCountryCode); + }); +}); diff --git a/packages/store/store-ca/misc/index.test.ts b/packages/store/store-ca/misc/index.test.ts deleted file mode 100644 index f6e1b6785b..0000000000 --- a/packages/store/store-ca/misc/index.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { miscSlice } from './slice'; -import { request } from '@portkey-wallet/api/api-did'; -import { configureStore } from '@reduxjs/toolkit'; -import { setUpdateVersionInfo } from './actions'; - -jest.mock('@portkey-wallet/api/api-did'); -const reducer = miscSlice.reducer; -describe('setUpdateVersionInfo', () => { - test('setUpdateVersionInfo will set versionInfo', async () => { - const returnValue = { - content: 'content', - downloadUrl: 'http://download.com', - isForceUpdate: true, - targetVersion: 'v1.2.0', - title: 'title', - }; - jest.mocked(request.wallet.pullNotify).mockResolvedValueOnce(returnValue); - const payload = { - deviceId: 'deviceId', - deviceType: 1, - appId: '10001', - appVersion: 'v1.1.0', - }; - const store = configureStore({ reducer }); - await store.dispatch(setUpdateVersionInfo(payload)); - expect(store.getState().versionInfo).toEqual(returnValue); - }); -}); diff --git a/packages/store/store-ca/recent/index.test.ts b/packages/store/store-ca/recent/index.test.ts index 8f912c24a9..c69c7fd75d 100644 --- a/packages/store/store-ca/recent/index.test.ts +++ b/packages/store/store-ca/recent/index.test.ts @@ -30,7 +30,7 @@ describe('fetchRecentListAsync', () => { chainId: 'AELF' as ChainId, caAddress: 'caAddress', address: 'address', - addressChainId: 'AELF', + addressChainId: 'AELF' as ChainId, transactionTime: '123456789', name: '', addresses: [], diff --git a/packages/web-extension-did/app/web/store/reducers/loginCache/index.test.ts b/packages/web-extension-did/app/web/store/reducers/loginCache/index.test.ts index a1258c692b..101f9b4528 100644 --- a/packages/web-extension-did/app/web/store/reducers/loginCache/index.test.ts +++ b/packages/web-extension-did/app/web/store/reducers/loginCache/index.test.ts @@ -3,7 +3,6 @@ import { setLoginAccountAction, setWalletInfoAction, resetLoginInfoAction, setRe import { VerificationType } from '@portkey-wallet/types/verifier'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import { ChainId } from '@portkey-wallet/types'; -import { DefaultCountry } from '@portkey-wallet/constants/constants-ca/country'; const reducer = loginSlice.reducer; const mockState = {}; @@ -73,11 +72,6 @@ describe('resetLoginInfoAction', () => { test('State only contains countryCode property', () => { const mockState = {}; const res = reducer(mockState, resetLoginInfoAction()); - expect(res).toEqual({ - countryCode: { - index: DefaultCountry.country[0], - country: DefaultCountry, - }, - }); + expect(res).toEqual({}); }); }); diff --git a/test/data/miscState.ts b/test/data/miscState.ts new file mode 100644 index 0000000000..6603f1df20 --- /dev/null +++ b/test/data/miscState.ts @@ -0,0 +1,30 @@ +import { MiscState as IMiscState } from '@portkey-wallet/store/store-ca/misc/types'; + +export const MiscState: { misc: IMiscState } = { + misc: { + phoneCountryCodeListChainMap: { + MAIN: [ + { country: 'China', code: '86', iso: 'CN' }, + { country: 'Denmark', code: '45', iso: 'DK' }, + { country: 'France', code: '33', iso: 'FR' }, + { country: 'Hong Kong', code: '852', iso: 'HK' }, + { country: 'Mexico', code: '52', iso: 'MX' }, + { country: 'Singapore', code: '65', iso: 'SG' }, + { country: 'United Kingdom', code: '44', iso: 'GB' }, + { country: 'United States', code: '1', iso: 'US' }, + ], + TESTNET: [ + { country: 'China', code: '86', iso: 'CN' }, + { country: 'Denmark', code: '45', iso: 'DK' }, + { country: 'France', code: '33', iso: 'FR' }, + { country: 'Hong Kong', code: '852', iso: 'HK' }, + { country: 'Mexico', code: '52', iso: 'MX' }, + { country: 'Singapore', code: '65', iso: 'SG' }, + { country: 'United Kingdom', code: '44', iso: 'GB' }, + { country: 'United States', code: '1', iso: 'US' }, + ], + }, + defaultPhoneCountryCode: { country: 'Singapore', code: '65', iso: 'SG' }, + localPhoneCountryCode: { country: 'United States', code: '1', iso: 'US' }, + }, +}; From 2c5c7b3df78434cc01459aab90b125572776391d Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 15:28:09 +0800 Subject: [PATCH 358/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?sign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index 8ac7d8885c..e7a493b5a4 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -72,7 +72,7 @@ export const useHandleAchSell = () => { const publicKey = (account.keyPair as any).getPublic('hex'); const message = SparkMD5.hash(`${params.orderId}${rawResult.data}`); - const signature = AElf.wallet.sign(message, account.keyPair).toString('hex'); + const signature = AElf.wallet.sign(Buffer.from(message).toString('hex'), account.keyPair).toString('hex'); return { rawTransaction: rawResult.data, publicKey, From adb14f9c25bb97510aea601d2c4381ac5cd9b0f7 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 14 Jul 2023 16:01:32 +0800 Subject: [PATCH 359/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20closeSwipeable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/BookmarkItem.tsx | 30 ++++++++++++------ .../Bookmark/components/BookmarksSection.tsx | 31 ++++++++++--------- .../mobile-app-did/js/utils/deviceEvent.ts | 16 +++++++++- 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 371bfccc8c..2e573a3a9a 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -1,7 +1,7 @@ import GStyles from 'assets/theme/GStyles'; import { TextM, TextS } from 'components/CommonText'; import Touchable from 'components/Touchable'; -import React, { memo, useCallback, useEffect, useRef } from 'react'; +import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; @@ -13,6 +13,8 @@ import { pTd } from 'utils/unit'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; import { defaultColors } from 'assets/theme'; +import myEvents from 'utils/deviceEvent'; +import useEffectOnce from 'hooks/useEffectOnce'; export default memo( function BookmarkItem(props: RenderItemParams) { @@ -23,7 +25,10 @@ export default memo( useEffect(() => { if (!isEdit && isEdit !== preIsEdit) swipeableRef.current?.close(); }, [preIsEdit, isEdit]); - + useEffectOnce(() => { + const listener = myEvents.bookmark.closeSwipeable.addListener(() => swipeableRef.current?.close()); + return () => listener.remove(); + }); const renderUnderlayLeft = useCallback( () => ( swipeableRef.current?.close()}> @@ -32,7 +37,19 @@ export default memo( ), [], ); - + const EditDom = useMemo(() => { + if (!isEdit) return null; + return ( + { + myEvents.bookmark.closeSwipeable.emit(); + swipeableRef.current?.open(OpenDirection.LEFT); + }}> + + + ); + }, [isEdit]); return ( - {isEdit && ( - swipeableRef.current?.open(OpenDirection.LEFT)}> - - - )} - + {EditDom} diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index d096800aa4..6b4212dedb 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import PageContainer from 'components/PageContainer'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { FlatList, StyleSheet, TouchableNativeFeedback, TouchableOpacity, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; @@ -13,6 +13,7 @@ import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; +import myEvents from 'utils/deviceEvent'; const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; @@ -44,20 +45,22 @@ function BookmarksSection() { if (list.length === 0) return ; return ( - - - } - ListFooterComponent={} - keyExtractor={_item => _item} - renderItem={props => } - onDragEnd={({ data }) => setList(data)} - /> + + + + } + ListFooterComponent={} + keyExtractor={_item => _item} + renderItem={props => } + onDragEnd={({ data }) => setList(data)} + /> + + {BottomBox} - {BottomBox} - + ); } diff --git a/packages/mobile-app-did/js/utils/deviceEvent.ts b/packages/mobile-app-did/js/utils/deviceEvent.ts index eb842ba643..edb485ed30 100644 --- a/packages/mobile-app-did/js/utils/deviceEvent.ts +++ b/packages/mobile-app-did/js/utils/deviceEvent.ts @@ -16,6 +16,8 @@ const EventList = [ 'refreshMyContactDetailInfo', ] as const; +const BookmarkEventList = ['closeSwipeable'] as const; + // eslint-disable-next-line no-new-func const eventsServer = new Function(); @@ -38,12 +40,24 @@ eventsServer.prototype.addListener = function (eventType: string, listener: (dat eventsServer.prototype.parseEvent('base', EventList); +eventsServer.prototype.parseEvent('bookmark', BookmarkEventList); + +export type BookmarkEventsTypes = { + [x in typeof BookmarkEventList[number]]: { + emit: (...params: any[]) => void; + addListener: (listener: (data: any) => void) => EmitterSubscription; + }; +}; + export type MyEventsTypes = { [x in typeof EventList[number]]: { emit: (...params: any[]) => void; addListener: (listener: (data: any) => void) => EmitterSubscription; }; +} & { + bookmark: BookmarkEventsTypes; }; -const myEvents = eventsServer.prototype.base; + +const myEvents = { ...eventsServer.prototype.base, bookmark: eventsServer.prototype.bookmark }; export default myEvents as unknown as MyEventsTypes; From eb0a5eed5436586df3a656a36ed6d49e2e51cd82 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 14 Jul 2023 16:09:36 +0800 Subject: [PATCH 360/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20closeSwipeable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/BookmarksSection.tsx | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 6b4212dedb..3b59a8e8ab 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -14,6 +14,7 @@ import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; import myEvents from 'utils/deviceEvent'; +import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; @@ -41,26 +42,24 @@ function BookmarksSection() { ), [dispatch, isEdit], ); - + const closeSwipeable = useLockCallback(() => myEvents.bookmark.closeSwipeable.emit(), []); if (list.length === 0) return ; - return ( - - - - } - ListFooterComponent={} - keyExtractor={_item => _item} - renderItem={props => } - onDragEnd={({ data }) => setList(data)} - /> - - {BottomBox} + + + } + ListFooterComponent={} + keyExtractor={_item => _item} + renderItem={props => } + onDragEnd={({ data }) => setList(data)} + /> - + {BottomBox} + ); } From 7149913ea538ab3e7c18705e150c9c834bea6431 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 16:30:26 +0800 Subject: [PATCH 361/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20bookmark?= =?UTF-8?q?=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/assets/image/svgs.js | 2 +- .../js/assets/image/svgs/bookmark.svg | 4 + .../js/assets/image/svgs/bookmarked.svg | 4 + .../components/TabsOverlay/index.tsx | 92 +++++++++++++++---- packages/mobile-app-did/js/hooks/discover.ts | 4 +- .../Bookmark/components/BookmarkItem.tsx | 5 +- .../Bookmark/components/BookmarksSection.tsx | 27 +++--- 7 files changed, 98 insertions(+), 40 deletions(-) create mode 100755 packages/mobile-app-did/js/assets/image/svgs/bookmark.svg create mode 100755 packages/mobile-app-did/js/assets/image/svgs/bookmarked.svg diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index 68235915a9..29062109c0 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/bookmark.svg b/packages/mobile-app-did/js/assets/image/svgs/bookmark.svg new file mode 100755 index 0000000000..ee16c40011 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/bookmark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/bookmarked.svg b/packages/mobile-app-did/js/assets/image/svgs/bookmarked.svg new file mode 100755 index 0000000000..1e3b1ab210 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/bookmarked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index 32470dff1b..c1bd073c4f 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -1,11 +1,11 @@ -import React, { Dispatch, SetStateAction, useCallback } from 'react'; +import React, { Dispatch, SetStateAction, useCallback, useMemo, useRef, useState } from 'react'; import OverlayModal from 'components/OverlayModal'; import { StyleSheet, TouchableOpacity, View, Share } from 'react-native'; import { TextL, TextS } from 'components/CommonText'; import { defaultColors } from 'assets/theme'; import fonts from 'assets/theme/fonts'; import { pTd } from 'utils/unit'; -import Svg from 'components/Svg'; +import Svg, { IconName } from 'components/Svg'; import { useLanguage } from 'i18n/hooks'; import GStyles from 'assets/theme/GStyles'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; @@ -19,6 +19,7 @@ import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; import { request } from '@portkey-wallet/api/api-did'; +import { useBookmarkList } from 'hooks/discover'; enum HANDLE_TYPE { REFRESH = 'Refresh', @@ -27,15 +28,10 @@ enum HANDLE_TYPE { CLOSE = 'Close', CANCEL = 'Cancel', SWITCH = 'Switch', + BOOKMARK = 'Bookmark', + UN_BOOKMARK = 'Delete Bookmark', } -const handleArray = [ - { title: HANDLE_TYPE.REFRESH, icon: 'refresh1' }, - { title: HANDLE_TYPE.COPY, icon: 'copy1' }, - { title: HANDLE_TYPE.SHARE, icon: 'share' }, - { title: HANDLE_TYPE.SWITCH, icon: 'switch' }, -] as const; - const BrowserEditModal = ({ browserInfo, activeWebViewRef, @@ -49,6 +45,9 @@ const BrowserEditModal = ({ }) => { const { t } = useLanguage(); const { activeTabId } = useAppCASelector(state => state.discover); + const { bookmarkList } = useBookmarkList(); + const isBookmarkLoading = useRef(false); + const [bookmark, setBookmark] = useState(bookmarkList.find(item => item.url === browserInfo?.url)); const handleUrl = useCallback( async (type: HANDLE_TYPE) => { @@ -85,13 +84,54 @@ const BrowserEditModal = ({ case HANDLE_TYPE.SWITCH: if (!activeTabId) return; - activeWebviewScreenShot(); OverlayModal.hide(); setPreActiveTabId(Number(browserInfo?.id)); + break; + case HANDLE_TYPE.BOOKMARK: + if (!isBookmarkLoading.current) { + isBookmarkLoading.current = true; + try { + const result = await request.discover.addBookmark({ + params: { + name: browserInfo?.name || '', + url: browserInfo?.url || '', + }, + }); + console.log('result', result); + // TODO: add Bookmark + setBookmark({} as any); + CommonToast.success('Added successfully'); + } catch (error) { + CommonToast.failError('Added failed'); + } + isBookmarkLoading.current = false; + } break; + case HANDLE_TYPE.UN_BOOKMARK: + if (!isBookmarkLoading.current) { + isBookmarkLoading.current = true; + try { + await request.discover.deleteBookmark({ + params: { + deleteInfos: [ + { + id: bookmark?.id, + index: bookmark?.index, + }, + ], + }, + }); + CommonToast.success('Deleted successfully'); + setBookmark(undefined); + } catch (error) { + CommonToast.failError('Deleted failed'); + } + isBookmarkLoading.current = false; + } + break; default: break; } @@ -105,9 +145,27 @@ const BrowserEditModal = ({ activeTabId, activeWebviewScreenShot, setPreActiveTabId, + bookmark?.id, + bookmark?.index, ], ); + const handleArray: Array<{ + title: HANDLE_TYPE; + icon: IconName; + }> = useMemo(() => { + return [ + { title: HANDLE_TYPE.REFRESH, icon: 'refresh1' }, + { title: HANDLE_TYPE.COPY, icon: 'copy1' }, + { title: HANDLE_TYPE.SHARE, icon: 'share' }, + { title: HANDLE_TYPE.SWITCH, icon: 'switch' }, + { + title: bookmark ? HANDLE_TYPE.UN_BOOKMARK : HANDLE_TYPE.BOOKMARK, + icon: bookmark ? 'bookmarked' : 'bookmark', + }, + ]; + }, [bookmark]); + return ( @@ -124,10 +182,7 @@ const BrowserEditModal = ({ {handleArray.map((ele, index) => ( - handleUrl(ele.title)}> + handleUrl(ele.title)}> @@ -187,18 +242,19 @@ const styles = StyleSheet.create({ display: 'flex', flexWrap: 'wrap', flexDirection: 'row', + justifyContent: 'space-between', }, listItem: { - marginRight: pTd(34), + width: pTd(60), + height: pTd(92), overflow: 'hidden', - }, - listItemNoMarginRight: { - marginRight: 0, + alignItems: 'center', }, svgWrap: { backgroundColor: defaultColors.bg1, borderRadius: pTd(6), overflow: 'hidden', + width: pTd(52), }, itemTitle: { textAlign: 'center', diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 13b4ab3eba..ec0f679853 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -102,9 +102,7 @@ export const useRecordsList = (isReverse: boolean): ITabItem[] => { const { discoverMap } = useAppCASelector(state => state.discover); const list = useMemo(() => { - return isReverse - ? (discoverMap?.[networkType]?.recordsList as ITabItem[]).reverse() - : (discoverMap?.[networkType]?.recordsList as ITabItem[]); + return []; }, [discoverMap, isReverse, networkType]); return list || []; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index ca37cc31de..78225d7d75 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -27,7 +27,7 @@ export default memo( const renderUnderlayLeft = useCallback( () => ( swipeableRef.current?.close()}> - Delete + Delete ), [], @@ -81,10 +81,9 @@ const styles = StyleSheet.create({ marginContainer: {}, underlayLeftBox: { flex: 1, - paddingRight: pTd(12), flexDirection: 'row', alignItems: 'center', - marginHorizontal: 16, + paddingHorizontal: pTd(16), justifyContent: 'flex-end', backgroundColor: defaultColors.bg17, color: defaultColors.font1, diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 3b0df582ea..f4f389e06d 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -15,7 +15,8 @@ import { TextM } from 'components/CommonText'; import { RefreshControl } from 'react-native-gesture-handler'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; -const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; +// const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9']; +const mockData = ['1', '2', '3']; function BookmarksSection() { const [list, setList] = useState(mockData); @@ -57,13 +58,12 @@ function BookmarksSection() { return ( - + } - ListFooterComponent={} keyExtractor={_item => _item} renderItem={props => } refreshControl={ @@ -94,23 +94,20 @@ const styles = StyleSheet.create({ }, listWrap: { paddingHorizontal: pTd(20), + flex: 1, }, buttonGroupWrap: { + marginTop: pTd(16), paddingHorizontal: pTd(20), }, deleteAll: { marginTop: pTd(10), }, - headerBlank: { - borderTopLeftRadius: pTd(6), - borderTopRightRadius: pTd(6), - height: pTd(8), - backgroundColor: defaultColors.bg1, - }, - footerBlank: { - borderBottomLeftRadius: pTd(6), - borderBottomRightRadius: pTd(6), - height: pTd(8), + flatListWrap: { height: '100%', borderRadius: pTd(6), overflow: 'hidden' }, + flatListContent: { backgroundColor: defaultColors.bg1, + paddingVertical: pTd(8), + borderRadius: pTd(6), + overflow: 'hidden', }, }); From c05653b1f2ba0791c06a55926b0020e4158af584 Mon Sep 17 00:00:00 2001 From: ykx Date: Fri, 14 Jul 2023 16:40:38 +0800 Subject: [PATCH 362/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20misc=20ut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.js | 3 +- packages/hooks/hooks-ca/misc.test.ts | 52 +++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index 3ab4b94184..5a67f2c7bf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,11 +11,12 @@ module.exports = { '**/packages/web-extension-did/app/web/store/reducers/**/*.{ts,tsx}', '**/packages/web-extension-did/app/web/controllers/approval/*.ts', '**/packages/web-extension-did/app/web/controllers/methodController/*.ts', - '**/packages/web-extension-did/app/web/utils/device.ts', + // '**/packages/web-extension-did/app/web/utils/device.ts', '**/packages/web-extension-did/app/web/utils/errorHandle.ts', '**/packages/web-extension-did/app/web/hooks/useActiveLockStatus.ts', '**/packages/web-extension-did/app/web/hooks/useCaInfoOnChain.ts', '**/packages/web-extension-did/app/web/hooks/useNetwork.ts', + '!**/packages/store/store-ca/{cms,dapp,discover,misc}/*.{ts,tsx}', '!**/packages/hooks/hooks-ca/contact.{ts,tsx}', '!**/node_modules/**', '!**/*.test.{ts,tsx}', diff --git a/packages/hooks/hooks-ca/misc.test.ts b/packages/hooks/hooks-ca/misc.test.ts index 036032990c..6e6e8fbba9 100644 --- a/packages/hooks/hooks-ca/misc.test.ts +++ b/packages/hooks/hooks-ca/misc.test.ts @@ -1,7 +1,7 @@ import { MiscState } from '../../../test/data/miscState'; import { renderHookWithProvider } from '../../../test/utils/render'; import { setupStore } from '../../../test/utils/setup'; -import { useMisc, useSetLocalPhoneCountryCode, usePhoneCountryCode } from './misc'; +import { useMisc, useSetLocalPhoneCountryCode, usePhoneCountryCode, useIsScanQRCode } from './misc'; import { useAppCommonDispatch } from '../index'; import { renderHook } from '@testing-library/react'; import { CountryItem } from '@portkey-wallet/types/types-ca/country'; @@ -10,6 +10,7 @@ import * as indexHook from '.'; import * as networkHook from '@portkey-wallet/hooks/hooks-ca/network'; import { MainnetNetworkInfo, TestnetNetworkInfo } from '../../../test/data/networkState'; import { DefaultCountry } from '@portkey-wallet/constants/constants-ca/country'; +import signalrDid from '@portkey-wallet/socket/socket-did'; jest.mock('../index', () => ({ useAppCommonDispatch: jest.fn(), @@ -130,3 +131,52 @@ describe('usePhoneCountryCode', () => { expect(result.current.localPhoneCountryCode).toEqual(MiscState.misc.localPhoneCountryCode); }); }); + +describe('useIsScanQRCode', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it('should return false by default', () => { + const { result } = renderHook(() => useIsScanQRCode(undefined)); + expect(result.current).toBe(false); + }); + + it('should set isScanQRCode to true when onScanLogin is called', () => { + jest.spyOn(signalrDid, 'stop').mockImplementation(jest.fn()); + jest.spyOn(signalrDid, 'onScanLogin').mockImplementation( + jest.fn(callback => { + callback?.({} as any); + return { + remove: jest.fn(), + }; + }), + ); + jest.spyOn(signalrDid, 'doOpen').mockImplementation(jest.fn()); + + const { result } = renderHook(() => useIsScanQRCode('clientId')); + + expect(result.current).toBe(true); + }); + + it('signalrDid.stop throw error, and catch error', async () => { + jest.spyOn(signalrDid, 'stop').mockImplementation( + jest.fn(() => { + throw Error; + }), + ); + jest.spyOn(signalrDid, 'onScanLogin').mockImplementation( + jest.fn(callback => { + callback?.({} as any); + return { + remove: jest.fn(), + }; + }), + ); + jest.spyOn(signalrDid, 'doOpen').mockRejectedValue({ error: 'signalrDid.doOpen' }); + + const { result } = renderHook(() => useIsScanQRCode('clientId')); + expect(result.current).toBe(true); + }); +}); From 7bba70f27eddd8e4b418c1b8e125f9f699108f7b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 17:27:04 +0800 Subject: [PATCH 363/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20records?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/discover.ts | 16 ++-- .../Bookmark/components/BookmarksSection.tsx | 11 +-- .../Bookmark/components/RecordsSection.tsx | 84 ++++++++++++------- .../js/pages/Discover/Bookmark/index.tsx | 5 +- .../DiscoverArchivedSection/index.tsx | 2 +- 5 files changed, 68 insertions(+), 50 deletions(-) diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 9e01e0b713..9cb51b3175 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -63,15 +63,15 @@ export const useDiscoverWhiteList = () => { }; export const useBookmarkList = () => { - const { networkType } = useCurrentNetworkInfo(); - const dispatch = useAppCommonDispatch(); - const { discoverMap } = useAppCASelector(state => state.discover); + // const { networkType } = useCurrentNetworkInfo(); + // const dispatch = useAppCommonDispatch(); + // const { discoverMap } = useAppCASelector(state => state.discover); useEffect(() => { // }, []); - const bookmarkList = useMemo(() => discoverMap?.[networkType]?.bookmarkList || [], [discoverMap, networkType]); + // const bookmarkList = useMemo(() => discoverMap?.[networkType]?.bookmarkList || [], [discoverMap, networkType]); return [ { @@ -101,14 +101,14 @@ export const useBookmarkList = () => { ]; }; -export const useRecordsList = (isReverse: boolean): ITabItem[] => { +export const useRecordsList = (isReverse = true): ITabItem[] => { const { networkType } = useCurrentNetworkInfo(); const { discoverMap } = useAppCASelector(state => state.discover); const list = useMemo(() => { - return isReverse - ? (discoverMap?.[networkType]?.recordsList as ITabItem[]).reverse() - : (discoverMap?.[networkType]?.recordsList as ITabItem[]); + const recordList = JSON.parse(JSON.stringify(discoverMap?.[networkType]?.recordsList || ([] as ITabItem[]))); + + return isReverse ? recordList.reverse() : recordList; }, [discoverMap, isReverse, networkType]); return list || []; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index d096800aa4..2c73e7e086 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -1,17 +1,13 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import PageContainer from 'components/PageContainer'; +import React, { useMemo, useState } from 'react'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; -import { BGStyles, FontStyles } from 'assets/theme/styles'; -import { ArchivedTabEnum } from 'pages/Discover/types'; +import { FontStyles } from 'assets/theme/styles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; -import fonts from 'assets/theme/fonts'; -import { TextM } from 'components/CommonText'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; @@ -49,6 +45,7 @@ function BookmarksSection() { } ListHeaderComponent={} ListFooterComponent={} keyExtractor={_item => _item} diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index d096800aa4..ce05a0bd29 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -1,27 +1,44 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import PageContainer from 'components/PageContainer'; +import React, { useCallback, useMemo } from 'react'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; -import { BGStyles, FontStyles } from 'assets/theme/styles'; -import { ArchivedTabEnum } from 'pages/Discover/types'; +import { FontStyles } from 'assets/theme/styles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; -import fonts from 'assets/theme/fonts'; -import { TextM } from 'components/CommonText'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; +import { useRecordsList } from 'hooks/discover'; +import { removeRecordsItems, clearRecordsList } from '@portkey-wallet/store/store-ca/discover/slice'; +import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; +// const mockData = ['1', '2', '3']; function BookmarksSection() { - const [list, setList] = useState(mockData); + // const [list, setList] = useState(mockData); const [{ isEdit }, dispatch] = useBookmark(); - const BottomBox = useMemo( - () => ( + const { networkType } = useCurrentNetworkInfo(); + const storeDispatch = useAppCommonDispatch(); + const recordList = useRecordsList(true); + + const onDelete = useCallback( + (item: ITabItem) => { + storeDispatch(removeRecordsItems({ ids: [item.id], networkType })); + }, + [networkType, storeDispatch], + ); + + const onDeleteAll = useCallback(() => { + storeDispatch(clearRecordsList({ networkType })); + }, [networkType, storeDispatch]); + const BottomBox = useMemo(() => { + if (recordList?.length === 0) return null; + + return ( {isEdit ? ( <> @@ -31,29 +48,29 @@ function BookmarksSection() { titleStyle={FontStyles.font12} type="outline" title="Delete All" + onPress={onDeleteAll} /> ) : ( dispatch(setEdit(true))} title="Edit" type="primary" /> )} - ), - [dispatch, isEdit], - ); - - if (list.length === 0) return ; + ); + }, [dispatch, isEdit, onDeleteAll, recordList?.length]); return ( } - ListFooterComponent={} - keyExtractor={_item => _item} - renderItem={props => } - onDragEnd={({ data }) => setList(data)} + data={recordList} + style={styles.flatListStyle} + contentContainerStyle={[styles.flatListContent, recordList.length === 0 && styles.noData]} + ListEmptyComponent={} + keyExtractor={_item => String(_item.id)} + renderItem={props => } + onDragEnd={({ data }) => { + console.log('===', data); + }} /> {BottomBox} @@ -85,16 +102,19 @@ const styles = StyleSheet.create({ deleteAll: { marginTop: pTd(10), }, - headerBlank: { - borderTopLeftRadius: pTd(6), - borderTopRightRadius: pTd(6), - height: pTd(8), - backgroundColor: defaultColors.bg1, + + flatListStyle: { + height: '100%', + borderRadius: pTd(6), + overflow: 'hidden', }, - footerBlank: { - borderBottomLeftRadius: pTd(6), - borderBottomRightRadius: pTd(6), - height: pTd(8), + flatListContent: { backgroundColor: defaultColors.bg1, + borderRadius: pTd(6), + paddingVertical: pTd(8), + overflow: 'hidden', + }, + noData: { + paddingVertical: 0, }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx index 978fd76a3b..8c43300409 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx @@ -9,6 +9,7 @@ import { pTd } from 'utils/unit'; import fonts from 'assets/theme/fonts'; import { TextM } from 'components/CommonText'; import BookmarksSection from './components/BookmarksSection'; +import RecordsSection from './components/RecordsSection'; import { RouteProp, useRoute } from '@react-navigation/native'; type TabItemType = { @@ -26,7 +27,7 @@ const tabList: TabItemType[] = [ { name: 'Records', type: ArchivedTabEnum.History, - component: <>, + component: , }, ]; @@ -71,7 +72,7 @@ export default function Bookmark() { } const styles = StyleSheet.create({ - containerStyles: { ...GStyles.paddingArg(0), flex: 1, backgroundColor: defaultColors.bg4 }, + containerStyles: { ...GStyles.paddingArg(0), flex: 1, backgroundColor: defaultColors.bg6 }, tabHeader: { width: pTd(214), diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 8ee800fc0d..744aa43e55 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -46,7 +46,7 @@ export function DiscoverArchivedSection() { setIndex(ArchivedTabEnum.History)} style={index === ArchivedTabEnum.History ? FontStyles.weight500 : FontStyles.font3}> - History + Records From a0cb045ea13beadd1e842e349b9492824aa64535 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Fri, 14 Jul 2023 17:41:02 +0800 Subject: [PATCH 364/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20device=20list=20?= =?UTF-8?q?&=20token=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/server.ts | 5 +++++ packages/hooks/hooks-ca/wallet.ts | 2 -- .../ManageDevices/DeviceDetail/index.tsx | 13 ++++++++----- .../ManageDevices/Devices/index.tsx | 15 +++++++++++---- .../web-extension-did/app/web/style/common.css | 4 +++- packages/web-extension-did/package.json | 2 +- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/api/api-did/server.ts b/packages/api/api-did/server.ts index 0798715b4d..9e4ee2c719 100644 --- a/packages/api/api-did/server.ts +++ b/packages/api/api-did/server.ts @@ -49,6 +49,11 @@ export class DidService extends ServiceInit { }; } this.refreshTokenConfig = config; + try { + this.getConnectToken(); + } catch (error) { + console.log(error); + } }; send = async (base: BaseConfig, config?: RequestConfig, reCount = 0): Promise => { try { diff --git a/packages/hooks/hooks-ca/wallet.ts b/packages/hooks/hooks-ca/wallet.ts index eec82881c0..1ff5916198 100644 --- a/packages/hooks/hooks-ca/wallet.ts +++ b/packages/hooks/hooks-ca/wallet.ts @@ -5,8 +5,6 @@ import { CAInfoType } from '@portkey-wallet/types/types-ca/wallet'; import { WalletState } from '@portkey-wallet/store/store-ca/wallet/type'; import { useCurrentNetworkInfo } from './network'; import { useCurrentChain, useCurrentChainList } from './chainList'; -import { useCaHolderManagerInfoLazyQuery } from '@portkey-wallet/graphql/contract/__generated__/hooks/caHolderManagerInfo'; -import { getApolloClient } from '@portkey-wallet/graphql/contract/apollo'; import { request } from '@portkey-wallet/api/api-did'; import { useAppCommonDispatch } from '../index'; import { setWalletNameAction } from '@portkey-wallet/store/store-ca/wallet/actions'; diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/DeviceDetail/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/DeviceDetail/index.tsx index 912a868273..c881f8cb82 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/DeviceDetail/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/DeviceDetail/index.tsx @@ -1,7 +1,7 @@ -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router'; -import { useAppDispatch, useCommonState } from 'store/Provider/hooks'; +import { useAppDispatch, useCommonState, useLoading } from 'store/Provider/hooks'; import { resetUserGuardianStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; import useGuardianList from 'hooks/useGuardianList'; import { useCurrentWallet, useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; @@ -16,11 +16,11 @@ export default function DeviceDetail() { const { t } = useTranslation(); const navigate = useNavigate(); const { isPrompt, isNotLessThan768 } = useCommonState(); - + const { setLoading } = useLoading(); const { managerAddress } = useParams(); const dispatch = useAppDispatch(); const userGuardianList = useGuardianList(); - const { deviceList } = useDeviceList(); + const { deviceList, loading } = useDeviceList(); const { walletInfo } = useCurrentWallet(); const device = useMemo( () => deviceList.filter((d) => d?.managerAddress === managerAddress)?.[0] || {}, @@ -30,7 +30,10 @@ export default function DeviceDetail() { if (device.managerAddress) return walletInfo.address === device?.managerAddress; return true; }, [device, walletInfo]); - + useEffect(() => { + setLoading(loading); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loading]); const handleDelete = useCallback(async () => { dispatch( setLoginAccountAction({ diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx index 0ac4bdbc1f..ddf7bfe668 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ManageDevices/Devices/index.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next'; -import { IDeviceList, useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { IDeviceItem, useDeviceList } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCallback, useEffect, useState } from 'react'; import { DeviceItemType } from '@portkey-wallet/types/types-ca/device'; import { useNavigate } from 'react-router'; @@ -7,12 +7,19 @@ import { useLoading } from 'store/Provider/hooks'; import DevicesPopup from './Popup'; import DevicesPrompt from './Prompt'; import { useCommonState } from 'store/Provider/hooks'; +import { message } from 'antd'; export default function Devices() { const { t } = useTranslation(); const navigate = useNavigate(); - const { deviceList, refetch, loading } = useDeviceList(); - const [devices, setDevices] = useState([]); + const onError = useCallback(() => { + message.error(`Loading failed. Please retry.`); + }, []); + const { deviceList, refresh, loading } = useDeviceList({ + isInit: false, + onError, + }); + const [devices, setDevices] = useState([]); const { setLoading } = useLoading(); const { isNotLessThan768 } = useCommonState(); @@ -26,7 +33,7 @@ export default function Devices() { useEffect(() => { setLoading(true); - refetch(); + refresh(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/packages/web-extension-did/app/web/style/common.css b/packages/web-extension-did/app/web/style/common.css index d3e101bb23..2f6bc5173c 100644 --- a/packages/web-extension-did/app/web/style/common.css +++ b/packages/web-extension-did/app/web/style/common.css @@ -18,7 +18,9 @@ body.en-language-wrapper { font-family: Roboto-Regular; } -#root { + +#root, +#portkey-ui-root { overflow-y: auto; height: 100vh; } diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index e9a1520d21..ab652bfc9b 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -20,7 +20,7 @@ "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", "@portkey/providers": "1.0.1-alpha.0", - "@portkey/did-ui-react": "1.0.0-alpha.8", + "@portkey/did-ui-react": "1.0.8", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", From b2eaab7134ec33d35ea61e969567faeeb3d73983 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 17:46:55 +0800 Subject: [PATCH 365/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20modal=20scroll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/OverlayModal/index.tsx | 12 ++++++++++-- .../js/dapp/components/ConnectOverlay/index.tsx | 1 - .../js/dapp/components/SignOverlay/index.tsx | 1 - .../js/dapp/components/TransactionOverlay/index.tsx | 1 - 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/components/OverlayModal/index.tsx b/packages/mobile-app-did/js/components/OverlayModal/index.tsx index 2ca1b4d36d..9a34beac31 100644 --- a/packages/mobile-app-did/js/components/OverlayModal/index.tsx +++ b/packages/mobile-app-did/js/components/OverlayModal/index.tsx @@ -39,10 +39,12 @@ export function OverlayTransformView({ containerStyle, children, enabledNestScrollView, + onCloseRequest, }: { containerStyle?: ViewStyleType; children: ReactNode; enabledNestScrollView?: boolean; + onCloseRequest?: OverlayModalProps['onCloseRequest']; }) { return ( { - translateY > 50 && OverlayModal.hide(); + if (translateY > 50) { + onCloseRequest?.(); + OverlayModal.hide(); + } }}> {children} @@ -99,7 +104,10 @@ export default class OverlayModal extends React.Component { elements.push(v); }} {...props}> - + {component} diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 3ad4ea6f3b..4c226c6a3c 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -105,7 +105,6 @@ const ConnectModal = (props: ConnectModalType) => { export const showConnectModal = (props: ConnectModalType) => { OverlayModal.show(, { position: 'bottom', - enabledNestScrollView: true, onCloseRequest: props.onReject, }); }; diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx index 885a21aaa6..6b432714a9 100644 --- a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -60,7 +60,6 @@ const SignModal = (props: SignModalPropsType) => { export const showSignModal = (props: SignModalPropsType) => { OverlayModal.show(, { position: 'bottom', - enabledNestScrollView: true, onCloseRequest: props.onReject, }); }; diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index b06270174b..9751b14996 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -418,7 +418,6 @@ const ConnectModal = (props: TransactionModalPropsType) => { export const showTransactionModal = (props: TransactionModalPropsType) => { OverlayModal.show(, { position: 'bottom', - enabledNestScrollView: true, onCloseRequest: props.onReject, }); }; From 7cd0d4674f638a807917819438f705fc938cae29 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 17:47:42 +0800 Subject: [PATCH 366/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20records?= =?UTF-8?q?=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 9 ++++-- .../Bookmark/components/BookmarkItem.tsx | 29 +++++++++++++------ .../Bookmark/components/RecordsSection.tsx | 9 +++++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 875f0ae14a..76ffa71105 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -15,7 +15,10 @@ type BrowserTabProps = { const Options: CaptureOptions = { quality: 0.2, format: 'jpg' }; -const BrowserTab = forwardRef(function BrowserTab({ isHidden, uri, onLoadEnd }, forward) { +const BrowserTab = forwardRef(function BrowserTab( + { isHidden, uri, onLoadEnd, autoApprove }, + forward, +) { const viewRef = useRef(null); const webViewRef = useRef(null); const progressbarRef = useRef(null); @@ -37,12 +40,12 @@ const BrowserTab = forwardRef(function BrowserTab( }, [isHidden, options]); const onPageLoadEnd = useCallback(() => { - if (!isApproved.current) { + if (!isApproved.current && autoApprove) { isApproved.current = true; webViewRef.current?.autoApprove(); } onLoadEnd?.(); - }, [onLoadEnd]); + }, [onLoadEnd, autoApprove]); return ( & { onDelete: (item: { id: string; [key: string]: any }) => void }; export default memo( - function BookmarkItem(props: RenderItemParams) { - const { drag, isActive, item } = props; + function BookmarkItem(props: BookmarkItemType) { + const { item, onDelete } = props; const swipeableRef = useRef(null); const [{ isEdit }] = useBookmark(); const preIsEdit = usePrevious(isEdit); @@ -31,7 +34,11 @@ export default memo( }); const renderUnderlayLeft = useCallback( () => ( - swipeableRef.current?.close()}> + { + onDelete(item); + }}> Delete ), @@ -53,10 +60,10 @@ export default memo( return ( {EditDom} - + - - dddddd + + {item.url} {/* @@ -84,7 +95,7 @@ export default memo( ); }, - (prevProps: RenderItemParams, nextProps: RenderItemParams) => { + (prevProps: RenderItemParams, nextProps: RenderItemParams) => { return prevProps.item === nextProps.item && prevProps.isActive === nextProps.isActive; }, ); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index ce05a0bd29..8e171f13d1 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -14,6 +14,7 @@ import { removeRecordsItems, clearRecordsList } from '@portkey-wallet/store/stor import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import ActionSheet from 'components/ActionSheet'; // const mockData = ['1', '2', '3']; @@ -33,7 +34,13 @@ function BookmarksSection() { ); const onDeleteAll = useCallback(() => { - storeDispatch(clearRecordsList({ networkType })); + ActionSheet.alert({ + title2: `Delete all records?`, + buttons: [ + { title: 'Cancel', type: 'outline' }, + { title: 'Confirm', type: 'primary', onPress: () => storeDispatch(clearRecordsList({ networkType })) }, + ], + }); }, [networkType, storeDispatch]); const BottomBox = useMemo(() => { if (recordList?.length === 0) return null; From e3843cdba35245e52ed51430c2bf29c09f4a46cb Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Fri, 14 Jul 2023 18:14:20 +0800 Subject: [PATCH 367/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20nextAnimation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/BookmarksSection.tsx | 21 ++++++++++++++++--- packages/mobile-app-did/js/utils/animation.ts | 21 +++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 packages/mobile-app-did/js/utils/animation.ts diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 3b59a8e8ab..1a8a04de61 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import PageContainer from 'components/PageContainer'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; -import { FlatList, StyleSheet, TouchableNativeFeedback, TouchableOpacity, View } from 'react-native'; +import { FlatList, LayoutAnimation, StyleSheet, TouchableNativeFeedback, TouchableOpacity, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; @@ -15,6 +15,7 @@ import { TextM } from 'components/CommonText'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; import myEvents from 'utils/deviceEvent'; import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; +import { nextAnimation } from 'utils/animation'; const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; @@ -27,7 +28,14 @@ function BookmarksSection() { {isEdit ? ( <> - dispatch(setEdit(false))} title="Done" type="primary" /> + { + nextAnimation(); + dispatch(setEdit(false)); + }} + title="Done" + type="primary" + /> ) : ( - dispatch(setEdit(true))} title="Edit" type="primary" /> + { + nextAnimation(); + dispatch(setEdit(true)); + }} + title="Edit" + type="primary" + /> )} ), diff --git a/packages/mobile-app-did/js/utils/animation.ts b/packages/mobile-app-did/js/utils/animation.ts new file mode 100644 index 0000000000..4a965bb8a2 --- /dev/null +++ b/packages/mobile-app-did/js/utils/animation.ts @@ -0,0 +1,21 @@ +import { isIos } from '@portkey-wallet/utils/mobile/device'; +import { LayoutAnimation, LayoutAnimationConfig, UIManager } from 'react-native'; + +if (!isIos) UIManager.setLayoutAnimationEnabledExperimental?.(true); + +const BaseAnimationConfig = { + duration: 400, + create: { + duration: 200, + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + update: { + type: LayoutAnimation.Types.spring, + springDamping: 200, + }, +}; + +export function nextAnimation(config?: LayoutAnimationConfig) { + LayoutAnimation.configureNext(config || BaseAnimationConfig); +} From 05118c222f240ccbc09327109805defdbf7906fb Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 18:42:52 +0800 Subject: [PATCH 368/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20bookmarkLi?= =?UTF-8?q?st?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TabsOverlay/index.tsx | 6 +- packages/mobile-app-did/js/hooks/discover.ts | 26 ++- .../Bookmark/components/BookmarkItem.tsx | 38 ++-- .../Bookmark/components/BookmarksSection.tsx | 174 +++++++++++++++--- .../js/pages/Discover/Bookmark/index.tsx | 2 +- 5 files changed, 191 insertions(+), 55 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index c1bd073c4f..38f246292c 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -95,13 +95,11 @@ const BrowserEditModal = ({ try { const result = await request.discover.addBookmark({ params: { - name: browserInfo?.name || '', + name: browserInfo?.name || browserInfo?.url || '', url: browserInfo?.url || '', }, }); - console.log('result', result); - // TODO: add Bookmark - setBookmark({} as any); + setBookmark(result); CommonToast.success('Added successfully'); } catch (error) { CommonToast.failError('Added failed'); diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index ec0f679853..731b01a773 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -12,7 +12,7 @@ import { addBookmarkList, addAutoApproveItem, } from '@portkey-wallet/store/store-ca/discover/slice'; -import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { useCallback, useEffect, useMemo } from 'react'; export const useIsDrawerOpen = () => useAppCASelector(state => state.discover.isDrawerOpen); @@ -65,34 +65,40 @@ export const useDiscoverWhiteList = () => { return { checkIsInWhiteList, upDateWhiteList }; }; -const DISCOVER_BOOKMARK_MAX_COUNT = 30; - export const useBookmarkList = () => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); const { discoverMap } = useAppCASelector(state => state.discover); + const clean = useCallback(() => { + dispatch(cleanBookmarkList(networkType)); + }, [dispatch, networkType]); + const refresh = useCallback( - async (pager = 0) => { + async (skipCount: number, maxResultCount: number) => { const result = await request.discover.getBookmarks({ params: { - skipCount: pager * DISCOVER_BOOKMARK_MAX_COUNT, - maxResultCount: DISCOVER_BOOKMARK_MAX_COUNT, + skipCount, + maxResultCount, }, }); - if (pager === 0) { - dispatch(cleanBookmarkList(networkType)); + if (skipCount === 0) { + clean(); } dispatch(addBookmarkList({ networkType, list: result.items || [] })); - return result; + return result as { + items: IBookmarkItem[]; + totalCount: number; + }; }, - [dispatch, networkType], + [clean, dispatch, networkType], ); const bookmarkList = useMemo(() => discoverMap?.[networkType]?.bookmarkList || [], [discoverMap, networkType]); return { refresh, + clean, bookmarkList, }; }; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 780865935c..90e8b699be 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -15,10 +15,16 @@ import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; import { defaultColors } from 'assets/theme'; import myEvents from 'utils/deviceEvent'; import useEffectOnce from 'hooks/useEffectOnce'; +import { IBookmarkItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; + +type BookmarkItemProps = RenderItemParams & { + onDelete: (item: T) => void; +}; export default memo( - function BookmarkItem(props: RenderItemParams) { - const { drag, isActive, item } = props; + function BookmarkItem(props: BookmarkItemProps) { + const { drag, isActive, item, onDelete } = props; const swipeableRef = useRef(null); const [{ isEdit }] = useBookmark(); const preIsEdit = usePrevious(isEdit); @@ -29,14 +35,21 @@ export default memo( const listener = myEvents.bookmark.closeSwipeable.addListener(() => swipeableRef.current?.close()); return () => listener.remove(); }); + + const deleteItem = useCallback(() => { + swipeableRef.current?.close(); + onDelete(item); + }, [item, onDelete]); + const renderUnderlayLeft = useCallback( () => ( - swipeableRef.current?.close()}> + Delete ), - [], + [deleteItem], ); + const EditDom = useMemo(() => { if (!isEdit) return null; return ( @@ -50,13 +63,14 @@ export default memo( ); }, [isEdit]); + return ( {EditDom} - + - - dddddd + + + {item.url} + {/* @@ -84,8 +100,8 @@ export default memo( ); }, - (prevProps: RenderItemParams, nextProps: RenderItemParams) => { - return prevProps.item === nextProps.item && prevProps.isActive === nextProps.isActive; + (prevProps: RenderItemParams, nextProps: RenderItemParams) => { + return prevProps.item.id === nextProps.item.id && prevProps.isActive === nextProps.isActive; }, ); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 2654967863..76a2ef555b 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -1,80 +1,194 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import PageContainer from 'components/PageContainer'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import DraggableFlatList from 'react-native-draggable-flatlist'; -import GStyles from 'assets/theme/GStyles'; -import { FlatList, StyleSheet, TouchableNativeFeedback, TouchableOpacity, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; import BookmarkItem from './BookmarkItem'; -import { BGStyles, FontStyles } from 'assets/theme/styles'; -import { ArchivedTabEnum } from 'pages/Discover/types'; +import { FontStyles } from 'assets/theme/styles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; -import fonts from 'assets/theme/fonts'; -import { TextM } from 'components/CommonText'; import { RefreshControl } from 'react-native-gesture-handler'; import NoDiscoverData from 'pages/Discover/components/NoDiscoverData'; import myEvents from 'utils/deviceEvent'; import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; +import { useBookmarkList } from 'hooks/discover'; -// const mockData = ['1', '2', '3', '4', '5', '6', '7', '8', '9']; -const mockData = ['1', '2', '3']; +import { IBookmarkItem } from '@portkey-wallet/store/store-ca/discover/type'; +import CommonToast from 'components/CommonToast'; +import { request } from '@portkey-wallet/api/api-did'; +import Loading from 'components/Loading'; +import ActionSheet from 'components/ActionSheet'; +import { TextL, TextXL } from 'components/CommonText'; + +const DISCOVER_BOOKMARK_MAX_COUNT = 30; function BookmarksSection() { - const [list, setList] = useState(mockData); const [{ isEdit }, dispatch] = useBookmark(); const [isLoading, setIsLoading] = useState(false); - const getBookmarkList = useCallback(async (isInit: any) => { - setTimeout(() => { - console.log(123); + const { refresh, clean } = useBookmarkList(); + const [list, setList] = useState([]); + const [editList, setEditList] = useState([]); + const deleteList = useRef([]); + const pagerRef = useRef({ + skipCount: 0, + maxCount: DISCOVER_BOOKMARK_MAX_COUNT, + totalCount: 0, + }); + + const loadingRef = useRef(false); + const getBookmarkList = useCallback( + async (isInit: boolean) => { + if (isEdit || loadingRef.current) return; + loadingRef.current = true; + + let { skipCount } = pagerRef.current; + const { maxCount, totalCount } = pagerRef.current; + if (isInit) { + skipCount = 0; + } + if (skipCount >= totalCount && totalCount !== 0) { + loadingRef.current = false; + return; + } + + if (isInit) { + setIsLoading(true); + } + try { + console.log('getBookmarkList', skipCount, maxCount); + const result = await refresh(skipCount, maxCount); + console.log('getBookmarkList result', result.totalCount); + if (isInit) { + setList(result.items); + } else { + setList(pre => [...pre, ...result.items]); + } + pagerRef.current.skipCount = result.items.length + skipCount; + pagerRef.current.totalCount = result.totalCount; + } catch (error) { + CommonToast.failError(error); + } + + if (isInit) { + setIsLoading(false); + } + loadingRef.current = false; + }, + [isEdit, refresh], + ); + const getBookmarkListRef = useRef(getBookmarkList); + getBookmarkListRef.current = getBookmarkList; - // setIsLoading(true); + useEffect(() => { + const timer = setTimeout(() => { + getBookmarkListRef.current(true); }, 100); + return () => clearTimeout(timer); + }, [clean]); - // const { data, maxResultCount = 10, skipCount = 0, totalRecordCount = 0 } = currentActivity; + const onItemDelete = useCallback((item: IBookmarkItem) => { + setEditList(pre => pre.filter(_item => _item.id !== item.id)); + deleteList.current.push(item); }, []); + const onEdit = useCallback(() => { + deleteList.current = []; + setEditList([...list]); + dispatch(setEdit(true)); + }, [dispatch, list]); + + const onDone = useCallback(async () => { + if (deleteList.current.length > 0) { + Loading.show(); + try { + await request.discover.deleteBookmark({ + params: { + deleteInfos: deleteList.current.map(item => ({ + id: item.id, + index: item.index, + })), + }, + }); + setTimeout(() => { + getBookmarkListRef.current(true); + }, 100); + } catch (error) { + CommonToast.failError(error); + } + Loading.hide(); + } + dispatch(setEdit(false)); + }, [dispatch]); + + const onDeleteAll = useCallback(() => { + ActionSheet.alert({ + title2: Delete all bookmarks?, + buttons: [ + { + title: 'Cancel', + type: 'outline', + }, + { + title: 'Confirm', + onPress: async () => { + Loading.show(); + try { + await request.discover.deleteAllBookmark(); + } catch (error) { + CommonToast.failError(error); + } + Loading.hide(); + dispatch(setEdit(false)); + }, + }, + ], + }); + }, [dispatch]); + const BottomBox = useMemo( () => ( {isEdit ? ( <> - dispatch(setEdit(false))} title="Done" type="primary" /> + ) : ( - dispatch(setEdit(true))} title="Edit" type="primary" /> + )} ), - [dispatch, isEdit], + [isEdit, onDeleteAll, onDone, onEdit], ); const closeSwipeable = useLockCallback(() => myEvents.bookmark.closeSwipeable.emit(), []); - if (list.length === 0) return ; return ( _item} - renderItem={props => } + keyExtractor={_item => _item.id} + renderItem={props => } refreshControl={ - getBookmarkList(true)} refreshing={isLoading} /> + !isEdit ? ( + getBookmarkList(true)} refreshing={isLoading} /> + ) : undefined } - onEndReached={() => getBookmarkList(123)} + onEndReached={() => getBookmarkList(false)} + ListEmptyComponent={} /> - {BottomBox} + {list.length > 0 && BottomBox} ); } @@ -105,11 +219,13 @@ const styles = StyleSheet.create({ deleteAll: { marginTop: pTd(10), }, - flatListWrap: { height: '100%', borderRadius: pTd(6), overflow: 'hidden' }, + flatListWrap: { borderRadius: pTd(6), overflow: 'hidden', height: '100%' }, flatListContent: { backgroundColor: defaultColors.bg1, - paddingVertical: pTd(8), borderRadius: pTd(6), overflow: 'hidden', }, + flatListPadding: { + paddingVertical: pTd(8), + }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx index a21db79aea..cf217d0330 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/index.tsx @@ -75,7 +75,7 @@ const styles = StyleSheet.create({ tabHeader: { width: pTd(214), - backgroundColor: defaultColors.bg6, + backgroundColor: defaultColors.bg18, borderRadius: pTd(6), flexDirection: 'row', justifyContent: 'space-between', From a5f41d60f6b9c4bbe9fbc05e1095343bf7d85f57 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 19:10:17 +0800 Subject: [PATCH 369/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20bookmar?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TabsOverlay/index.tsx | 4 +- .../mobile-app-did/js/constants/common.ts | 2 + packages/mobile-app-did/js/hooks/discover.ts | 5 +- .../Bookmark/components/BookmarkItem.tsx | 56 ++++++++++++------- .../Bookmark/components/BookmarksSection.tsx | 6 +- .../DiscoverArchivedSection/index.tsx | 9 ++- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index 38f246292c..798adbc6a7 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -45,7 +45,7 @@ const BrowserEditModal = ({ }) => { const { t } = useLanguage(); const { activeTabId } = useAppCASelector(state => state.discover); - const { bookmarkList } = useBookmarkList(); + const { bookmarkList, refresh } = useBookmarkList(); const isBookmarkLoading = useRef(false); const [bookmark, setBookmark] = useState(bookmarkList.find(item => item.url === browserInfo?.url)); @@ -101,6 +101,7 @@ const BrowserEditModal = ({ }); setBookmark(result); CommonToast.success('Added successfully'); + refresh(); } catch (error) { CommonToast.failError('Added failed'); } @@ -123,6 +124,7 @@ const BrowserEditModal = ({ }, }); CommonToast.success('Deleted successfully'); + refresh(); setBookmark(undefined); } catch (error) { CommonToast.failError('Deleted failed'); diff --git a/packages/mobile-app-did/js/constants/common.ts b/packages/mobile-app-did/js/constants/common.ts index 74c5331e99..914720b3ca 100644 --- a/packages/mobile-app-did/js/constants/common.ts +++ b/packages/mobile-app-did/js/constants/common.ts @@ -35,3 +35,5 @@ export const APP_SCHEMA = 'portkey.did'; export const ACH_REDIRECT_URL = 'http://portkey'; export const ACH_WITHDRAW_URL = 'http://portkey_sell'; + +export const DISCOVER_BOOKMARK_MAX_COUNT = 30; diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 731b01a773..95a6081568 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -13,7 +13,8 @@ import { addAutoApproveItem, } from '@portkey-wallet/store/store-ca/discover/slice'; import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; -import { useCallback, useEffect, useMemo } from 'react'; +import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; +import { useCallback, useMemo } from 'react'; export const useIsDrawerOpen = () => useAppCASelector(state => state.discover.isDrawerOpen); @@ -75,7 +76,7 @@ export const useBookmarkList = () => { }, [dispatch, networkType]); const refresh = useCallback( - async (skipCount: number, maxResultCount: number) => { + async (skipCount = 0, maxResultCount = DISCOVER_BOOKMARK_MAX_COUNT) => { const result = await request.discover.getBookmarks({ params: { skipCount, diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 90e8b699be..92e38c0625 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -17,6 +17,8 @@ import myEvents from 'utils/deviceEvent'; import useEffectOnce from 'hooks/useEffectOnce'; import { IBookmarkItem } from '@portkey-wallet/store/store-ca/discover/type'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; type BookmarkItemProps = RenderItemParams & { onDelete: (item: T) => void; @@ -35,6 +37,18 @@ export default memo( const listener = myEvents.bookmark.closeSwipeable.addListener(() => swipeableRef.current?.close()); return () => listener.remove(); }); + const discoverJump = useDiscoverJumpWithNetWork(); + + const onClickJump = useCallback(() => { + if (isEdit) return; + discoverJump({ + item: { + id: Date.now(), + name: item?.name || '', + url: item?.url || '', + }, + }); + }, [discoverJump, isEdit, item?.name, item?.url]); const deleteItem = useCallback(() => { swipeableRef.current?.close(); @@ -73,29 +87,31 @@ export default memo( swipeEnabled={false} snapPointsLeft={[80]} renderUnderlayLeft={renderUnderlayLeft}> - - {EditDom} - - - - - {item.url} - - + + + {EditDom} + + + + + {item.url} + + - {/* + {/* drag */} - + + ); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index bfdf36b4c5..232ec477b5 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -20,8 +20,7 @@ import { request } from '@portkey-wallet/api/api-did'; import Loading from 'components/Loading'; import ActionSheet from 'components/ActionSheet'; import { TextXL } from 'components/CommonText'; - -const DISCOVER_BOOKMARK_MAX_COUNT = 30; +import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; function BookmarksSection() { const [{ isEdit }, dispatch] = useBookmark(); @@ -138,6 +137,9 @@ function BookmarksSection() { Loading.show(); try { await request.discover.deleteAllBookmark(); + setTimeout(() => { + getBookmarkListRef.current(true); + }, 100); } catch (error) { CommonToast.failError(error); } diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index d1def2a781..6bb2b8b90f 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -12,9 +12,16 @@ import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import navigationService from 'utils/navigationService'; import { ArchivedTabEnum } from 'pages/Discover/types'; import NoDiscoverData from '../NoDiscoverData'; +import { useFocusEffect } from '@react-navigation/native'; export function DiscoverArchivedSection() { - const { bookmarkList: bookmarkListStore } = useBookmarkList(); + const { bookmarkList: bookmarkListStore, refresh } = useBookmarkList(); + useFocusEffect( + useCallback(() => { + refresh(); + }, [refresh]), + ); + const recordsListStore = useRecordsList(true); const [index, setIndex] = React.useState( From 8c597cc663906622181b15d1fa6a8e9709207645 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 19:12:51 +0800 Subject: [PATCH 370/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20records?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/RecordItem.tsx | 151 ++++++++++++++++++ .../Bookmark/components/RecordsSection.tsx | 38 +++-- .../DiscoverArchivedSection/index.tsx | 39 +++-- .../DiscoverCmsListSection/index.tsx | 2 - 4 files changed, 207 insertions(+), 23 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx new file mode 100644 index 0000000000..10f14352b4 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx @@ -0,0 +1,151 @@ +import GStyles from 'assets/theme/GStyles'; +import { TextM, TextS } from 'components/CommonText'; +import Touchable from 'components/Touchable'; +import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'; +import { StyleSheet, View } from 'react-native'; +import { RenderItemParams, ScaleDecorator } from 'react-native-draggable-flatlist'; +import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; +import { useBookmark } from '../context/bookmarksContext'; +import usePrevious from 'hooks/usePrevious'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import { defaultColors } from 'assets/theme'; +import myEvents from 'utils/deviceEvent'; +import useEffectOnce from 'hooks/useEffectOnce'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; + +type RecordItemType = RenderItemParams & { onDelete: (item: ITabItem) => void }; + +export default memo( + function RecordItem(props: RecordItemType) { + const { item, onDelete } = props; + + const discoverJump = useDiscoverJumpWithNetWork(); + + const swipeableRef = useRef(null); + const [{ isEdit }] = useBookmark(); + const preIsEdit = usePrevious(isEdit); + useEffect(() => { + if (!isEdit && isEdit !== preIsEdit) swipeableRef.current?.close(); + }, [preIsEdit, isEdit]); + useEffectOnce(() => { + const listener = myEvents.bookmark.closeSwipeable.addListener(() => swipeableRef.current?.close()); + return () => listener.remove(); + }); + + const onClickJump = useCallback( + (i: any) => { + discoverJump({ + item: { + id: Date.now(), + name: i?.title || '', + url: i?.url ?? i?.description, + }, + }); + }, + [discoverJump], + ); + + const renderUnderlayLeft = useCallback( + () => ( + { + onDelete(item); + }}> + Delete + + ), + [item, onDelete], + ); + + const EditDom = useMemo(() => { + if (!isEdit) return null; + return ( + { + myEvents.bookmark.closeSwipeable.emit(); + swipeableRef.current?.open(OpenDirection.LEFT); + }}> + + + ); + }, [isEdit]); + return ( + + + onClickJump(item)} + // disabled={!isEdit || isActive} + style={[ + GStyles.flexRow, + GStyles.itemCenter, + styles.itemRow, + BGStyles.bg1, + // add margin to scale item + styles.marginContainer, + ]}> + {EditDom} + + + + + {item.url} + + + + {/* + drag + */} + + + + ); + }, + (prevProps: RenderItemParams, nextProps: RenderItemParams) => { + return prevProps.item === nextProps.item && prevProps.isActive === nextProps.isActive; + }, +); + +const styles = StyleSheet.create({ + marginContainer: {}, + underlayLeftBox: { + flex: 1, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-end', + paddingRight: pTd(16), + backgroundColor: defaultColors.bg17, + color: defaultColors.font1, + }, + itemRow: { + padding: pTd(12), + height: pTd(72), + }, + deleteIconWrap: { + marginRight: pTd(16), + }, + websiteIconStyle: { + marginRight: pTd(16), + }, + infoWrap: { + flex: 1, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index 8e171f13d1..1843ceb69f 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -4,7 +4,7 @@ import GStyles from 'assets/theme/GStyles'; import { StyleSheet, View } from 'react-native'; import { BookmarkProvider, setEdit, useBookmark } from '../context/bookmarksContext'; import CommonButton from 'components/CommonButton'; -import BookmarkItem from './BookmarkItem'; +import RecordItem from './RecordItem'; import { FontStyles } from 'assets/theme/styles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; @@ -15,11 +15,11 @@ import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import ActionSheet from 'components/ActionSheet'; - -// const mockData = ['1', '2', '3']; +import { nextAnimation } from 'utils/animation'; +import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; +import myEvents from 'utils/deviceEvent'; function BookmarksSection() { - // const [list, setList] = useState(mockData); const [{ isEdit }, dispatch] = useBookmark(); const { networkType } = useCurrentNetworkInfo(); @@ -49,7 +49,14 @@ function BookmarksSection() { {isEdit ? ( <> - dispatch(setEdit(false))} title="Done" type="primary" /> + { + nextAnimation(); + dispatch(setEdit(false)); + }} + title="Done" + type="primary" + /> ) : ( - dispatch(setEdit(true))} title="Edit" type="primary" /> + { + nextAnimation(); + dispatch(setEdit(true)); + }} + title="Edit" + type="primary" + /> )} ); }, [dispatch, isEdit, onDeleteAll, recordList?.length]); + const closeSwipeable = useLockCallback(() => myEvents.bookmark.closeSwipeable.emit(), []); + return ( @@ -72,12 +88,12 @@ function BookmarksSection() { data={recordList} style={styles.flatListStyle} contentContainerStyle={[styles.flatListContent, recordList.length === 0 && styles.noData]} - ListEmptyComponent={} + ListEmptyComponent={ + + } keyExtractor={_item => String(_item.id)} - renderItem={props => } - onDragEnd={({ data }) => { - console.log('===', data); - }} + renderItem={props => } + onTouchStart={closeSwipeable} /> {BottomBox} diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 744aa43e55..6f6ac3dd3a 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -2,9 +2,9 @@ import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import { TextM, TextS } from 'components/CommonText'; -import { useBookmarkList, useRecordsList } from 'hooks/discover'; +import { useBookmarkList, useDiscoverJumpWithNetWork, useRecordsList } from 'hooks/discover'; import React, { useCallback, useMemo } from 'react'; -import { StyleSheet, View, Easing } from 'react-native'; +import { StyleSheet, View, Easing, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; import { TabView } from '@rneui/base'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; @@ -12,8 +12,10 @@ import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import navigationService from 'utils/navigationService'; import { ArchivedTabEnum } from 'pages/Discover/types'; import NoDiscoverData from '../NoDiscoverData'; +import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; export function DiscoverArchivedSection() { + const discoverJump = useDiscoverJumpWithNetWork(); const bookmarkListStore = useBookmarkList(); const recordsListStore = useRecordsList(true); @@ -21,16 +23,32 @@ export function DiscoverArchivedSection() { bookmarkListStore.length ? ArchivedTabEnum.Bookmarks : ArchivedTabEnum.History, ); + const animateDuration = useMemo(() => (bookmarkListStore.length === 0 ? 0 : 250), [bookmarkListStore.length]); const bookmarkList = useMemo(() => bookmarkListStore.slice(0, 4), [bookmarkListStore]); const recordsList = useMemo(() => recordsListStore.slice(0, 4), [recordsListStore]); + const isShowArchivedSections = useMemo( + () => bookmarkList?.length > 0 || recordsList?.length > 0, + [bookmarkList?.length, recordsList?.length], + ); const onSeeAllPress = useCallback(() => { navigationService.navigate('Bookmark', { type: index }); }, [index]); - const isShowArchivedSections = false; + const onClickJump = useCallback( + (i: DiscoverItem) => { + discoverJump({ + item: { + id: Date.now(), + name: i?.title || '', + url: i?.url ?? i?.description, + }, + }); + }, + [discoverJump], + ); - if (isShowArchivedSections) return null; + if (!isShowArchivedSections) return null; return ( @@ -58,21 +76,21 @@ export function DiscoverArchivedSection() { value={index} disableSwipe={true} animationType="timing" - animationConfig={{ duration: 250, useNativeDriver: true, easing: Easing.linear }}> + animationConfig={{ duration: animateDuration, useNativeDriver: true, easing: Easing.linear }}> {bookmarkList?.length === 0 ? ( ) : ( {bookmarkList.map((item, idx) => ( - + onClickJump(item)}> {item.url} - + ))} )} @@ -83,14 +101,14 @@ export function DiscoverArchivedSection() { ) : ( {recordsList.map((item, idx) => ( - + onClickJump(item)}> {item.url} - + ))} )} @@ -120,15 +138,16 @@ const styles = StyleSheet.create({ tabViewWrap: { width: '100%', overflow: 'hidden', + borderRadius: pTd(6), flex: 1, }, tabListWrap: { width: '100%', height: '100%', backgroundColor: defaultColors.bg1, - borderRadius: pTd(6), paddingHorizontal: pTd(12), flexDirection: 'row', + overflow: 'hidden', }, tabItemWrap: { width: '25%', diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 184917d645..9b8e263612 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -17,8 +17,6 @@ export function DiscoverCmsListSection() { const { s3Url } = useCurrentNetworkInfo(); const discoverJump = useDiscoverJumpWithNetWork(); - console.log('===GroupListGroupList=================================', GroupList); - const onClickJump = useCallback( (i: DiscoverItem) => { discoverJump({ From 2efea9a9762041cd6fb41d0bb2318cf5d820488b Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Fri, 14 Jul 2023 19:38:06 +0800 Subject: [PATCH 371/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20Referral?= =?UTF-8?q?=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Referral/img/splashScreen.png | Bin 0 -> 61904 bytes .../js/pages/Referral/index.tsx | 35 +++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Referral/img/splashScreen.png diff --git a/packages/mobile-app-did/js/pages/Referral/img/splashScreen.png b/packages/mobile-app-did/js/pages/Referral/img/splashScreen.png new file mode 100644 index 0000000000000000000000000000000000000000..e4f7f642a71fe110af257263f916e0d00caba9d4 GIT binary patch literal 61904 zcmeEu^>C8%_$2#BQ8E!`zC0s=C0%n(Bj zH4HWMyodY#e&6T$7v9h7Pt+G|yz1LcM%|z;GDc>Z&OHM#Qa8p&~*>eH{B548w zl6JDIz;6*^x%+f7S(F@}}MT>VK{J%TRv4 zx3-3y<3B6Q&p#siug$*VW!rf*WAdKrKP$8CeEF|U5m(aOf>b(}{`{}Y-h%x8uT2q7 zk&{VJ)f!s<*Ja71ZT_<)puS2t-25BnZKacWI;^c2TaD zqAK-y20BNg6Wj7}#kn6U@kS?}rQF}a(O>AJ>0S4OGwo;}Ju>VH-8Jg^^j?wAbQ0gF z&;rNmmp_gMmiC`yVg{BD<7V5rK{a7Y%_je{l}x|*0UGB^F4n|#+F`TW3f`QYi8eol zR@hoFk%^z?`9G3me!Y{Dw%y%|<|fi!m0v?E<|jWLN)6!(1eY4V&tQKZrizzFa$A)! zJVM6o;v$uREuF1)Uym|wWV6CPtbN{-)xMB<8Ft41#B zJ`kao#KajL5>PrIbiY9%50{u6#yCk5lYPBVW9bNpwKlmJ6$q!eIXS}L`FpVC{7(X* zw#hKs%m38KKB}%{{Pzv*u~jA}2?}r5p^&lM>b~mWT>BLa0$hG}3N7XC99WEGPvTQ) zV^0#~vs(TH<+Hd`;i1ZJ$S+{k{V5_bIv6ThaI*QLp4}BW5KY7hY$yQmr>!`yGKmAx zihIsR@@`+PEms{@WOQ;`?NH^2&$Z*3Z?3GO6LiA#M3;M8pO%4}E1DQbwk~$Hy{}D& zs1L&#b8)F3&5*aPYy!52nRY`Iq`706n6!%6J_}f@2Wz$ETdNn#ZzPRbA$IqRBW@N) z8Q0X)s?#d*nd(G$Uq?bk665>l;_}p6lYlzwX#ph5sf@=vAh+o2<;h9xy*bUl*9O{( zXUrgDSHW97!lq3XTd`n`nqr#Nx`wW;tW*E1L2qC?VPk#6zg=Z+g*H+RC{YzB(^mL7 z^J~c!ve>5S^J0ihB&aI9o?w%1L2j^OOfUJ%%Q(P!=UM%TA{toN-ZE!q3-lRR&qS&U zEwBFAo)OqGDanAHi$Ps69ZvtoN#HJBj?p7h5R=k|vx z89Esc`mtEeni~}`#;*X|JDHmsdy8QpHwNo5_^!j4r0dDw;tYI0U3?z%F5K ze3{;pgn?HsCki*r<>wisDky=eb9=?LoYnE!^JiQaOCY^v@}Y9D9LuUx0rRBSJip~j zysH27?Gr2=2s`hY4)Jgl2M1-h6`PQ2qeVTdLajkX9RU+F&MG=Br{Bga;ljSOF}Tdm z1VUA)k~TSaEL6AbXO0DWC>HwcTqxQy{ruaHiLLPT>ghxQR*j~%T>k12UKyP2Fr+K0 zyGBCLe7CkSqGv`LXDjp&`TnxGhus6}gup|`84}3ABp(&v8+^#(v z$Yx@2*>wYj5YuxsoyY5qvkH2`v^jDu38(rfQ{;!49col>d21B-zly&w^Givlo z(>5(+RAG-EjR%WqP4n|RGo%M;^KzquQmU;ISRR_l+EwdzLC7UyFV;1SV3!Akn2ag< zO}i(bLu)wj6UQw(Uv?HqEhGNGG@-)ZH8>$;IXX(hTa$lWpg0k?AF`M?hjLt+1yW$TX4hp-iKiv|2eikKn-}iUdrj;e%`K%Yt8A0!--@k1bD3wQP2{T zPloPM#vh*!Et-@Vd#!ycR-c8p1*-qxj&tR8;rQ1a+=rio7qhgszrBpZ$~$=)6CbU^ zMZq?4W?xxkq>GxA0oxZU;v(j$u)ulJ@U%OP@D`DN+D(g&7tf;;k|(S>i`ccmFS>dR z4lpR~m z7I%exH1v4>OLZt$H94!)R5v=oXw(W-WYXY2#pAeM5wKv|=D&g+T*H_&d$~&` zHnyD@f+^)&Y#-{{vsh+dnD-?KP|chu30}0B=A%OR)4#YJ2-L%n;T>RzTc{MflUY;^yrK*%Go8?3L$+^rBt+>~z*2}m+AnI&FFRz5ywr3&ZfR$hz zAASxB3;Lq`DXSMVS?po@{`=uE&p8VA#f>uxNJB<2p%SXkmB0tXXK)#@;GNh)V<8sz;XG)- zSyY9DpPz2oZ>HV{7U)kkGpx?A0m|C`KUKbs!EJ*oV6^9<9Ej_Es^ef8?6s{(-|7k< zgJ9nniy)8>K!oQ2&-XF%2~7JjAQJ>%=sZ~o_b5B=c^F)542G_Tc2qluZH4aeXMi|7O1 z=-rOIu?-c4nG%uqaG>e|<%Q$UElAw0zGQ&zz07O>9sc%WYik5M;BT@lBaUWC9@toS zADD`*U;hm~zBTyPqUrEpv4bB`zr0{5ePSia_aW85;HU2 zI%nCvC8%Gd@${6_&?`YOlB(k1Kufx=#mGhD!&8-p<^5a9-f&xe9C3OzyLsby#GZCx z_teG)>Gn{eEM7&R6Qm?C_wVVoQ3N0bjSs8Z8+i&s`;pK)h8a`iv2*9^`T&wK1;-^l zRhahXe;@PudbDvvHMg(qj4%Usy$rmGoQO3kjZg|adcsAL;>CxG}nIOJ10KA6w8ZJ~)v~xLNx}>_ngTi`s$+-k}1RsI4f_k`Kgv zgVkqrD32wYWiG4ttuMSlkugKRLjlhhc}+jW_O#4p_9)wD_RI}*sCn)+&+yt3{iWxw zVu?Apt7*u-OIg6Ni;(elOeKlzKMi2z%bRv#*xf}p;97Eou2(a_vk+g9OHhI6MhMVb zgB>?<7NaqHV&f;Nw~dEWe-1RCs`lTS*DuceOCg)@#l`t0 zR>Wn(Ts4L+UbI!xw(Vq8NZ2ay)5bL^tkTm5UcbEIR}X>rW=tEureYHc2yrl7&#&`F z$pv~p1ka5yukU%;80x!d={ys01Ynzkj4bp82T-Ntb_n!9KW?_+Wkdcc_2IxB%bhH7 zU%U0?E#VmXPxO`Fa21uo#XF4 zy%iyF9}1#Ic4B9 z`EdY=l4>Za^S_A_P)gT^ujz$cjBW2^R_N`sazf#|CIM@38?#`rOcEboUzTX5fMxMd zWf~_6Iy-po+l0~x0D}%mI(V}~(So=(3h)6#G2NqajW+fkM%{~`dKKdq^JDL)f6Pv_ z2P%N4I$sYIC=}=ZRiJy>5gN2Zyp04~p|-rOIC);1v|!5j*^MzEGv`ADWurC zssSO<1qw{|=-~^1X8DJ#OM}wi;pe|;oozXzr$C5rS2OP1hWB;$7}Oli)>ymu+Sk~L zoH){kn>Kvm-%0UXn*WgS6QEyIO}GOqjJ_by?5$=PTOiIx;$Y-d^n~xhVyw(1Ta89u zjOsEpBh(C+bx1PT_loE;5by1gw@9P5AD{8&|XS34PA6 z)lUsEZHcGJn)=F}sSa$`nhvPEh(X`mY;K|CISK3jnVVg7uIX`f;0+2HFRwWm1#@J6#5L^2?>xcHQU}o@oS%mP-Sax(1AfuubMnsTB)^ z&*sicTy@2tOQW6pZ&l$lxxUsIH;x)FLfN%Ptr|7^D|H=(V4C zRc_Xy$}29|JRNIW=Zh+IPM>T=4fGEqKfOjm-%qPFf;>^7>qNzE>K(kLXC)Rd;$w@W zCY#GxxJtSE!kYvD=KNjqlfwSJe;ObGfC~sBkf8)2XCy_ZJ}kk7j211$G`=$9aW>g) zPlZa18gtf1$7+t~Sn1<|HrRKnyRz%p$ovSg-F;}@a)cG$s>h?NKaKEX8VU{d`{aI) zZS2_08#-%7Yw(3WoWgY`K8R7VfxK77+^r-2+<`2Qb^~J z0SH8J2)Ju7u;r-O)WrRiW6LAg*0}sLDSRfg8K^z`$cp;?edy>&ptPq5w^tKY6Bh?d zKrJ&)1`*3u=WVr$1Xr7!(^Qy&?bUZ|vbkBe8?AKt2jy~US6vFaB^enjAnYrM+^~jy zMP}g6CDH(tEpZQ@0RVn5Tj^pBqNCMY8`SFVTHLJ9n|?;&=$+q~woW$(Vo7!`>;8#t zNbs^5Pc!t&2(F>S$&SS-TdaNN1=%ZvdbMdqNea83WMULPOS#RrW=41z)m2w?{ETED z=i^xn)!T6IW{5Ses*X@X4Jej0+LicF{``<`;=BU-0{50}z~Ktlen*5_dRWR5&vwAr8wmREHYR z^jgHyjl?;mPlCoMiFwGD?s!o=Fnym{o$}12T@9aR2!Ri}0{OjIIkN7KW;h^v;@v94 zWq&lXM12W^On@w?M#{z#guI|F(9)sA1thND7Y#i3Wp#qpVH~@!&onI$Q=-^Q@A$y4 z?XTx52JBbb%(^`hrq%p?u_a*eQUjLkqt}E_(URVG;sTP0dIq_c^n~ku8WAtUsjp1x zvRM2Q%C-!dBdMBvn2VTwzB5(dNDC0t?$@b|6)t|k@5{c*6w7kh$5Y+|y8D$YAuu5T zQuze8;B2)mPQT`)Q-=!^LebkUL6d1iR>nu&W98WYs)r z_Nq*C32NIuvuS8d5QzOcFwsFt9_ZsQxE)x@jFUw~uGaeYt53jN_&S@bUBx!!l7`Ao zzJxkXYhsp`Ya}qAp=Qm6K+crFD&L=HK(o*lYbPu~pJQqQ5@yqRQedYiBVuSy znugK^y*X!9j}J%`)-Jll^N)#{*Aurjr*=KWgN{@{+W{ra|rX&zGk*E;)yryucw!s%Ch?#p3^s5gk&@vzC)^YKiC+(OA`O_NDovx1*XW zmg65Vh?tD%*D6m=WhwzrW6ISLO2aoZF9|DI2<*;ftmMfBEE=k4#1Rv7c5Lat;zgq* z`~!7ec$?idWef$>h78cIpjq_36|6PF)&kwdA?VeY^?BQfVWl^)aqdAny+wT?m7J$R zcJpyyt=2o+tLZHxEf6<#@olYQDQzFCA^qU&u=Qs$Gv63qT8=5`kK{TP!Wa$578f-S zDq*%6X8!$A@%^(`$uOlrO1XUid~NrEx0ycAlJX1oQx3YR%VNDJ zP`eIAN?C`hF+U$GT~|84=lSaNaCzC~^$xmw{lP)~Zi!O!YFc&g6PX_AE|Hm5-WWg( z1yqOMv9c26fB?a!Q~DVYnT%xl_Al~HhNs)CJm*mqjlDrD+SAJJz2l$W^K6QsT2@6V z%pQsnM{z*=Tl88WN5?J!_z)lIAGy?sbAoI)NInstpfpn#zh+hQx1YbIqEwO_#ujB( zMf*ro@&+El9p;=$x0#3E9I*q5^IH!%Tp416meg!+ON8kAyS=3?N1d*kJ?iB+Uz5}= zBfk{0x(G@zoLij7xFzSy*jW0$&R7$RD{Nxh0ao19NSYxjJfl_UJ9d?P%)!)q1V6Kq z#n6#Q{=;BWrETbaKdcC4EM(Y~o4SGRR|pYdww6P_vGA35{lI2CNIV*sSP)SArA{Qjd$$ez0G+8sFLW#!Ol1cr*5 zmOq@$8Pdkx$n7WJIAC8_#Al9R>`AMudlhOvKJzQrIKiD7g_zh^z42*_t3EY9_7{{v z6&NAa6Sn)MemEqy`qoAv@D1pC@shIc8e8$j^(w~KN9nvn7GGcAWz`1qHw5SaM3(!U zK&W1acvYrqJMpoP?1)#&dpV(Bsr(M1j&O!>xkqcZR_YGr4FO!DJqrM9Pokl606LoLUYD4IBrIa`RB7uY#mxPjs!y-ipQcS zd~l1$6VPQ&nxR&BY<{h`buU~bdfpW(w7=F94>zK z%ylm+ct*`H^RXdS|+<;u@tSkt$Qr4i1pa#8dEnL!Rpwf?BJ(>DyaJ+F_WdyI5Yu5pt zNPjwUv4I-i{*uqO4__yfR(4$h0mKMKXrH*+7GV7_XxA*i&3Y!n@kLF2MDTG(Wc~bS z_MX)2nS!k8JpWZ>1S7`2n>oJ5dSe6eO-{$#=9jAJV?jGw_row~T2(7cera)Hu|)4H z>^&Aj|H*-Cah1NpZ{gp5>X6G{;^tarDxj0~bPgS`);4-th&w1gg0J54hEhetqU-JS zWMl&ks&0gh$*3v6s1@V__IOQ@Z6|GqgWyej z8u1tYBSr(PbF#iNA|Yu4xqFu4FlM{r;?ODcQyeo@C~Ip&rpEhFumu@SXR*$SK3Ak86?^f1&S^VNA)Xt}NNAesT(P3Dl^$g>;^AE;)v9F5h=IWc6 z=bD>n{7pKWWQ)o9(5-3vtKPg0753hxvyt2Ik&O!Xu~d-A@7#2F(Ua0Rdz&NGCyz=G z&z6q0q;YEEwXMU^)z2jiFU%S2$cH4=@osCe) zj#rx;0Am8_`dn%`Bs`L?*4JuiG?iCY`C?ERm&g;4OW@a6bV>S@%sdUV_p9NaUU98o z0aR%OfID~pat}hTv`ac3kWGij_9rX{-tDB2M(21n>wcC?!B4QV@VfqD?Os+P6|kNe zj>c@w#X8R|xn)))gqk-xxt{l#m<1Zj2*|bQy6~i51bt2}x@rBxHZ0T2ZxJQvy*LP) z#&(UnH1C@T9%^Fd7MxX5<@CSVA}URqac9tgokb-+3w>&6*sv2u^^Lp_@No`4{06tK z5V~@?u@P^scVo^DUN!GeyAf$iscIGz0$n^hvjJMtOo~ePp_r4s@p%DFT9sjhooW@8}njsc<6Ua+e-7Aatfr7OhS#Nty9X z>KDs4;S|r-dVK;;TMe6=tLPF&IuY}rsXWpOu)hn3Y>=O@p#Uw^BVE`VP%G$xwk#9} z3?@SS*mOuTl96lOc;O}W>7uc8b#d{<1S7G&t@p~U-_%cvrR=FAsIj6`Q4n{3ahJdxW?#9fxQWC46G~5JIIm(+bviE5R%|h| z#gQ+Gj~-6yXA0c^D?m1W_2=0G04Kd7G?@3>>;}?zf(w;+&&5Qi)a|U#8-*S2Kk?Uz zxANvf2=0xJyPR!pmDXbl4?}EWOeudp1{xf^`8}}VuuR_BoiCc^hRIi-36$G?%GK&& z%bNiyq*fHPGH&2{pFv_>bMz}W_Na>{)>vA1vSsU+(xed;K!Jf-8JBTqqWb(ZI+dn~ z^AV2pqheN#o6e@|>sR-xw92~M&e|bqtu7+s$@%hij+J!5hX`~%KE+2vtGnIEx9E@0 z1yAhJ{#dMpwK+d+%+#Zj#)#5crAB7#CNJXXLSd`^{G*RX%_-jJPt%pWAvH^f0XVy? zhpMn_z>BH?r4Romf&t)npaR4V9m3N*4q5zLbp6(_PeYX<^am;nQ(s^xFAMb(4mxhE z*nM2Ci+Jrm61a5)r_8q=(s$(+AIk5a9GudL&kV#Frv-XAbMvY++;|0bLvO|VEGC7U zV&NMIyBziFq-JIfqTYg5=Y8z5r)3rOuRLXFW3Ois*&O-+pu_VLpNaoj_!pmflmkZ` z<=ZCpV!J>z#;8GDo(fP|O#)WF+L(FUWX~q~8H0QcL;(UyDK{4>h0u zC3m+!VB2c<=_HB0C>pyU?TC5!`)0O9Cv<3ix3|!zE1m_@Rfoxp^Ws_7+0LI1Zg@3a zd6HlxikkMkyL^>_=k>4`WScJ zgsk6tns*!wT@UwjfYHkemBpzpFVH+~xfww@ShblCnJuj>Z4iX9nVXv22MW*axUTkz zOIc9>KxdxcuE&KCgnT0h1mq!SOn30<_$vUL#a6RFjxmzz(2%p&#%-Hb+t+iY8<_LV z19GQfz(JW|wbVK3ON#YGWYJVXH>qG5jH{d}dO7-pSNZK(%oFAfvw+NpPQcijS9hO#yi=}-J%S+@Fh4`h=84#*CWijPVXHvm$>p>zH&5TpN@a^dXaZD|N{#$!h}pK&C3 z!y4s&%n(bzfl-nfohSp8jno+vY-eV5e=PacnrjLk>Z^mR>F@}PmR5%RveE|DR|M%L z+Z?BGxtc{~?dtwFQZZwdW9m^yN6XgOZ|ZlK&&@7B!bjpG05ho@P93=12+2MdgQ&2- zYhBKe6P$o+;7MsP(_Pn*uO??r+L~Re{o#0x&xWAIjC>F_UtF^rS%_>m^yw1~{mzXc zI&Q$lbg$IZ>}If)caY_z1ZOf~_{=>W^!;B%0-G8mk;Fx+q6%NaVgmd5O*txVroGKgr1)4_>MSyb9A5 z#u7e9DA0I$!S=VQT{ZEGlgvESMLvkgyhhE^_5f~j)|jFAy7)08US9tWZ3!%sNT`nmPJuPHydmRShOu?j`qzo6?FZcIdG8fP!;*sl@%K3E9kSm(LD&L#i9(4rq&bh)lP5mng6VT zeCN)jtM;;_>ZhfB%|BKQoztH&t8XqgxfXz}0sGm{_y*5fR4^{3<$a}#qjNtei9NE# z+>P$0&d3${fOl)Y2H;CTq)QPm^~eHJEmq!}J>_Y~+^iV$jPGF|_0f|I6&8uexCol) zs3SR9aVkI#x#|-+)p1}LvI@*Omnkfk<2;GD3vP%hQcYxxv~>jfL4Er9S}1}F?8!yT zH`jXlL5;w~EyBpxo5T50t8JyxwnnmHebl+XU4FL9X!LGt_Jiw=6?W2Ay`d&0Cbuh~ z_EbE7O3j`(zlK#h9N39ZZel{B@iS zP&6ii;z>_JNL+cRfTi#FeQG|cr^$4=O66*xq-o7LrpuV>QYLi`3}8>vxDj4OPRLUq z2$b+yFALtgLlePU-f=_nqE2(RUA)dF0dI=*EzmLDj!yNu`U zd+QkyF8M}fzel6KpPkQVeX7)+I&}$7-3FIObfc(>lW5%GmB6i-be$yDOjg&@0+>@! zP%*9HL^!p>wulCc;Yf9m&1ak_ebGhqYkDk}!Q+CCLs(|PRL)dZZk`7L5% zmZ~S0AIT+%+3d~?wtcwmS0wzw%}q$(drlqt3w0A{1_?$Y)(b~&5|ky|($jI}bBMV7 zTA!=Cn@Q{Nnjte`+RXZ9D%8M5tU$IDgMh-?#Zan?vh?wDz|t`unJGONscf;?vmJPz z*x;2WEC`5aKgWNg%6*=x7YCvD;JGfBj;Bt@ z^~c}F*=RAy$1#P6B-V;&y9d_G4&cV$1D8`iu27(Av3tFUCDsxOdB`+-uFrL`3DWzx zeM;ZMn|>!hvoGKL2R!#BU51HZ9W6^waS^%=pVihlvUPwlmqZ#tyvpbx?D{D^IoMayerMrT`I_27##!#|q2W z&g0u#h0wFLJ;V_zHP31$ZoA!=Mx(?1JjB^=gAQ}$Nyo;1>L7nlp)`VxIP*4&J; zW(Q_c#6<}xSHw(I+u6mwyx~&{Z+ZJ}ON(NMpZ1Z#d`GkQ{7rhcq;9h@OjhhC`9 ztpEPPMd5PB5;6;P$yrc|EFo8xyi`g?T9ijsXj_xD6m2Xe8xzydifP1VkuW0`BPSE| zlCIt|oo(*?6{r>BSbT#F=8xBs>Z#85HbETc;zX=b>(jr2r!&P^j*xG+>X*OyNaG#q zZkTmKgf>5(ap`PLi#-BIZw!^gZmxXc%bw1LH=!eet7w^#`s1W3gx|rV(rz=NC+rM)sfB z+|F%3Ll^ffhDG14kdhH!A9!DLMHR{j^xEIdz-G7ie|`k^O%Sr)wY3sH4ao1Mle@dv z@-U|LA4Sa5Ng8*sdl=?upqtVkX)o9YZN~w?Cm}VjpCDO4K?<2LcL$QqA|u}7QL#O2j`@wSe$P)a8|)0MOz8+ z*}`bf^SO)BXK7X6XcebR<|U9DZgVDMJ6-DN$F*q4T7y;&f5GZWu~~OCw|ImKXeSWh zF=?%3hDcix&*)9dv5&L0EyK}sr_=hn<^2BazGt)@2>TONI+T0JvyRYX0XaQm76Cle_ z07Dx!Hj)hd*;wIYrko!_0138yB4~vUWk>phIf9nMD!!HO0M2oo>{MJ^TNRDA%NEOo zjHG^Rn=g&i|9t!;AYvtq zP_^wX_d?EcJ6xAdEe$}`r2w1=7j)^#FJnOpL*N7Fa}DJ*b3s`&z?2+R#yk!0-TNN- zJ>a6xn{L$l_?EA&dS;DpBVF+EIeNF_d)2Yd@DmEZgCA3#Vj_TI=-MN>5uOSuIxPl@ z0b?e;ScMu5$Wlz=6|7XG34UPtYfL?)DI%zSQO0>&%x? zJaAlIWWPw(*6L!|h_cJuLITxE3yy()rw+!p6hZ=Vx4sI!L&<)vD;Z;Vsm}_o!b7U) zP4LCm)1a|n3Oy)2I?I=(P#c4Fr{9ec(_a@S;iVqJar_%b^^ zq;dBjq*mX)Gzgx}#K*&)dFrgY24=2jB%Ee!KWoG+Ev#Pkzc&6YPAdH(-*)u#%)@+J z@&V&|HCTcVJBX#JEhIj*uk6IWTbK@8OsP^jAjA>T=KdVpfsM@e6ZKo5 zrsdvq?4WYZCogQC(pfw!!=;{NsziypNY+=ETDg?t%F=TCiQ{ocwuf>gFduRDfnROM zcESA~mu+_mP;Hjrs@}IGLP(0*M;w&Tw&%1HxoY3a-b?G9p_B$l+Drt{Pa%GiV?tUU zBepJcG}z!^cULF5!FFN9x<$~z1}lBEr}%hyWBM+&X%jYiE;C9wL$1}iI@lqHQd&}U zu_R$*`K{iNR0A#WW{f_AjLw!?%zAgy7NULw>oa=};kpPuL=vgQ@q$LqM+^)=5Xzta z>W~k_S8ng$UkgLo^)#gq|0*%$-BOZvmhRp*ZTVA{GuC{~Udbg!gsF4kk3Z!Hf`yxw zWEh#dENk0EUV?)IeM;YSW6O%lO3I2Zgb9f!(^rrf8{gTsK}mc=8lSWS=MXuEEfI9> z=*0Zdti!xJcDo4F;2!IBh9N<3M?w8YNN89hsyv*MwaZ?)E$?6nUB*Y2be zkZ4y#?grhLCp3R5|15rV>+Ik<|76hQmea zl7+^t+=+UwN_pm%N<>9@yHGFCnOueDb3>me^`rTfYqS|Acl1KGv=057+T_L(-CeAFk%l`=Qp>3F48gehkG}>pWs~INpH-V0t2RLWN@Meq z78#VeeWh+1i%fN;kMPIbr5DjmqXL%A z@#sW~5ffyE()d;PCL%H>SAgQIgXo$DQyvHHli+N(mO-=Koplq^@)Fg1HYB17-BQma z)Ur(U}b-V<<0H|Q)%0s()mEl)gj(o*(tU3S)3CUmu( zhc);-V3kB0e#ItUY=P#^n^msGMaoJZ z!I3Z1+&F0Hv*I!Iw5OP_&AMH|lW$gb>?sCXx)%DF$+ZuQJ&_{S7F1`qq!Eu|7{4ER z{Bx0qlchzHHYL?!3jcJ0K=loAQ52B32K-!H3WSK{H}&qS<~`Soozf&$dFslZij_Cq z`ur=@m_;y3;wOi-WY9Pazc(|OTWwxAK=yp+^Q}mspL;{D`8$I>ixQFVw=F-(9!;h4 zEwdZ)aaHfgno>fKdYkwkK6oA~{GM3z{)w1heFzs_k5GvPw&%;SY#1sqxauuy%kXor z4vwKuw#IpRG5lR0vN@CbwTXxBUAfH`vdbyop@-b)90qc8Nu}KEOrthkw-Eh?4On$5G^hI_*U`l)eXC{uc%n`Z;-6rCXPBo% zQvv3?9l4N)4WTi$%?73PGk#^3##O``jZghZf4ZdQ1`B%^h7nYKx$f4Y$K$)z5@dNd z)7TMzG;seD+^Aq@!6r~+@?CZCP!^~COYPG6(GI3yFTcB&72uiHn@}!~t^k5-ALMz+ zRJJ-?lk@dIuLdMgRtYPvy}l0-;0looZhWCwdjGMW%F#UygRkNOZi&|&L!?q@#szZo zlXG`^piSJV?iHylqV4TQv@jtlXMG)wj*+{|l1q!-20Tu(Uwo4K6;#NSDc|H0&DHaU zkR*JsIH!O$DzZPv+;?9O_@iAg8BWG)|NL~T>Gl_C4Fz_?vpX$T@hLct%W!T`Wz8>~ zJ^{`=4e|Z%%~i~U3LD?XHnosK)rNNN*J`GEw~ttdL{K~S`Cq=l)6)lX^F9Ag5mz-xZ>Iajc~Essz5XUy*86zo&Hl zAaXJPW>~20sxF;eu$<6p#Nw@v5uNOXisMJKk9&3S0)}@j@1T}tfFxAO;s8nzGu0vv z_~Bvb8z*D@!ohtpRyPB^9xJ6Q(}X&)$_cWx8jrCsO0L;eZ?XUN0yni&p#CIy?<1_)Yzu*I2jrF@JadoklC+;;pX z+*Vd?p`E9WEnG4(P1Stjt*7-vnvzNOd(D~q^L|i!@=O*Vj^nA%tD{|_d}&UWy-Y;s zcfS{^M!ni;=DB?0ukOirbWVf<+kZ$FA#~Ky50v|^_vTqbzi8gwmgZsggnd0af8aja zQyuzFMPMi)NKVdQFjDi>EY0=s`t4h#wWAz2&yt5YZ$(cgK|8^8ZMr`@HuN37O+#RB zT#@PRX%%5b{ZWf#@869E1^$@oh%VM6xyG^}?f2yyU%E2o!&VhC7>^Qd8#v?Vw7J$D ziBTC%h0b^1uC_v(-roz1mP`J1b&xRu$VUPa%DKzO@9=;vbk!B?)KQi#z@&Xp5VNU6 z`18;mr9w2+!jnohRof6v)DfTM!S4ZE7+8x5r~9pjv4waVea0rxV`+t10}sM z#*d1SXJ5__QFml#ZyVbyyZ-`>)IBk}siI5~(ev(Jl>6R4z34+MP$vTffl6N#`i3k6 zfBy9PAro)xynRLBf&GOM9Vz3_0kSuOw|oCH;pfpyfAUV(7+8Z(t**yq_P5C@Awx1m zrCR-bZwMzs^|=dj!JHwg3Otaa#pL^3PY4lTfSM@| z4$w*`E{c@bBR?vht9gmJRyTjsr%d{(>H4F$UIFe;Zj311{rCc>reL6Y^R%0k$4PRY z`^r7V z&W@Sg)u0BE$mN;zW*A>b5=5iZ7ZYnaXk4{6KDt2P{eGt*v3A}2&ig+}e!r@^Xm*-# zSFMEmS~Yc>!<)JO$XiLw4qlOc_A4V_NcAfWEp~Uw6OG%f%eQcDn09dcU$rD8j6ZPmA)N+bi{A+>TQos!aF_&x5HTG2Eh_MU z$E>vE5n=E0N($S&{P{dO_7)u&%gfaltUN8pM=Mj3*E;Jv8XpT3L@VP6-?KZ<+|xc# z`o!||_C4d$!$#>rRyM9(FDq z5tXJ(4WyAtSSizW`*lb9liJbFnQ_j6-f&A~v+!k9iHW%(N1T`8CAmT+9emMmvwVv) zQzLot15AU}eo;>5c6=EpXG^bn-GqUR6$P!`0iShZL4P|~>Y{vTBFiG)E`9U<-35vE zD%Kf@Z9;64{;EF*LUydnS{(K#zx7DQ<+CyN8!}x6CUaK4YT_oDVq2kq!ZO#$8wbi$ z=7Qu1tdmf+J7&{K`)btf9GhAdsDEDMiW&ky5L<{Hna<9$&P3k_0Csy6(yuz-cK-cE zgJa@%BwGoJK}H7_>{oJCX5)MM4`V9)Qu_VzO@U@kC9!>(4UvF@Ly5fjQ4VcNY9+2A z|D%`1#owvmw`%_=Y#UII?8G)L*4qbQng2W{8EU1@sFaYKSXxDh8qf#tm1S)|C?bm| zV~IR!p{6%b$tFYueui`>sTFGj-XO-jVw}ZZ+SE=VZP#ya|URFok6z!&9 zh`MS0luuxmZ+aM)>vge(ZseP~Wj?Fb<1vas3v&43F3rtvI*gNgFji@LSj0sxx}5## zcWh|<#Keqo?;kuAeu2tV8u1^HlJ~IPxtTeq%5dn0?TZC}P zez}ERN11h*$3&EkdcfANE)T!nGwuMBI^_>Zfl*SZ-o)PL{)q$&-jb7tEe)vumxeR zNapxz?Ao%|O>Lg%)1T)XQbW0leB*GJd3F=n5!`J>4~QB&CvarL%WLOH6a=1Yi3ot5 z^Rpg*3EppW>qDsGA~3`RCqaB9*!;?y|xaTxkDW<7?a)F)xE~- zvGF53_SAe+W}$E;=Tjvoikub6EzlVUH&E?Yx z74l~-ldYb{%rylstqZ#9!@0~Y6ygwJ85}7`L--eFezdjx;-hzibq^hq;u_XJupGKx zxy_j2at?GfWI>CeOsXgI?3u`53MbV|^q_FCcn3>Y6W~GU=%}5SQ_j)q&HbXv z!EeUS{_vzXu;+0EDzcj*a*in}FkR!*2{@7zn)zSVMTHe-S=?P5v(P7+QrFih5n1_v zZ^)87p6Sga35lyfvMtswEoos@w#pb}qC>iEq>j+<7R(okoce>2a3Q~r&+Z!ywRu;p`$Hc&ITFE`D z2A{>J_NQ-+bd1B_pI`ItPtMXrwj=^48G=(dbGS#hc>xZ+mju}7!Zr>4(#|@hZom+I zz7G}W$jZz5V9xpH_r&Xm>mWu=bZeJ-!i%$JESe)O%{!_m?}72N-`Th)iIh<^43ln= zhb*dHY(IEf(@_Ua!tA&?LrKz_*0=h;5>hVg(E?DeBbq)2@gmxnh0iW#j_^ubb~j4w*lKdj|S+<2NbY4&JWsZkt!x zH$3N#ZO^BimATxxnVG1sm>rQKhU;y{5D^?HoGj@ahI`u>mj^Ohj|}#)VaCd19Xg-V zN%t*D@!DIsuqB`6s7rcr^=9O71l`CM3S;}Ujj4Q0HVhHu=|i)!^6qHd8biHz|Al52 zkk~MNNl!s(HNO4Y(3h5UeUA92P40y7Fl|X@`*`p`fb&Jgmu}u%XSR&9q4}WJs-{CwdEz(bR57YtH6nS*Ck(+~7~8Uje(C zz{p$=PG`x0K)_e8s?L|}ySm$3;(8#7k|W%XHPK3Hlkw(JdNYxQb`fCut!CSdz;9l> z!3eVc`5eFUIQC!E%dnjf@F9@Fh)~T+o#Fj{4vO5av`?>k^fJkbaehu%7Jl8yc_LE~ zki%ZNDK7jmi!OkmytMxOsGp{1L`YrKaiIB~{;oIoC&Lq8#jrF;WG3;35KkV&5U?L0 zwDpGg+pQfsZ#Y*$uo(poANpI3_q+k(yIQopvf-n-DYf;?8$0#q*%_<3OKIsA%abZR ztGhVr6;~a-rMv`sI>SpT1T7V`{+9^|%BWCoW_Q)%7`WroU#&7VWxTSOd2J?@%~FO0 zM|2andD1J|V_?(st$%El;mdBj4fsN+CE7R;zx(sDL7azb>6g@t9y zWia6tF|7>-eYW`P>AOxI0egGIDRGgM_&KzGB@f*lx?0^+&&_pC;-4y0Qyxk^u?h~} ztJgejPU496eRxqK&VHg6{S(wyVuXKx!Jt&O%Y|msR@#8<-bjMgu1!}-B0IeoWCVe#InJ?J2vM(eoA6( z(@Y!5ax|53g(r3Fc|!vr-hKCOhR*HNiNpCI9=^MLLy;H-aV6Paw?m!yPH$|A2Bz_w z(#=_z9*##f;=BsW9~g}YS!cKc9Uaam;d<(V@af@RC->w^$SCV)f>+mhOr z`*`l#BqNf_;U>4EY!l4_@Jl`xS=y$ryly^&oZo0#$xG!%ZkKzAhb_ZqyGD=E{kUIx zP{~&P%V6S?k3IB=faiG2eBK(9TW zl_huLw^GIQ#Mr$wp017<1Or^mtP!`it<8G~@Kt~X@aSW~IUWS~l9Qx#A-VHEU9DhC zWZ+#Fdg6pRsDC4j7_Dah*78@nY1il90B?~s^*PrOc@957 z#5K3h;9Lbmw~0S*7y(|Eb;zVs^s41=u^*nXBFf|oFg;bCCmN@VZrNwEH5ASBN)xfgzBEq4!?OB#r-mH!cU9tJ?8-IsxX4R8`R+qPkk*xg5VvC@U;C^t71#y z%1rQ>K?wI<)G9py44!0@!?tV`-rrn#{be5sYPh^69$p}wmJ6G!<`x!lP5jDhS3+JW zdBm%DEXY4-<{*x(Y40*`>Eg*>b;JV&#>O9wPr3ReY`;M-p?iurPX$x{ZddDT?4f4u zX!(b3;$^0QH+b~bKG?i-!55JF#ai^cPreodCzbT88F?Xrv_Fq?(eo8^9|rfE?n6i- zcct?m0(akpNk~^(#Zr%@8>Ck;{xQr@t{JJ+Gu_iF-)LK)+zF@Au~K6Bq>yd(DBo~5 zBaVDSa_0F=^cI zTR!T&?s58AHq?7~Xhp!aOyIr9FqPm&6tD#JlWPI44sUamU*U>|VZ|OGa`rcN_9g(X%)SyO6sLFGD;AA|iKdYvdYv^}eUCamY)NV^E4Y zS0~kd)(n?hs6UKMb{OBJU@wB8N;2V_qnP&Z>Wd;Vs%JjelY2_yq55XppQl1Ofj?xu zi#1IAe1a&@-0Nkk&c?Ns8)yBm+3!Qy*X&h$E)4LAEh}wz^lL_a^;GPHmEVlo}wi1=lU2k{u`dBZ=d!VJ~Xpyf%oZhiqI+g zjEG-UIL0ntOY1M7f7T(lYA>iIMi)i*wiOoCxwecF%z+6yYy3!oDcCEwi_j~bR}hW) zQF3FcX011Qg)uziI(B^DQq#cn+!y-YGa6hBgyCfwdgOGEUP;HTt0t;`F#xS>kx=1$W9 zH8;YH=H9Lm4B463YL&n%csDmQ^hxzk5e-<;?+q)w_sI~SY@59DF%?UcDu3I9jM&5k z!qZ9fO)58>3Wc|g@6EXrOnI4o#iM!+M_Z#%Y*=otu;jZtHJpBCw~Fwf+p^Mbc+s&& z>5j`{tCB^%EhTB&?Tlfj8dfKrOvWs|O$9{HoUWPduZ2-4Z)(@DzX5jUj1^16^iFLvUg zKs(l3T*d!T~ z&fD)&41=APxduDtu=^(zlo#=E7KYOkB*u*Khw6AtTh?J}IYd`S0@wvMF?a6;+~y8( z7uC84{y>W%F{?=F>VyS=CQnmCo_R^x+!WRRiv%qXib+QH$D*u*M#}XLIQeT$9=BQL zAzCDn8PAZ^YgK$G>lEX{zGD3$9rQ{lGfViAg>50yIQ=$~)DVjGs%HW}Gj~V0^=i(3 zn_I*m`<6QQ2)<*K|EebV^z6;wBM^Mz{oNEln60H^vhV9Bazz8j)^#*SQf9Z=0~b3z z#Ol5S^KRWDvT64b$@LX^%aZvM+7x3n_+D(d_xUpKDDWMV+`A^3GxNj$w0`ew_4zhO zdJUmmd3LcZB2R^6QttCv+|+UGujhWGUAx~{vkyFruT08423D>|RO5nAd5+m6 zHMk!~eQ&MWPhWh5aNeV%J7U#RQL*$;v?Jtt3M-F19N%(%j7T~&{Mk9gKmHbyN1$GX zp2c>O+Q5Qk_EMn2rxS|vQ@G@2wpPpOh~n!lvdg&5d+6zDJ64^BblJ>A+DH7T=U6Bj zl@w7Z6#QdO_mOo=CuxmNhk*Xec+RKp=?zMOdXj*b{2>`PbF0mmZS~@#E_Wk=G#*uJ zs&76WYWU#uk_8(F8kh?^Z|pWXsLbePauZ8KsEx9u4BZXJSg+V{t`6Vy`0-g@eZL zcha!kX7(}petvclhych(2|%@|&Fv9L=@KGD?egi=>AY(Y6FK!NR>!t6D1H2u%Z)ll zMtiRhTP=MYl=@Dx-&o81roo(|d~o^*%yMP`<*T{sGB@HXe$JYy_~f^hwAAnf$139~>2a$JPuk2mK!~|QBc?icT{GkE{C|bG6QrbM z1^!kPEz}St=-Am!2g;+$)!^mDdeq2xxMkMvu2xq4y{U2bYrdq3pTc28vnNiKP6@uP z`FuvknGcJY3nz`JpL$yW?ObG{K#=&pOQmiq2j+mniWYB_${|Usb*i^qKxSjOw?iTu zQ+%FGYI1=n(O@GU&Nx97Uz$Z%0CEoQpi{7)emJTTBUDd9k}b{=K2*1Ox9?7KSHvlk zB&!o6TOSVC)@8?>;6<0Y=}SNL-&3BIo&q+5%8vYp&dpp74^rhRQrH4T#u5kzhQHTh z0`U_$S9@yEprLONX+xw4jbd&U%>xAhchk$xA+T^7JN-pCeI@ovb&YVpoeW9;KOKu& z(D5y-!`k)$TcQc)m-4F=hX2V<13nOMr3!=Z0Ryh@0oAlh`0c7Q!$Pt5d8m%+*^#@_0f*q8{l=5x; zotC1NsA`SBTGd3^NoiS79@30?Jk4?uC0l&pl={SjIb(O(1_>xOfRiO^!vw#oN|VUU zM29_0jv%M&TICREMZ8Io`*KA>C{G`vcfg!8WI_Gjf2?sxVD{LtQm@>XzuzPE>qTr> z?G+~Nu0wr=94#u3z{Bl0OG!q!BquKFZ%d&(L7ddOhQsrWVyuAO0_PT#evZueZEruvO^~UC)B~Y57^H!vWW@5az zRv^m;jv-w%^15`6Ik!9jUp%S7HaGY~t6_x>ox{fuG=-MsCAF1a=3| z`*`s?0EE4scMkn<3uXr6+`b(1oz80zx8D!+E%GC?wOjZ#%&kv1 zL~bJh0L3QjHX-oz!N~8JMeXgqo$cQuRq!wE_(d{j`q^dhtk*$b^U8wS>T)Q&!O!R8 zAM6eL1D}X6cO6yBZ%3*52T{3{xpB6dPli(UqayQNkAAlUHE$!5zR%Om^|FT&l@w2R z1PdcwnT>yuc+kQ1KQN7^xN8&X^Qxr}zINB4z?Sxe74L`XLw64XT7clL zik;GFgN;jI=@KO7VCNJucAgeqA<2Z zsX1_aP;8!3Sx9-Xe|3xKCf%X(v>_jtlgd+qmsnMJ3v0}dm(39sEoWb$%oqTg86jB$ zef=(^COiBeDalnyCq)mbH@G3V9$%|{anvYm+i27#d->%P=fficclypcED>6_2@zzD zT>gcq8Hh_ZQEdBxg)X9-T9qvv0c^wZS<@(|m$5y}W~aeuSYMIfIn|qK6;C{3@nS8s zs2~F0GMB>t?92>hGt#+YqW8R7AX)hXIa%nSp{8DaA6J)-bKl8hk!%AtZ0hQDlhi%_ z>|6)ZvjgIBv2e(#0k#T*Od}Dg6UT}D*%3TG;A0{MWOODF)v|T?drH>BaRKKz^hqJ% zkjG9NI7A%8A*yD72d+J^;tFL=XBHyBv$-=FIzL);~0uy zmE8WG?o0Y@=1;#;rgF)`WJUL0BQaUcsgW|uxGJBR>ti4l>;>lj)X2-te{;0H{LB>Y<0wSL>)C3cbZ=D)cUvyyf};N@;}KRqD>kp9%=;IByCX=`i8ZtwO;vE zX&Nn~N`{k=*ldq@F;ePD0{cE{jEq*AJoQob#d&-7b>8HD=(K`Ql<8ac^vmAaTr|Fh&f7SHuK`Ct^udPqZO< zd>wrD+`I$+6e0`^my5s^2fPWMj09W@F0RejO7EX0&bNIr6PMAp>nl%C*YihXx%MQ( zOb>K?kl62+y?4bVh0L8ej-Ai@pkY!_xPsn#qY0}KUwGY?Or1TVkTZJ)AyyH1 zO8`3WPeQTT{9r|&c`uX$gY_lnerDT((WdhE>Nv>WzjOY>@@I7cWX4jLidbuw@kWkz7W=MEHH=k*% zILtzwt+$8llHsaA?^w=k6;Z$+JY5@fLt=-dYzV+7=m-mO6yq3HCwC1JZYyhlhDE$5 zZ`bt=(8!UcPqftk{er)OpiF^x_akoXK{B(fEO#ZL)AoaBB|N2E_e2eS}C$sMTifDAn zFgv3!%x9G7ZAOMTjBA!DFP!P1B89EI!sGEvMKvL)r$z`9rw(+f>6bRY+}h%xD^3kf zzm@O%j1O~?^0wRLYaQYzpKTrsl3 ztNYp;4 zfsxrFG`O5TTdx@-l9kCmr*UhT=Sn20e-kjf58@2~nRNs6)OYS}BS{@U=JM_17F@JcD zTQ{Sd=F30yM{{GJq)867@p)mLsQ|~M1+l7%ICoiTl-wEbOzGu^E6 z3vE$l$bsCAUH^MHw*it2fq79){%6PU@0uSfM)O0Ekh@Ifj2U7Q)gSf-ml*jx1>}2C{ybWn;px72hgmWUk zt#Bv~(-G&#nkW>h)WVoLujSQmFw(F1K7*cBQh*cU3+evSfloB5B_IIBrVuwOtn!k` z9$#}82sVUVa#;SZRYLKym03`Za9;qu#VVGP$g?cuNf4BTS3*k%6~%5{>B*M=J;#I( zup0VviY#PjvBxzclC@B-u2q+`<<2ho=j;1mu#XWJq69HrkkpvEq$mYrfj2MiwmpOH&mgWhaJlvM_Zkz^%mtpR8jFY+Et_@RbwU%hvj^A;`$m%I z@TxvR4HW`=`7Hy)ye2AqQNV0@(IZFBcb*x&n)&?ssrce{RD;dr?n`v2;mO0wDa*1L zk*~_;Sez=t-{v49o|fkbD2~3TdbEtlxCYIc{D<8m?w!qPQDRvJ0dh|kOuKs<+I)MX zr@XAKU!p9_0wW4EiF^>q`u+7Qu*)78kl)vNUK?IA;cv$A>aM_QSv zyt~7QZZaHfD<=Ab?TK9N7;EpO8lK z`llEEe;c$CBrvb^m7iuQVam&^ZQFhoA#=> zPiFMU>>wLVfHeUZI!F5lm4(MRGW_crhMJsvlT7$OynZ)<3SW^hx=jK5NQ-pzb1PWOVDK~Fls<2Q``N$F=pHi2s7>9p_TgZ8X_Tlu^~VA` zo3)O}v&Xiyvvmzq%#)l0p$&eHf|E=Am%b8GR@$#40v_-(+r)&&7D-6S|CV7)U>uEQ z?N5#?BMhVV&}w;)P&qbI%06_l%fV({t5?%djt(L4)kFthcLaF=cT^bDswV_2d} zYV&@M0rRU;vHk5ZE>V&N&FF@^W(oU_C&ZL8@jx87BDlW+^rjp~ z_9TTWG9>V4foPeLfp$)Ew^eZNi+61i>xX8x**)PRdXt65dQC~t0+5VM3JLbVt8B-?Yjs40 zI8M+%piW#aOW}j&W@kVPUdRVhpX4O?Ioi-FK05p^85Y-(+hS(BsR?vLF{ivR=JTW! z1|y<(X72yx991CNZKV9`9k`;yP6ap-ou3^>C=KqYD>b@66!!yzDbGkT%5|P^;0v;xmCbPvV^^q^-0J*rKy1TF7(`DklpOxq{`ZtobPD ziXZt&>hG77TAlyZcmOv<%&zcn|cH1FBEg}XMB*E zi)pxjvx%mJ!VzW~%U212En6Yj(%Fie6}%B0E%J(E?9EO3)md|dl_mdP0oBw%rU-(< zal}OC1HlSwchAZc!&TATtGOog49;C~$eKk-LEa;eY(avcNmy^Iz*l6xQ2FGovTogH zB;H+c1CKfQcqggGK+uliiGL@CB^RRx>I(-o5Pnu1x4FN97iVL@t}HnbT%V1K;p5*n znMz4wjAqUT!odBOBB{BE=Sg!n(P%Iz`q_g_#@mSg8-i8IZ9oSeWexhDPJqh3)SYuR9vupHDIy(v=<(X;P-lR zl8l}DTp!H(HAeZ30W*{`OzX2fr`O~@KzrYxO`~?;n0PRM{259PqC|rzhoxR5Mf@vn z>t09KMfipZtUmYjHSsfQMt{)9pIRz<)PZ|EOiT^{w9|GMWuYa%g<0pP6}qtnkTV@9 z03a{R9WwzR;o$xX2mvgY4>!_AIBp1>RgVME1ylr7udV&;#NuoFCT(^dFq~ISX&%jp zAm0fzN&toi$1|PELH+^M;_k%&GXS(#va+?AW5ofOFTGs%8-+WSKM3TcpC+*xFmQPX zI)W0*Or2U)q;lp-0s|k3VLL2y&L1!zj_uwF`g-ELV2ZJw(K3O|gT9^N5 z?bmL2Fk zpb+kHRQoN#ZKZAdkJ@G*0r*y2n-75?c%=4OoUTnxD7@=!Ii=edF63y(Y7LBb5Ht2@rPUQLcT_zrn>&7T+s!3Pel0&|(UQ$a5c2fu#so zj3hzG1zhjpps`<<0`cDtzN~zP3B&QwHouvn)NE|3bTvyTvBaG?Z}~###%YQ>xdaAN zpska?WC~;z7NlyDCRaNC!aL7`93u(X_B$dFcEH}DM#|P6rQQM*ZI}N;9=UWjfh4%=(;QA!Kak9m}Nphl2oM@?g$RXMS!!qtwLEP0dh5n=^|5K|69Z3h)M zsEI^`I2OWTAXx5NPMn>A-*MGfU27?QOx6i*mXy#36~ znuxBZ8nep1-(bkf5E*BxrR6QpQ#;UpOXG$D7IgrrEYc1KH6T_`_6mK4+gW3Ina^^0 zSAR+XM{+69ay04F%stLKhhFI(HZU47y(fd7rHr3jeGYLh0I0tLd)RIjARvUq=o)Du zQJY*Zs@7fXjVYJT?kH9Ph|c&pGf+5xxfp)e%*^u{hn7_eBT>wBZ;lL=XG&}jcUD*J zuWkx9&jG>g;aNV2n}CyrFb5h;C@}xhKqJ~fO;tSIDCa$w?P&K&4$%$4ZN;fypusMH zu*Rke6B%cFqx_z+pJWiQ^>QX(dIBiY4S;kX5(`U9M!<(w_8GPOI1QV(D3BEcI)wy$ z^vfST@BZx}E#{+2-GT+i_J| z6fsO4o+t!MRvB@kQA}gYYL*xbXc5HY9H6&B%0pW>#R>@BQ@MWZ`~@~tg&2g911;rI zV50yNPeezZQ2w>x?eG>0zG>ic80?Q?B^uSfn#sUkQp*y06<4iLX6 z39AtUYm@C=Xdo07AA$Lm?Pg10b;7H2`xCqIHF3C1L^KsR#Y|>VhR^|O+b!ua0^)$I zEXJ}ziSVnzx6Az<;{tdA*HbKRk>P%hZ|Lv?cCJIt4ga0C1$dEBD<1&?SKCq~Nw30Fo#M7q5(x~nx$UKmP8|EPD~-o$A2 zdi~zZCgO^?QG@z(SAi5RyE#6K(nS1hGU;*A6;C01G+rv6vhXPp-UIs{kaemRnqQoH zx3W$5`I^Ss<;G06*r=NFC{&(O!Td`g{vfu|_~DiR%f!}F9Z|`+YN;cyyve@(w!le& zR2{aBz2eH~4F!L0Y%94Xf@6|Nf}tf0bev~MwLV~K0k>&1BqSefKXZ*63 zCG%ZYP8+mo!35}ICTC)c{|P#avXJF}KCQt$$dm3d`!{I^R|6ReR0BhS5L80_np_`4 z%%8lA>@P7q9t+ZdrJI{GazLSsDjg<8`M=t%2tq&43=KhiCQ`^1buUy)3)`UXA`w&kA4(=y zKm#XFtDnRr@y~((+5A6G{s<8(e+-+0#OeR>qw*R!UWhs+a`eB&gZ2OC2h4F`^7$)l zu|v%1fBay501%fnKG+u!5Rm8o_YXBBKfRlF`G-IM&kKY^!I=~W8Euk)Z2Di1{`U(! zq=^p8{#p3{diwt^|9^M?e~st=57+fRk?ly>FaBHe%~bQ&xCc^$fQ>h#0&$G<%+UY|Afdc<)pf$41`XydR68_o?kq~!QKF~;IER`GMUo`OEghXLv@l{Aq zU>Np}FjsJ!UmHn-bIElr5a+k3(5#};%x6sl0|1)xUe@Snjlq~301=T4kd1z>-~IKU zV6*Za0(aF71ES<<$8ti`O0CTUo0H0m)ujJ>Q*f5ocu<7kC z(LPjyO?+S?74(f^z*9~tM^h=_9PnRNeGr)512e@qF!HWaxtI>73Mq$vKeH&IuL>rZ zE9wI_o{_G~rckS2kTC>Ne}^Ycof5_5W>9I{@w4kA~9@RD@H) z*0VuEau~=tyiL#(G664p7)XewCfWl73cDSK@eDu-z@NW#p4$b9*nMGYkWderjgV*6 zL)tk1p7n@^Ge%!uS<31|9)$3wkwU>>sj;wZ#2}h0;Jvzs zO;0ud>Vd{FM>IJ8;``PcG3Xqaw-C~!kj$Y+Mm9J*^+Xqsp2A%Ucbms zMEU)<7GUZp118e~$4NR)P)d>kA|DS^zyy>pv%E7lU!c6Z3z!Tr(jqi^ASwKw6j+hO zv)qG$K;zQ1D^F|k4<-F10~v{D)&m`U4;;7($HlN(CuJNc=X{N%!WV0yVjjr}Gr9M- zs-Dc?|-XNnXvD+d+ z#%uA#-v*`3NHspYDxNvCv+ssN@@VEV@W|Llcv}y!F|KF>qGuAazkr6eiPhS9Q2ZC#erljjMuy5YjeNHWpuaWq%9b2X+&2dfM&?qh*?y^tfM;61tCK9a~> zLK{#$lfmhdc0KRX{70RvZkJkC0?G3+@BcBL}^gTHCOohPC$0wBIANut|1RsXL3 z54Ya$%{?>S#t4R`0~m7Eyom!@T70^llr-~KiAnmYxtk4H9+4p zuQx)xUIF4^gSM(%l%Ra_ffJ!{F%;U^dCajw_PSJr-%Wn9IF6s1$Xv=#Z1@)Mug9HR z>mQYiAS>WRbXjp&5a=65H2V2sT>nu@+b?BYL`F@qFzT6IrlqqZ$eRZbJsDl4v8n49 zU%ZvQ%~egli1JRjCda`wJ6<31EQ?oA7RT;I?MlD4NX; zI<59UtZ-;5sfnoGRn@{P%PJc*S%v&OXyxJYledSTcVF&3F-YIExo8Pgks|f=)<&ab zwTl?TrbL{H^>?jv#87NBPVfcI~FLp2(HtA~0gSkvuFV;*a`I(pvmw7H2B__~?7Shf0YFA;EkuHa6<6k4cR+8=SNJI4ib zW7pn$xxR_w&&nUSR+IA3;d)W|>~tIF&h0-WuiTJX&r%1%z6dV5I?UQie@khdW>`l@ z-evoX4_vfR;n7^2t%)8Mb1l7~R-QJzHr|VcI+)kYSf&TGpqEsMiOeEuALYXprh^JR zoMXbk;0>2Ojr-*%?r(rU`u1e=ir-@CuOj@Mjy8-DVa6kuf8Os}nxrm?S<9m@P+;IX zuLtvAgVLCfjh~GJfzL*aGY7B zJP@h~XovF|U1*V{IMQiV8Kj$%X(=wCqDb{cCX=AAI?UoY>vsZnr0s_m%kXiBzxgd2|?cw(+J9Z^cpS7;L43z>BtjRw z-e~WJh%&}dw$44hw?VunqyJZuDJa_zn(2vdUx)K*=6s|X9ha5M-GR)o;WRfe;nTu= z#hB>exz?MaWuCcOWp+&>6{$_nYI-b1ym7ic1PUKX()WU1WWh7MY2Ux4BnXv7^t18t zQ3W)c;1z_KX~@3+*h#i;`*)2_5)Ax(c5lCOyDpdjS3xj<>!}6{PeR*WAc!&(9Uujj zE?V1Au!Gzf)c)%W?p@uNbjtG;obK1nmNNMfY-i&H2r+?15oeb3hlM zm|PB420Ydb%ShXDt#<_l5`l6raWwvti7*|~5xRk-5wBn_Az)k{up+cBlNl}LOItvs zk*6X%z)ng*NYQ8%9SmcvCFSCWZx1h7TVdXcmg z?7Vi=RPTi+4;R57X=jbOq$t9KPjBOATjpd9@3%fkU5D$K~4q5evE$zCl-wgCTh{6(7D!aEcD7P}TOK4p%v27p zYN#$%dI&{)i9gC}ZI;MA*l~Ljq3gVIaqs&3KWP}!njO@`;sUKH?`*Unsj~w4-6{O5 zdJZ0!KL+@9b8XT~(cDTdU$ZjiKj(7}UEBh_ak_?H@ki(VZ8AnPD^dT&TyurF!Tg)e zWXg|XlLI~L>q2y>QIECZ8r%;z8UYbV=?|AOeq`mm=q)(A@C+s*? zT5CW%6gyqx zagS^{CAQ4xqe6etEec{Q_K5dQLXnFcCYIyGQp4oGQ77)5nW!qaGh#{;M=hyq^1DQGwnlq!7rV z_!_C93scNaVHckF!pH^YH>tB`;v1KnL7mteQ;WNN7}9~UdTM2NmV!m2YT_^wRi_u< zlmxCr=m?Ek=#_Ix!{wA9Kio>Ivl|@3UWrOV+@r9Rk$KC=G2NuU<}%U!&`Kn*e6T+4 zdYbH(YNG>vGjR@Np}2xvUr6m^+T_vsF*;-4Zo}p1>*Lvit3!0vQNLnIS?$s~?f!kd z$KI}*yHU9Q$f4q}Te#F~%!iutuf3hA35kV6Qg)DLScs4S7%Bf@aG-prjcnV%18&_z zD7NybV!Es0*9sNo2qA;U1^Epz#u}Dm+-_hlBK`Wj*a%z>N7DPekb%d^K8$O%-nqbn zNSvv2E!o_ZK*?{**V3}s7cZ<|p6Urjcbibq3vQR2P{7X(-Nx96vhO{Xo%|_=G|iHo z5x(*4cvaB)o~rU=d^$sj`-9*HD{{uoB$6hfQFp5BSrARvR=wsyTawu~y+!SY^e#rG z&KRdkt>z)s+E3zRZWX3*+8H}})##_nb>In;ib z1PUnZRESe%VKp>SLQJuILo(VDRI;g--fc?s49%S&P7eNV)UP=^JqE}F<)EYdJL0qtQ;|Qsf@}cT|H6s z%&^4i9U4)iw$cLmds4+LkviBIbWXgJMB~l$BuePpHZO-=rRoT9XVp*3rX}pBz;0Rf zWX)zRs+6GmsS1^c!>6ZKa-c|1l~pumko^MvQU>1|G9yKT1}`eRyr^_%V{uc%hqX~O zah9J^*hy_9O%zIPNVR$COsAyYOvOAfz}IzaJ^Y1G+E{Bah*Y|RaW_K{l5;?(Kmrum z1Zw^UfY+x^6bkf#`CbSG=4li8w8eEcL#Tgw6fEz4RBk%i^eI0u$^9g1ZdT@ucu=}^ zn?TN7^>+qcYO?sVPJ&O+3pDiwB@6-otGk5>6r`5!aIq@UiN<+hpk=5wTLnH2}=<+;GGoc5tW zt`wylZ{NlGaWFT_?P6pFzssb1oQMODj~1uf!VX5fWv_~mviaUaH|8fAJl&OIj%v4j zIs*es25vHzy0rv{l1t;a z?@;XAe3#D|Z60cMTIjY-?#ai-6h5|MSqI-d9#_%|E3e+cXbki1Pa_`UQJj_`m3C$s zI^VTY_oWMoOK$LCxuBF@|AYg$-uaNO&;^X2gMnVhTI*xNEF0HT4DhF=>QwFL>ySe8c4Q4ZrvM9Qi?_rTAD0LWnRGPWWmTvstH~kFS-@ z|2S~Pf>h=`e=zk3ffa2OZ6AW3XF{kIpvC@PIH6CfurD=Jz0xLCj$Wj)AEJm(xI`hB zhqMR&zo~=Khw4eC#Y#tVNpYZ0zOHL-=|zB?j*|Q3-bj2=d*n`7>8Pohfp-p&{T&Z6 zaCz-4p2;b&ozP`nrZwm8>^zW?E&CB$UY-3wQHtCXt_v%{%pM~&+1zNvrbKLwuA&fo zO$bL6%=k2uV4;XH_SLHCq%Z)%5EmyL_eR)=Fmh zd`g^qHcBbpI9|)IB0`V+wE7IE+B`MEACYl;-kqSO5tB!f2>!^!iUDV@%@Db; zDA{%-`{O<@O-p<4sCgg*8YN5gUF+jkqQFNrMUi0K^u|59teaV}|EIk(|A)GJAAd== zq)0+FmP(S4%55J;356&-V=K2Mj4d;U!6YsAX|aS*2{D#zGcgz=jn(OiKH{&roYA@#{izsAtf z#7CP<<53v+i|z(wosi_-f^_P16k zuiBfrxniMk)N=tES9|~*lOi#IjBQ`4GJ1ONx#LKjc(`-Z4r585aTcGv@YUOjZ21@P zWeyRo5S;@p@89*g>O{bF-1%?N=EpfL*JzxkGR+^lA?nc|k-2-GEZuk=6L$2^B+_Zz zcxt=L$nfN7Z>8)D4K&OSdb&p1vl5w>KP~Gdq)K#>uPmj#MYXRLQ-i!mb9o7;b~HTqqH{pb9KEQElAdLP$afMNU^ zIwSZ`_n#$vZ(I@TY?}I)!2ahGb zOpxhMREeHnpE9p}TXLX5s_>-3^-G7x+=vPg!4n+RZRC32^9zq2Ti}5DJo3zD&5Nc5 zQ}JG<)6l=Jzc)-YHInP4)mn93QSqRLz$;E40xw7?JU_{gIPcmhE`drwArX3eK|dYS z`$Qb&ek5YP#J&m&^XpBcJncySkRvjeX03JTYL_>g58r%rSL>pdreDHoyO7W1m(m=$AP;R z(ejdp^EDOCpY4#R^5Twqylu`g+mt%%=j-FwqYRUJ_`$Gixar_}Y5GYnq}~Bhlw*zL zR>m(-85q}M$DP_+HQ;Z#foNkak2$X0mkCPSXILkbZum-71h9}NvRjCJUg_j56%lIYp{yq7AEHY5cRI7BY z*FBdc1wK>o)MqgbCjRF&^CuX^kyCEFZ&gy5SeY*!s%y*NXjcnIq=3Q!Pal$_cwZohIPyNh{S!g3@#V0%0YM{8^zR~-sjb3&d+Uc=o8hOUbPVpvEWd#)|AiVmX`b_{ zQeoLzAlWVMVQxo!i*nD$%XIF|tl+@JgVHmG1--dpXT#?M*6zvN1kjP2zH=s#@(Lwu zce79g-=)HH=ja4M?7qY2{*b!fbb|N(q0eQ~(#^>VGEdGJ4kig~q*=T+tTM|tG|S&> z8U3v6;OBI2=`wlV<6H^u8suaI7?T=R@1-HJ|*MS`NP!1)n!pzpp!NIta3} z`_3FHd}beflT|avT3kH9p?C;+)$AVo^M2+ReI%zIb+Z!MaBPo*rab%x~%J}Q<=7J0owcdQG z_=VzPC4uD6;xMS*Z&tFb-DVm^E64j4GouJu-x?Q&^|l&vMK3;e$QqL+67Lt31vkw2rWRXCKGHG`Y>;(fmIfI&he3{TXHGx9 z6B*OM`v_x+zn-j%OxZhV}r#-`&a^!t&`NK4uP|&YP99%j&h%e`=P;eUFDk#mHF={o|wQ1gX@oPgy-J> z?iF68Y;)dz(s?lFVHJOG5mO(&aYOw8d{!X^K7xLqFNzN64y;+X^Zg!U##`0@qCbX+ zE)`WJD=7s^zvZs*>d1pX&yQhA>N%O75b1H*;`hOKOEh6><1W!~BE>NG_U$3j-e zExa@A-Y;B-8EJK6+ByJNtt7M1>KFnKwE{>sgYBH<^ttw?B?;QTk>qM2=Ff?if|;NW z^iZa+QLP!`w%pD)V&K&hZQQ8@($}aw)unMhe0$TbbP))$f;(C%K^*JD(O^HHem$em z+t=>^5g!RhF5`NpSZ%W~^!!F|FRC(GZOAt&Y%3GTP??VpC<=cFAj;YtbVhAw#RI~) z_t2lz2kZv}EENo`PFolfT#O$Lj%jGf7M#fdi)>D|ex~}8gMtWKtRAL2idhA9ztzph zCoouO9p8%RnY!D)@WreTN-`X^A)Xmm>gVS&U0F&G|C-=S9*Um9FBa@j-?UAk>I}MB zKqH7VWGk`Y`FYC(65Z4{BRYN7*BYPLj!w#w<;ikf>-hX0IE&kc=-HDlzT{DXl`K~A zq8U^Q2Uj`35G>qoUkjgDxgp1tc!g^nTc#s{H=h{c~|ubbufH%1>awskN7HAqA(;?x6VP3{pO4-$1l)r zQ^x)0)AZLH`A!w=mwE`(xv_}{0VFExIZx|H53F{2nnbojOyl(I1Ki}akvHWMY)*pX zfrIJU8ynXs%j*h}6w=SY?E3+nshZ5P>dKH?zV=B=TR8NEt(g^swigFx*6DX-F4M3T z>92{3?P&PgXmI*MWoQZ!l||{RE>yE$6F4POTU4|fFcgJ^Lm_nmL7F}Qu6u&zYn!%T zWonmp_{v{3OXP->l@J=e%#bNb1Pd>1yoYL`E~xHHq@5wO)(%D*WTC1gomL$+eZrjG zzFT!jBa@)5hD9>s>ZhH*cX*a>*3zF^b%>oIOzs~_Ei9bxiOA!Z#b{Ho#bdAXM10!p zSIsi?x-o!VEnUuNlNq0s5*?{zGyos@fx%d2mQxN(2JD}zRVnsruAEDYVwp;SQA(}H zGc~;i+i}x(Huv;&rVEdhkky4og5M|w_TCOcd!K4P%Iq9bwS|Q_Xwt_dl^`a-01Hb4 z1N;_IWy8gte9$H#B5#9hBRdPgdySkEPsc0TKaq7x7@|a#xi0wHg9SxjTu__H<2RVf zqQR1koL4qnD9g0Dj;2`M4ww9(H4Wyxe&~3Nr9Y`OQgOjoZ!~|6MM`Kmp4va6rL=f) z(`@brsCnGndOz;bS!D)T>`q6@CdmiG)hTZk+^H!^1IX1`(J46ok@}a~cUftCcD?FX z9uiR(1uo?P5^(y_j)g z7({2YvJzyKg=yUhM=;n9NlbZyul}GvthB$!7?K%UC+aFqeU(?;JH1mhQ^P7FR;e{t z7!qD5nneh%)^H9SP(ZODcpDRw%Qc(#tZLTiR$?`{v6YM`8ztk9_8hSJ0T65q=U&oe zbeu&aT#q2#vL34t9AmcxW~5S zQbyqAa*`~4(WRvz=~)13SP+zENvSjlF43|b%jbE`Z-fC;Vj*kO#@-?RW7GUo?rp7I z#;W7MF-sFta~=$GC2ZC?X({CARQNhOD0iZ`g@-^?5V;V;8k9-UlTd#6@pmnNEkl-v zSZpg?S}k_auH)N}4T2zp+-avq3`5pE!cMMRwe7$T57c; zxhobl2Wx2;2z;%zUR=8ytzx*m&RHsoQNn=kmxfhI6E>9rnw4|ao;~2U9HHXK9TBxB z-iht%B!56uNY$-!Qs1*1Olr!CoWv#6>91G|uGNK2Vinal7~sBHy_$*~^3R^wQn&2Y z4_fpkOF0=ALCN)`h9h#)@{D~u$}dh7*^J5dGm9Q+0INhER&gR$%f^XHXRiV#(v2Nz zQZ+1x&KFL}P_Db9Hy!^+=9zPPO~#tzvyuWI`f>RmTMA!Hv0`Q7S|GInCsY!LTBmEM zkD0YC)yyQ#A>g|yryBxGB@*3nt$8J>9?i{>t!D!wDHAbz>Ypmex2zG@u_)V1)+<^D zV&ph9v+grhr76czDdVlxqYWBcv!EJ5ICJR6?nxnxN3&$ONAJDWgMIFVq$(`gDD)F!wLeaOfP z;+16S-e6~zD|a${SA1RZ_yD6JNOj=ry~Regh8`QHPMI;+6wii3p)jZ?YN@Jdxsngs z>n8k*D19A()9uRXrk?b>`|IoQH@o?2m)nL&%un5v)A(vesPpFjpZPrwR#!T};o$I@ zvd?PN#}9lLt#)TFd}ha7Q9s6_EmdSYbbax0>{aj)EcLlCoMMFoJUY z?*zft=-0ZL^tQ&viN|a2pT1ygNsAEQ&kVDWiIPW>jnH74`v!Gl} z!5Pj6%{OK04A4VPpVrVUdyC6 zyZdG)q&R}YWB;ms9Y{Lw!u;WNYT_NdQNY$=wR@d=EBwd>{MZX?(*;2Iarq@b7+raJ zbR~x)hHRZmxy$-8?Fdh91-$)osp`B=?FaKr6p5k^&ayo@PcI}&GD#LqS!l0n(()_%sLJMnZ&dd+L7KJdcy@|$ zleBwWOF{Gl&gse|hb0MgF0`n4ee!{iapyjHAUeOj@hLfk#tPwg7>ZL$0L8LQZ#N%l ziL(YBmWL;piYK-{+I-Z?NED2kr9wl4m)Cz}EsZ})|D$21%6hdI=F>ag zNEVJ#m%SNKb3-fJ93irnWZp9Vj@@ok+Z0nzu4iQtFIX1sOQh6`@PRjHRY#Whc9K_H z*HYZC)p5SkVa}R=0M2en$$iYiNGIn3bD6VbYBiSYIJPnY%EOL8ytD5vC{2xiLvt|w zP2WJtbyf5q?Hu5Majk{&F_3`8@mHe$>tilVl(k8dtbwX%t!q)wd~08LVp3t519I@h zYA>T4|7AZR*7xT^TD*Q`HIm${FC1vN1M-)HZJ2jlD9S|~i35xaHV8frTEbzgQsu5c z`ZU4s)tpz77AM5-o3QBpRePoJ$29;F_bv;t8yxOA7v2K{L0qm&*s^Tfl@n^4gU=VF zOABKPQ@t&qf28nqFU`={2f9iSq%)wSiGK6sHV!fvs)#dXy9_OkswZ0lsbk-nwuVFl z9}g?1$8EDtuI}a37a4;?pOOpmLZ{2qQ8)Oz?MIbBWp*y{Y8&&@ud#YYVa@NH6a`jN zdZWs82dDd2Z{RG4t6luO21%BJpgxs}0o`;}d{>4#-H_dY25G(08g=>$Ir zd#TH;&M1(kk4bCO3o+GK$qy3v6wvmK{C8a1;b*n!?CMS$ZhQU74y%E1QB> z)s=acd%hN=S?yo7<}w{DBajnpHlUoNuJ97iZf~LykXX(WQYCj6<7zU|l~d(yDudFO zi#-(_+RQ3ffsk=e;h$Sx8T*#PctFZO&IF@j1K`rhw)~)`L$Uzs>540z6=00c}e}%n)4Y;p$2T@XHm9siU%B(e!Aa z!7s$F2QZ5x`uqW5>)h4Z8*vb^F%rgjKUSCOfcsc%9oJfJ2&uVgQGrh+OCoNuaE@@VA+ojYXIbcr zEC+;CO`))iSl=OJaxlh0Vkg%c!1o;t!VRYafiz%6gd=1!86Pp3Kbn8$N2MZuIk%AD zI_QJ6n*^s60R_zu4q-lW+5uO+O6((5j|0J-q3{8umFJB87WZ_|8_~Bxx7Jo`GpO@@ z)_Arf#6IvzyjUyM#Fn>u6gI&GiQ)?wAGNBVgZM-S>xo-MEJiiqM!} z$NL($s7lf;c(8rYAc3_ePb0d1lfl`{U)d04>`|+rpVz!<-KM7uy5!w*=?s z%$w+^METJI?>B8(0%9O)v_70bBKLO;^%gYV1~IXDn2(RndVahC^0x4Ez832VEI!77 z*g^k@eQ#ntYtg6{z((gyMC4VM=Jp;god46&>4)s*u($q}!#)>^k{kA4MS9!djrjs> zx2(1RFy7%hOHclO#-WIVZh*IofzHlZM9fseY6mp5mNR&YZa1y=#;XN}W$)E=&lrfC zP*riEoPi#SyG3&cu+e^7j!<6z8R;#Sr|_xkB6nI>G&_l17*WR`Enb=-=X)i*^=c7i zf?hI4^S7IC^6ck7!alKe@dGX{Og^A#fFOCfsZdJc+)0nzgFv^iK(`#H7Tx~t)*N5r zx$=RBJ(l&dgmMN4!%hI&uK*c|Hy+tr+V|@LK-1w<$L62&0b~EWsSB90x1rc`|7rTg z!~LaTTNmlr?TnCtc?s+PJ$_qF0L}N0e;)w6Mh-(d=UCC*`e(Sxy;8sXc}xcwwq5^T4d^PMxLr7_oBwIr19tmgpLRr4 zaB-QE`p(4t=fx2%`+k2X@ZQH(I-^&I>>dDd$nX0AVf?30+}pzV-vDh3V_P$}r^&X> z*fyZQmz?d@WP51^JhE*+<9}+fEsWnwB;ZnR3u9Xtze9`d*yQ)B2}Bgz!q^rDpa$C^ z{D0J7TNvBI_$R^e|5B#QO61hJ9XkY7u3o-q@vob=odWzfVYr-Hw}_8!W=QPqx}+rrou#`d { await sleep(200); + SplashScreen.hideAsync(); + await sleep(500); if (address) { let name: keyof RootStackParamList = 'SecurityLock'; if (credentials && caHash) name = 'Tab'; navigationService.reset(name); } - - setTimeout(() => { - SplashScreen.hideAsync(); - }, 500); + await sleep(500); + setIsSplashScreen(false); }, [credentials, address, caHash]); useEffect(() => { init(); }, [init]); return ( - + - {!address ? ( + {isSplashScreen && ( + + {`V${Application.nativeApplicationVersion}`} + + )} + {!isSplashScreen && !address ? ( <> Date: Fri, 14 Jul 2023 19:43:40 +0800 Subject: [PATCH 372/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?fee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Buy/BuyHome/components/SellForm/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx index eeec427d7b..c49f859e8f 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyHome/components/SellForm/index.tsx @@ -33,12 +33,12 @@ import { getManagerAccount } from 'utils/redux'; import { getELFChainBalance } from '@portkey-wallet/utils/balance'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { ZERO } from '@portkey-wallet/constants/misc'; -import { DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; import BigNumber from 'bignumber.js'; import { PaymentLimitType, PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; import CommonToast from 'components/CommonToast'; import { useCheckManagerSyncState } from 'hooks/wallet'; +import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; export default function SellForm() { const { sellFiatList: fiatList } = usePayment(); @@ -51,6 +51,8 @@ export default function SellForm() { const [token, setToken] = useState(tokenList[0]); const [amount, setAmount] = useState(INIT_SELL_AMOUNT); const [amountLocalError, setAmountLocalError] = useState(INIT_NONE_ERROR); + useFetchTxFee(); + const { ach: achFee } = useGetTxFee('AELF'); const { accountToken } = useAssets(); const aelfToken = useMemo( @@ -194,7 +196,7 @@ export default function SellForm() { return; } - if (ZERO.plus(amount).isLessThanOrEqualTo(DEFAULT_FEE)) { + if (ZERO.plus(amount).isLessThanOrEqualTo(achFee)) { throw new Error('Insufficient funds'); } const isRefreshReceiveValidValue = isRefreshReceiveValid.current; @@ -210,7 +212,7 @@ export default function SellForm() { const balance = await getELFChainBalance(tokenContract, symbol, wallet?.[chainId]?.caAddress || ''); - if (divDecimals(balance, decimals).minus(DEFAULT_FEE).isLessThan(amount)) { + if (divDecimals(balance, decimals).minus(achFee).isLessThan(amount)) { throw new Error('Insufficient funds'); } @@ -247,6 +249,7 @@ export default function SellForm() { token, refreshBuyButton, checkManagerSyncState, + achFee, wallet, ]); From 3e0eecaf4fb5f25b8d68003d5555fe3263281ddc Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 19:47:36 +0800 Subject: [PATCH 373/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20fee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/Updater/index.tsx | 3 ++- .../DiscoverArchivedSection/index.tsx | 1 - .../js/pages/Send/SendHome/index.tsx | 17 +++++++++++------ .../js/pages/Send/SendPreview/index.tsx | 18 ++++++++++-------- packages/mobile-app-did/js/store/config.ts | 3 +++ .../mobile-app-did/js/store/rootReducer.ts | 2 ++ 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index b67af1f140..8cdc61e625 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -19,7 +19,7 @@ import { useBuyButton, useDiscoverGroupList, useSocialMediaList } from '@portkey import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; -import { useBookmarkList } from 'hooks/discover'; +import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; request.setExceptionManager(exceptionManager); export default function Updater() { @@ -39,6 +39,7 @@ export default function Updater() { useCheckManager(checkManagerOnLogout); useFetchSymbolImages(); + useFetchTxFee(); useMemo(() => { request.set('baseURL', apiUrl); if (service.defaults.baseURL !== apiUrl) { diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 701119e624..6a8e5de226 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -18,7 +18,6 @@ import { useFocusEffect } from '@react-navigation/native'; export function DiscoverArchivedSection() { const discoverJump = useDiscoverJumpWithNetWork(); const { bookmarkList: bookmarkListStore, refresh } = useBookmarkList(); - const recordsListStore = useRecordsList(true); const [index, setIndex] = React.useState( diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index 35b59fb997..c5995082e1 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -34,7 +34,7 @@ import { BGStyles } from 'assets/theme/styles'; import { RouteProp, useRoute } from '@react-navigation/native'; import Loading from 'components/Loading'; import { DEFAULT_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; -import { CROSS_FEE, DEFAULT_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; +import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import { TransactionError, @@ -48,6 +48,7 @@ import { useCheckManagerSyncState } from 'hooks/wallet'; const SendHome: React.FC = () => { const { t } = useLanguage(); + useFetchTxFee(); const isValidChainId = useIsValidSuffix(); @@ -60,6 +61,8 @@ const SendHome: React.FC = () => { const pin = usePin(); + const { max: maxFee, crossChain: crossFee } = useGetTxFee(assetInfo?.chainId); + const [, requestQrPermission] = useQrScanPermission(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -152,8 +155,8 @@ const SendHome: React.FC = () => { if (selectedAssets.symbol !== ELF_SYMBOL) return setSendNumber(divDecimals(selectedAssets.balance, selectedAssets.decimals || '0').toString()); - // elf <= DEFAULT_FEE - if (divDecimals(selectedAssets.balance, selectedAssets.decimals).isLessThanOrEqualTo(DEFAULT_FEE)) + // elf <= maxFee + if (divDecimals(selectedAssets.balance, selectedAssets.decimals).isLessThanOrEqualTo(maxFee)) return setSendNumber(divDecimals(selectedAssets.balance, selectedAssets.decimals || '0').toString()); Loading.show(); @@ -174,9 +177,9 @@ const SendHome: React.FC = () => { ); } catch (err: any) { if (err?.code === 500) { - setTransactionFee(DEFAULT_FEE); + setTransactionFee(String(maxFee)); const selectedAssetsNum = divDecimals(selectedAssets.balance, selectedAssets.decimals || '0'); - setSendNumber(selectedAssetsNum.minus(DEFAULT_FEE).toString()); + setSendNumber(selectedAssetsNum.minus(maxFee).toString()); } } Loading.hide(); @@ -184,6 +187,7 @@ const SendHome: React.FC = () => { chainInfo?.chainId, checkManagerSyncState, getTransactionFee, + maxFee, selectedAssets.balance, selectedAssets.chainId, selectedAssets.decimals, @@ -351,7 +355,7 @@ const SendHome: React.FC = () => { return { status: false }; } - if (isCross && sendBigNumber.isLessThanOrEqualTo(timesDecimals(CROSS_FEE, DEFAULT_DECIMAL))) { + if (isCross && sendBigNumber.isLessThanOrEqualTo(timesDecimals(crossFee, DEFAULT_DECIMAL))) { setErrorMessage([TransactionError.CROSS_NOT_ENOUGH]); return { status: false }; } @@ -392,6 +396,7 @@ const SendHome: React.FC = () => { assetInfo.symbol, chainInfo?.chainId, checkManagerSyncState, + crossFee, getTransactionFee, selectedAssets.balance, selectedAssets.decimals, diff --git a/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx b/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx index 920f744759..2000bc531a 100644 --- a/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx @@ -35,7 +35,6 @@ import { IToSendPreviewParamsType } from '@portkey-wallet/types/types-ca/routePa import { BaseToken } from '@portkey-wallet/types/types-ca/token'; import { ContractBasic } from '@portkey-wallet/contracts/utils/ContractBasic'; import { ZERO } from '@portkey-wallet/constants/misc'; -import { CROSS_FEE } from '@portkey-wallet/constants/constants-ca/wallet'; import { clearNftCollection, fetchNFTCollectionsAsync, @@ -47,14 +46,17 @@ import { ChainId } from '@portkey-wallet/types'; import { useGetCurrentAccountTokenPrice, useIsTokenHasPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import useEffectOnce from 'hooks/useEffectOnce'; +import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; const SendHome: React.FC = () => { const { t } = useLanguage(); - const isTestnet = useIsTestnet(); const { sendType, assetInfo, toInfo, transactionFee, sendNumber } = useRouterParams(); + useFetchTxFee(); + const { crossChain: crossFee } = useGetTxFee(assetInfo.chainId); + const dispatch = useAppCommonDispatch(); const pin = usePin(); const chainInfo = useCurrentChain(assetInfo.chainId); @@ -351,12 +353,12 @@ const SendHome: React.FC = () => { {`${unitConverter( - CROSS_FEE, + crossFee, )} ELF`} {!isTestnet ? ( {`$ ${unitConverter( - ZERO.plus(CROSS_FEE).multipliedBy(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(crossFee).multipliedBy(tokenPriceObject[ELF_SYMBOL]), )}`} ) : ( @@ -374,17 +376,17 @@ const SendHome: React.FC = () => { - {ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(CROSS_FEE)) + {ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossFee)) ? '0' - : formatAmountShow(ZERO.plus(sendNumber).minus(ZERO.plus(CROSS_FEE)))}{' '} + : formatAmountShow(ZERO.plus(sendNumber).minus(ZERO.plus(crossFee)))}{' '} {'ELF'} {!isTestnet ? ( {`$ ${ - ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(CROSS_FEE)) + ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossFee)) ? '0' : formatAmountShow( - ZERO.plus(sendNumber).minus(ZERO.plus(CROSS_FEE)).times(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(sendNumber).minus(ZERO.plus(crossFee)).times(tokenPriceObject[ELF_SYMBOL]), 2, ) }`} diff --git a/packages/mobile-app-did/js/store/config.ts b/packages/mobile-app-did/js/store/config.ts index 9bd0286fad..d90eeb16ae 100644 --- a/packages/mobile-app-did/js/store/config.ts +++ b/packages/mobile-app-did/js/store/config.ts @@ -17,6 +17,8 @@ import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; import assetsSlice from '@portkey-wallet/store/store-ca/assets/slice'; +import txFeeSlice from '@portkey-wallet/store/store-ca/txFee/slice'; + import tokenManagementSlice from '@portkey-wallet/store/store-ca/tokenManagement/slice'; interface ThunkOptions { @@ -52,6 +54,7 @@ const reduxPersistConfig = { dappSlice.name, cmsSlice.name, paymentSlice.name, + txFeeSlice.name, ], // More info here: https://shift.infinite.red/shipping-persistant-reducers-7341691232b1 diff --git a/packages/mobile-app-did/js/store/rootReducer.ts b/packages/mobile-app-did/js/store/rootReducer.ts index 4ce514c96c..cf13c8427b 100644 --- a/packages/mobile-app-did/js/store/rootReducer.ts +++ b/packages/mobile-app-did/js/store/rootReducer.ts @@ -20,6 +20,7 @@ import { switchSlice } from '@portkey-wallet/store/store-ca/switch/slice'; import { miscSlice } from '@portkey-wallet/store/store-ca/misc/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; +import { txFeeSlice } from '@portkey-wallet/store/store-ca/txFee/slice'; const userPersistConfig = { key: userSlice.name, @@ -53,6 +54,7 @@ const rootReducer = combineReducers({ [cmsSlice.name]: cmsSlice.reducer, [userSlice.name]: userReducer, [discoverSlice.name]: discoverReducer, + [txFeeSlice.name]: txFeeSlice.reducer, }); export default rootReducer; From 58dc9978dffb6890052961560ddcce126592b9d2 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 14 Jul 2023 19:58:56 +0800 Subject: [PATCH 374/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20bookmarks=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Discover/Bookmark/components/BookmarksSection.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 232ec477b5..d9cefb1a0c 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -179,7 +179,10 @@ function BookmarksSection() { Date: Mon, 17 Jul 2023 10:11:15 +0800 Subject: [PATCH 375/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20item=20?= =?UTF-8?q?=20detail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/RecordItem.tsx | 4 +- .../Bookmark/components/RecordsSection.tsx | 42 +++++++++++-------- .../DiscoverArchivedSection/index.tsx | 7 +--- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx index 10f14352b4..5cfbfb152f 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx @@ -40,6 +40,8 @@ export default memo( const onClickJump = useCallback( (i: any) => { + if (isEdit) return; + discoverJump({ item: { id: Date.now(), @@ -48,7 +50,7 @@ export default memo( }, }); }, - [discoverJump], + [discoverJump, isEdit], ); const renderUnderlayLeft = useCallback( diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index 1843ceb69f..c25396c519 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import DraggableFlatList from 'react-native-draggable-flatlist'; import GStyles from 'assets/theme/GStyles'; import { StyleSheet, View } from 'react-native'; @@ -26,13 +26,26 @@ function BookmarksSection() { const storeDispatch = useAppCommonDispatch(); const recordList = useRecordsList(true); - const onDelete = useCallback( + const [deleteIdMap, setDeleteIdsMap] = useState<{ [key: number]: number }>({}); + + const onAddDelete = useCallback( (item: ITabItem) => { - storeDispatch(removeRecordsItems({ ids: [item.id], networkType })); + deleteIdMap[item.id] = item.id; + setDeleteIdsMap(JSON.parse(JSON.stringify(deleteIdMap))); }, - [networkType, storeDispatch], + [deleteIdMap], ); + const onDone = useCallback(() => { + nextAnimation(); + dispatch(setEdit(false)); + const ids = Object.keys(deleteIdMap).map(ele => Number(ele)); + if (ids.length > 0) { + storeDispatch(removeRecordsItems({ ids: ids, networkType })); + setDeleteIdsMap({}); + } + }, [deleteIdMap, dispatch, networkType, storeDispatch]); + const onDeleteAll = useCallback(() => { ActionSheet.alert({ title2: `Delete all records?`, @@ -42,21 +55,16 @@ function BookmarksSection() { ], }); }, [networkType, storeDispatch]); + + const recordListShow = useMemo(() => recordList.filter(ele => !deleteIdMap[ele.id]), [deleteIdMap, recordList]); + const BottomBox = useMemo(() => { if (recordList?.length === 0) return null; - return ( {isEdit ? ( <> - { - nextAnimation(); - dispatch(setEdit(false)); - }} - title="Done" - type="primary" - /> + ); - }, [dispatch, isEdit, onDeleteAll, recordList?.length]); + }, [dispatch, isEdit, onDeleteAll, onDone, recordList?.length]); const closeSwipeable = useLockCallback(() => myEvents.bookmark.closeSwipeable.emit(), []); @@ -85,14 +93,14 @@ function BookmarksSection() { } keyExtractor={_item => String(_item.id)} - renderItem={props => } + renderItem={props => } onTouchStart={closeSwipeable} /> diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 6a8e5de226..dca17fea49 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -24,7 +24,6 @@ export function DiscoverArchivedSection() { bookmarkListStore.length ? ArchivedTabEnum.Bookmarks : ArchivedTabEnum.History, ); - const animateDuration = useMemo(() => (bookmarkListStore.length === 0 ? 0 : 250), [bookmarkListStore.length]); const bookmarkList = useMemo(() => bookmarkListStore.slice(0, 4), [bookmarkListStore]); const recordsList = useMemo(() => recordsListStore.slice(0, 4), [recordsListStore]); const isShowArchivedSections = useMemo( @@ -79,11 +78,7 @@ export function DiscoverArchivedSection() { - + {bookmarkList?.length === 0 ? ( From 39f49dbe87d5b8817a346f15b9109076c8d09a58 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 17 Jul 2023 10:54:18 +0800 Subject: [PATCH 376/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20bookmar?= =?UTF-8?q?k=20disable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/Bookmark/components/BookmarksSection.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index d9cefb1a0c..be17f6e75d 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -159,7 +159,7 @@ function BookmarksSection() { <> ), - [isEdit, onDeleteAll, onDone, onEdit], + [editList.length, isEdit, onDeleteAll, onDone, onEdit], ); const closeSwipeable = useLockCallback(() => myEvents.bookmark.closeSwipeable.emit(), []); return ( @@ -237,4 +237,7 @@ const styles = StyleSheet.create({ flatListPadding: { paddingVertical: pTd(8), }, + buttonDisabledStyle: { + opacity: 0.3, + }, }); From 698aef6b0ec1dead588ee31bd708510a29512e05 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 17 Jul 2023 11:17:20 +0800 Subject: [PATCH 377/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20bookmar?= =?UTF-8?q?k=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/Bookmark/components/BookmarksSection.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index be17f6e75d..5bd4c2c890 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -159,9 +159,12 @@ function BookmarksSection() { <> From 2c13e4d7713f95c9c18f6c8bc9328256e1138040 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 11:32:17 +0800 Subject: [PATCH 378/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20change=20versio?= =?UTF-8?q?n=20to=201.3.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/android/app/build.gradle | 2 +- packages/mobile-app-did/ios/Portkey/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/android/app/build.gradle b/packages/mobile-app-did/android/app/build.gradle index ccd163b063..933c53ff0a 100644 --- a/packages/mobile-app-did/android/app/build.gradle +++ b/packages/mobile-app-did/android/app/build.gradle @@ -168,7 +168,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 231 - versionName "1.3.2" + versionName "1.3.4" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/packages/mobile-app-did/ios/Portkey/Info.plist b/packages/mobile-app-did/ios/Portkey/Info.plist index 8dbd017cc4..fb64de9954 100644 --- a/packages/mobile-app-did/ios/Portkey/Info.plist +++ b/packages/mobile-app-did/ios/Portkey/Info.plist @@ -32,7 +32,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.2 + 1.3.4 CFBundleSignature ???? CFBundleVersion From bfa5354e8676a5c730cd94a287c46142daba2eb0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 11:55:11 +0800 Subject: [PATCH 379/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20record=20delete?= =?UTF-8?q?=20all=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/Bookmark/components/RecordsSection.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index c25396c519..b029eaa9b7 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -66,9 +66,12 @@ function BookmarksSection() { <> @@ -85,7 +88,7 @@ function BookmarksSection() { )} ); - }, [dispatch, isEdit, onDeleteAll, onDone, recordList?.length]); + }, [dispatch, isEdit, onDeleteAll, onDone, recordList?.length, recordListShow.length]); const closeSwipeable = useLockCallback(() => myEvents.bookmark.closeSwipeable.emit(), []); @@ -148,4 +151,7 @@ const styles = StyleSheet.create({ noData: { paddingVertical: 0, }, + buttonDisabledStyle: { + opacity: 0.3, + }, }); From b9c51c7bf213bb8221327244642aaf19aae277ab Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 17 Jul 2023 12:03:18 +0800 Subject: [PATCH 380/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20bookmar?= =?UTF-8?q?k=20edit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bookmark/components/BookmarksSection.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 5bd4c2c890..dc624e6449 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -21,6 +21,7 @@ import Loading from 'components/Loading'; import ActionSheet from 'components/ActionSheet'; import { TextXL } from 'components/CommonText'; import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; +import { sleep } from '@portkey-wallet/utils'; function BookmarksSection() { const [{ isEdit }, dispatch] = useBookmark(); @@ -39,7 +40,7 @@ function BookmarksSection() { const loadingRef = useRef(false); const getBookmarkList = useCallback( async (isInit: boolean) => { - if (isEdit || loadingRef.current) return; + if (loadingRef.current) return; loadingRef.current = true; let { skipCount } = pagerRef.current; @@ -75,7 +76,7 @@ function BookmarksSection() { } loadingRef.current = false; }, - [isEdit, refresh], + [refresh], ); const getBookmarkListRef = useRef(getBookmarkList); getBookmarkListRef.current = getBookmarkList; @@ -93,6 +94,7 @@ function BookmarksSection() { }, []); const onEdit = useCallback(() => { + if (loadingRef.current) return; deleteList.current = []; setEditList([...list]); nextAnimation(); @@ -111,9 +113,8 @@ function BookmarksSection() { })), }, }); - setTimeout(() => { - getBookmarkListRef.current(true); - }, 100); + await sleep(100); + getBookmarkListRef.current(true); } catch (error) { CommonToast.failError(error); } @@ -137,9 +138,8 @@ function BookmarksSection() { Loading.show(); try { await request.discover.deleteAllBookmark(); - setTimeout(() => { - getBookmarkListRef.current(true); - }, 100); + await sleep(100); + getBookmarkListRef.current(true); } catch (error) { CommonToast.failError(error); } From 1e112b1013ff631c7df2104123a25aff02f2dbac Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 17 Jul 2023 15:55:46 +0800 Subject: [PATCH 381/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20CodeVerify=20cha?= =?UTF-8?q?nge=20to=20CodeVerifyUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/VerifierAccount/index.tsx | 46 +----- .../pages/components/VerifierPage/index.tsx | 155 ++++++++++++++++++ packages/web-extension-did/package.json | 2 +- yarn.lock | 133 +++++++-------- 4 files changed, 232 insertions(+), 104 deletions(-) create mode 100644 packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index 8237612fa7..b4b1905f89 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -2,11 +2,8 @@ import { useNavigate } from 'react-router'; import { useAppDispatch, useLoginInfo, useGuardiansInfo, useUserInfo, useLoading } from 'store/Provider/hooks'; import { useCallback, useMemo } from 'react'; import { message } from 'antd'; -import { - setUserGuardianItemStatus, - setUserGuardianSessionIdAction, -} from '@portkey-wallet/store/store-ca/guardians/actions'; -import { OperationTypeEnum, VerifierInfo, VerifierItem, VerifyStatus } from '@portkey-wallet/types/verifier'; +import { setUserGuardianItemStatus } from '@portkey-wallet/store/store-ca/guardians/actions'; +import { OperationTypeEnum, VerifierInfo, VerifyStatus } from '@portkey-wallet/types/verifier'; import useLocationState from 'hooks/useLocationState'; import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { handleGuardian } from 'utils/sandboxUtil/handleGuardian'; @@ -25,9 +22,7 @@ import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQ import { useCommonState } from 'store/Provider/hooks'; import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; -import { CodeVerify, OnErrorFunc } from '@portkey/did-ui-react'; -import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; -import type { AccountType } from '@portkey/services'; +import VerifierPage from 'pages/components/VerifierPage'; export default function VerifierAccount() { const { loginAccount } = useLoginInfo(); @@ -223,43 +218,20 @@ export default function VerifierAccount() { } }, [state]); - const onReSend = useCallback( - (res: { verifier: VerifierItem; verifierSessionId: string }) => { - dispatch( - setUserGuardianSessionIdAction({ - key: currentGuardian?.key ?? `${currentGuardian?.guardianAccount}&${currentGuardian?.verifier?.name}`, - verifierInfo: { - sessionId: res.verifierSessionId, - }, - }), - ); - }, - [currentGuardian, dispatch], - ); - - const onError: OnErrorFunc = useCallback((error) => { - console.log('CodeVerify:', error); - }, []); - const renderContent = useMemo( () => (
    -
    ), - [currentGuardian, isInitStatus, loginAccount, onError, onReSend, onSuccess, operationType, originChainId], + [currentGuardian, isInitStatus, loginAccount, onSuccess, operationType], ); const props = useMemo( diff --git a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx new file mode 100644 index 0000000000..b414cc0d4f --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx @@ -0,0 +1,155 @@ +import { message } from 'antd'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useAppDispatch, useLoading } from 'store/Provider/hooks'; +import { LoginInfo } from 'store/reducers/loginCache/type'; +import { UserGuardianItem } from '@portkey-wallet/store/store-ca/guardians/type'; +import { useTranslation } from 'react-i18next'; +import { setUserGuardianSessionIdAction } from '@portkey-wallet/store/store-ca/guardians/actions'; +import { verifyErrorHandler } from 'utils/tryErrorHandler'; +import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; +import { verification } from 'utils/api'; +import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { useCommonState } from 'store/Provider/hooks'; +import { useLocation } from 'react-router'; +import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; +import { CodeVerifyUI } from '@portkey/did-ui-react'; +import { AccountType } from '@portkey/services'; + +const MAX_TIMER = 60; + +enum VerificationError { + InvalidCode = 'Invalid code', + codeExpired = 'The code has expired. Please resend it.', +} + +interface VerifierPageProps { + operationType: OperationTypeEnum; + loginAccount?: LoginInfo; + currentGuardian?: UserGuardianItem; + guardianType?: LoginType; + isInitStatus?: boolean; + onSuccess?: (res: { verificationDoc: string; signature: string; verifierId: string }) => void; +} + +interface ICodeVerifyUIInterface { + setTimer: (timer: number) => void; +} + +export default function VerifierPage({ + operationType, + currentGuardian, + guardianType, + isInitStatus, + onSuccess, +}: VerifierPageProps) { + const { setLoading } = useLoading(); + const { isNotLessThan768 } = useCommonState(); + const { pathname } = useLocation(); + const [isFromLoginOrRegister, setIsFromLoginOrRegister] = useState(true); + const [pinVal, setPinVal] = useState(); + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const originChainId = useOriginChainId(); + const uiRef = useRef(); + + useEffect(() => { + setIsFromLoginOrRegister(pathname.includes('register') || pathname.includes('login')); + console.log('isFromLoginOrRegister', isFromLoginOrRegister); + }, [isFromLoginOrRegister, pathname]); + + const onFinish = useCallback( + async (code: string) => { + try { + console.log(code); + if (code && code.length === 6) { + if (!guardianType && guardianType !== 0) return message.error('Missing guardiansType'); + if (!currentGuardian?.verifierInfo) throw 'Missing verifierInfo!!!'; + setLoading(true); + + const res = await verification.checkVerificationCode({ + params: { + type: LoginType[currentGuardian?.guardianType as LoginType], + guardianIdentifier: currentGuardian.guardianAccount.replaceAll(' ', ''), + verifierSessionId: currentGuardian.verifierInfo.sessionId, + verificationCode: code, + verifierId: currentGuardian.verifier?.id || '', + chainId: originChainId, + operationType, + }, + }); + + setLoading(false); + if (res.signature) return onSuccess?.({ ...res, verifierId: currentGuardian.verifier?.id || '' }); + + if (res?.error?.message) { + message.error(t(res.error.message)); + } else { + message.error(t(VerificationError.InvalidCode)); + } + setPinVal(''); + } + } catch (error: any) { + console.log(error, 'error===='); + setLoading(false); + setPinVal(''); + const _error = verifyErrorHandler(error); + message.error(_error); + } + }, + [guardianType, originChainId, currentGuardian, setLoading, onSuccess, t, operationType], + ); + + const resendCode = useCallback(async () => { + try { + if (!currentGuardian?.guardianAccount) throw 'Missing loginGuardianType'; + if (!guardianType && guardianType !== 0) throw 'Missing guardiansType'; + setLoading(true); + + const res = await verification.sendVerificationCode({ + params: { + guardianIdentifier: currentGuardian.guardianAccount.replaceAll(' ', ''), + type: LoginType[guardianType], + verifierId: currentGuardian.verifier?.id || '', + chainId: originChainId, + operationType, + }, + }); + setLoading(false); + if (res.verifierSessionId) { + uiRef.current?.setTimer(MAX_TIMER); + dispatch( + setUserGuardianSessionIdAction({ + key: currentGuardian?.key ?? `${currentGuardian?.guardianAccount}&${currentGuardian?.verifier?.name}`, + verifierInfo: { + sessionId: res.verifierSessionId, + endPoint: res.endPoint, + }, + }), + ); + } + } catch (error: any) { + console.log(error, 'error==='); + setLoading(false); + const _error = verifyErrorHandler(error); + message.error(_error); + } + }, [currentGuardian, guardianType, originChainId, dispatch, setLoading, operationType]); + + return currentGuardian?.verifier ? ( + + ) : ( +
    + ); +} diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index b8500e4682..3d0456f770 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -20,7 +20,7 @@ "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", "@portkey/providers": "1.0.1-alpha.0", - "@portkey/did-ui-react": "1.0.0-alpha.8", + "@portkey/did-ui-react": "1.0.9", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index 3f98247abb..a5221ab979 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5203,14 +5203,14 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/accounts@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/accounts/-/accounts-1.0.0-alpha.8.tgz#101190172e8c4c6f44d2dca220c91b4f00bbd1fc" - integrity sha512-oduw84UzaPNsbQtNJgHMoMx0FUl4b86IGjd0jEAY0M9z3gag2M8fjYZDIbkqMcpIOXMG0tMTDhrQzQSjFhvjjQ== - dependencies: - "@portkey/types" "^1.0.0-alpha.8" - "@portkey/utils" "^1.0.0-alpha.8" - "@portkey/validator" "^1.0.0-alpha.8" +"@portkey/accounts@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/accounts/-/accounts-1.0.9.tgz#e829614313d79b2a595de9e09b7a187f0fc999f7" + integrity sha512-eupcTiqG3fjQFEwVlzMOXE/kX951vEZn1Hdw1wXkvngZw2qwPwa6gJF9cL7/Ov+hv/kOZOlaKmB38AhJfQonYw== + dependencies: + "@portkey/types" "^1.0.9" + "@portkey/utils" "^1.0.9" + "@portkey/validator" "^1.0.9" aelf-sdk "^3.2.44" "@portkey/chain@^1.0.1-alpha.0": @@ -5232,24 +5232,24 @@ "@portkey/utils" "^1.0.0-alpha.5" aelf-sdk "^3.2.44" -"@portkey/contracts@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/contracts/-/contracts-1.0.0-alpha.8.tgz#2d2faab25005acae50418a162e8f520b211d8e17" - integrity sha512-sNZtrQmC+ZLaOK2ZGwpbC1nlmWwVj775oWT5V/EsudBKrnldcCoZMshBFJyNj0EVoGzXjqwLFfHDP8rC4LyRkQ== +"@portkey/contracts@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/contracts/-/contracts-1.0.9.tgz#c7729be31a48d0dc20b4acab162cbab0c871308d" + integrity sha512-JGfwu9TQL1nJlLIkBq4kC2iARR5HmtQa/S4EBK1lGsonNA23H9OMFCE9n4Pt0Lb50TcUqHJzwJU3UkQD8Z8Wjg== dependencies: "@portkey/provider-types" "^1.0.1-alpha.0" - "@portkey/types" "^1.0.0-alpha.8" - "@portkey/utils" "^1.0.0-alpha.8" + "@portkey/types" "^1.0.9" + "@portkey/utils" "^1.0.9" aelf-sdk "^3.2.44" -"@portkey/did-ui-react@1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/did-ui-react/-/did-ui-react-1.0.0-alpha.8.tgz#84135ed6cdc0fdec2c1dae54f2e2897a38c14ba9" - integrity sha512-bOKf58tbw+eugNbdSwbR/bEGXtvq+Lysk93zp2MXuxnOdKJj46Wib9fTq7EghQdJK1E2TpuOJ+eLO+897srD6g== +"@portkey/did-ui-react@1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/did-ui-react/-/did-ui-react-1.0.9.tgz#0673d65ccd0fe2c543593f0640d4854fa569511f" + integrity sha512-oo5yso/fbzbNnS0AEtixCMx0FzRBYR9Q7lrTNCphtl/pOURniZnJR8xSqWXfQfEnVGSqGS0YGm+p7fWb7tJBaA== dependencies: "@matt-block/react-recaptcha-v2" "^2.0.0" - "@portkey/did" "^1.0.0-alpha.8" - "@portkey/socket" "^1.0.0-alpha.8" + "@portkey/did" "^1.0.9" + "@portkey/socket" "^1.0.9" "@rc-component/portal" "1.0.2" aelf-sdk "^3.2.40" antd "4.24.4" @@ -5264,18 +5264,18 @@ reactjs-social-login "^2.6.2" uuid "^8.3.2" -"@portkey/did@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/did/-/did-1.0.0-alpha.8.tgz#5f5c5c8dd59f0a971aa293ee51b482761c7e3f8a" - integrity sha512-5wP8t7ZXQskgSoPk2ef3hA7tHILsmhrCVmg9B/2oMILFBmQ3Woe8fNkDgT8ECUDsjE/fSlj7ytrtpxc2QSjxog== - dependencies: - "@portkey/accounts" "^1.0.0-alpha.8" - "@portkey/contracts" "^1.0.0-alpha.8" - "@portkey/graphql" "^1.0.0-alpha.8" - "@portkey/request" "^1.0.0-alpha.8" - "@portkey/services" "^1.0.0-alpha.8" - "@portkey/types" "^1.0.0-alpha.8" - "@portkey/utils" "^1.0.0-alpha.8" +"@portkey/did@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/did/-/did-1.0.9.tgz#a9dcbe67050ea71f81c6b736c112a96030c9ea2e" + integrity sha512-WpUDRphdJExE9eZMhK5wFZr2q1onlGwr/N+y6DyqzPEJe2reFOupQtCNriPYhmUQlu9IxiTaZOf7sHdL/F9IXA== + dependencies: + "@portkey/accounts" "^1.0.9" + "@portkey/contracts" "^1.0.9" + "@portkey/graphql" "^1.0.9" + "@portkey/request" "^1.0.9" + "@portkey/services" "^1.0.9" + "@portkey/types" "^1.0.9" + "@portkey/utils" "^1.0.9" aelf-sdk "^3.2.44" react "^18.2.0" @@ -5289,13 +5289,13 @@ "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/graphql@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/graphql/-/graphql-1.0.0-alpha.8.tgz#4aa5c1d6d5fda63c15a053650aa64bf6e9b803a8" - integrity sha512-luksErVuyAlQJeYkqVofwx7vxHJuW7o0JlT5yhGWWSE4XUro8H49FTfLJ+zxkRvhU/iG69KeeStY+wuw3z1gJA== +"@portkey/graphql@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/graphql/-/graphql-1.0.9.tgz#ce8776ab4a68dd56525e1bdedd569a86485eb45c" + integrity sha512-F2VN3fJR+Hk9fBH3Z8fbc7QFgxLLSGVOpjGMSOdeSXlLRzqxhYPZ9EWU+dKvo24vGkcO09487dxswHHEVqp85Q== dependencies: "@apollo/client" "^3.7.3" - "@portkey/types" "^1.0.0-alpha.8" + "@portkey/types" "^1.0.9" graphql "^16.6.0" subscriptions-transport-ws "^0.11.0" @@ -5331,22 +5331,22 @@ lodash "^4.17.21" readable-stream "^4.4.0" -"@portkey/request@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/request/-/request-1.0.0-alpha.8.tgz#af86f263289966afc1200c07058610650e75d807" - integrity sha512-y6WnZGyyN6kdr9sXcCvXo27xB10j6MqIOAd1rrnCyCZewAxM2OdoK5JgAZEd8phqltCGuME6Uw5HuL1URU4Y8w== +"@portkey/request@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/request/-/request-1.0.9.tgz#80b5395795f74258e9785609fa8cd16b14fc8546" + integrity sha512-73afM9+otCSa4NPrRe4Sw9lDSutNVz1wEoMNI1Yl7+LDm8BrTIKTM4FLYpnobg52cacWO6ZKjK9jhKHWaQH7YQ== dependencies: - "@portkey/types" "^1.0.0-alpha.8" + "@portkey/types" "^1.0.9" query-string "^6.14.1" -"@portkey/services@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/services/-/services-1.0.0-alpha.8.tgz#0a804ef26917e2d1c6fc4e9839e77d81670234f6" - integrity sha512-5sZ8S7ITl7Smzx769aAg+4OCufOYrAmxgtU3JMXh9iP9LDVjNmvHeAfrMY7OVFb5MtFkJOSralxkH0O16Pg1mA== +"@portkey/services@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/services/-/services-1.0.9.tgz#3ff0dc51744839791178387105df4f54d5124e3e" + integrity sha512-N9UEoXZqbgMACklgyN156b3HNtpmdRBWsOu2lUVfZsvmWPPrEopOwLi+iqwiSILcme36ZnFe85whf7kJ4gVgrA== dependencies: - "@portkey/graphql" "^1.0.0-alpha.8" - "@portkey/request" "^1.0.0-alpha.8" - "@portkey/types" "^1.0.0-alpha.8" + "@portkey/graphql" "^1.0.9" + "@portkey/request" "^1.0.9" + "@portkey/types" "^1.0.9" aelf-sdk "^3.2.44" query-string "^7.1.1" @@ -5358,13 +5358,13 @@ "@abp/signalr" "^7.0.0" "@portkey/utils" "^1.0.0-alpha.2" -"@portkey/socket@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/socket/-/socket-1.0.0-alpha.8.tgz#b929ead80dd48e473bc851f98ec03f1c8801b371" - integrity sha512-SHsX1a4/LnmZcutR9cXs49EsigdIcHfqS69ezPGvPFZoZCSrqmkCi6Amj3mETRMWx3nfmgJlcBlwjlnwq14tGw== +"@portkey/socket@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/socket/-/socket-1.0.9.tgz#e21913615e80deb7692fc5437d265f3209783102" + integrity sha512-bUYNnZR9osUIB2SAuhwoM3OJuSOzFDxxNOlEKJ7fUclEzD7HTJsCPxsLS3J8J3O55tFJzrT8ZJ+MtWrzqQcLAw== dependencies: "@abp/signalr" "^7.0.0" - "@portkey/utils" "^1.0.0-alpha.8" + "@portkey/utils" "^1.0.9" "@portkey/types@1.0.0-alpha.4": version "1.0.0-alpha.4" @@ -5380,10 +5380,10 @@ dependencies: "@types/elliptic" "^6.4.14" -"@portkey/types@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/types/-/types-1.0.0-alpha.8.tgz#cdafac8f96102250ba5c8346142f018ac595331c" - integrity sha512-RTTK8+PdUbVCpArjrQxyG7CZKDzb9f4TBs/2nkmlkwhyZpm4PoWD87gcpSoYHbJN5WDMJ0N4I4N8AO6rnivn5g== +"@portkey/types@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/types/-/types-1.0.9.tgz#da1693c26a07e02af7d51de845bac4c87c28c407" + integrity sha512-imjlIBqNfasQVKlpiRdjSOovgM9uhncDVTxe4WxJA5m2DP6GdR/3lPSu2iOzGKsD2DQWGMCc/oGFbVRjBwl6pw== dependencies: "@types/elliptic" "^6.4.14" @@ -5401,17 +5401,18 @@ dependencies: aelf-sdk "^3.2.44" -"@portkey/utils@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/utils/-/utils-1.0.0-alpha.8.tgz#3fc0d2858888761008e3366c0ff8e3032caf36ea" - integrity sha512-n4E2WZ89sarg0GsQrEtMSv6mI5sq4kxPwkXMqfdjmGFvZmDr7RoDp2BHhKdGbDXBJUJgCvSsSm2r1mg72hwaIA== +"@portkey/utils@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/utils/-/utils-1.0.9.tgz#c120c198be2551b28f3c0b4afa6bff617102edba" + integrity sha512-Hvl2fmSgtfb9MTRzUL6yy3SelJOppwZsRdwzs7EyobRinagyzj3df4b9t+Z7Ppe95e7UUhMnT0XBoJssio+snw== dependencies: aelf-sdk "^3.2.44" + query-string "^7.1.1" -"@portkey/validator@^1.0.0-alpha.8": - version "1.0.0-alpha.8" - resolved "https://registry.npmjs.org/@portkey/validator/-/validator-1.0.0-alpha.8.tgz#2a0ee1465dfd2360169ffb553040f6416e21c3fb" - integrity sha512-8ePgVtXi3juMB0WYxtnRVgqAo8lr8Lglst4kUAr9mmk0wdQPgnd4vH9PuqcKw/1p7oJcff3wjxjgp0MV3g4jBw== +"@portkey/validator@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@portkey/validator/-/validator-1.0.9.tgz#61d4233335082abb7e8434dfe8a6a89295ad9d6d" + integrity sha512-HqVdKtWMNe5KWykOlQmoSIgSBNRaclgy6L043swhIH8/KfUMLcOw5P415/8AnHWYC71gIzepk4AFKpe9MB96yw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" From efdcd0b6f9ae3af486ce458281485c923ba5e8d1 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 16:04:34 +0800 Subject: [PATCH 382/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20diffrent=20networ?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/QrScanner/index.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 314ef1e4c4..0086b0db5f 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -60,11 +60,12 @@ const QrScanner: React.FC = () => { } const qrCodeData = expandQrData(JSON.parse(data)); + // if not currentNetwork - if (currentNetwork !== qrCodeData.netWorkType) return; - invalidQRCode( - currentNetwork === 'MAIN' ? InvalidQRCodeText.SWITCH_TO_TESTNET : InvalidQRCodeText.SWITCH_TO_MAINNET, - ); + if (currentNetwork !== qrCodeData.netWorkType) + return invalidQRCode( + currentNetwork === 'MAIN' ? InvalidQRCodeText.SWITCH_TO_TESTNET : InvalidQRCodeText.SWITCH_TO_MAINNET, + ); handleQRCodeData(qrCodeData, previousRouteInfo, setRefresh); } catch (error) { From 9c173ad1e33f419c9d559e086b7020a3f1852761 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 16:06:30 +0800 Subject: [PATCH 383/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20discover=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/Updater/index.tsx | 2 ++ packages/mobile-app-did/js/hooks/discover.ts | 22 +++++++++++++------ .../mobile-app-did/js/hooks/useInitData.ts | 5 ++++- .../js/pages/Discover/DiscoverHome/index.tsx | 3 +++ packages/store/store-ca/discover/slice.ts | 18 ++++++++++----- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index 8cdc61e625..312270be4b 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -20,6 +20,7 @@ import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; +import { useCheckAndInitNetworkDiscoverMap } from 'hooks/discover'; request.setExceptionManager(exceptionManager); export default function Updater() { @@ -38,6 +39,7 @@ export default function Updater() { useCaInfoOnChain(); useCheckManager(checkManagerOnLogout); + useCheckAndInitNetworkDiscoverMap(); useFetchSymbolImages(); useFetchTxFee(); useMemo(() => { diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index ef7fc8b3d1..f9c5a01691 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -14,28 +14,35 @@ import { } from '@portkey-wallet/store/store-ca/discover/slice'; import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; export const useIsDrawerOpen = () => useAppCASelector(state => state.discover.isDrawerOpen); +// check and init +export const useCheckAndInitNetworkDiscoverMap = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + const { discoverMap } = useAppCASelector(state => state.discover); + + useEffect(() => { + if (!discoverMap || !discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); + }, [discoverMap, dispatch, networkType]); +}; + // discover jump export const useDiscoverJumpWithNetWork = () => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { discoverMap } = useAppCASelector(state => state.discover); - const discoverJump = useCallback( ({ item, autoApprove }: { item: ITabItem; autoApprove?: boolean }) => { - if (!discoverMap || !discoverMap[networkType]) dispatch(initNetworkDiscoverMap(networkType)); - dispatch(createNewTab({ ...item, networkType })); dispatch(setActiveTab({ ...item, networkType })); dispatch(addRecordsItem({ ...item, networkType })); dispatch(changeDrawerOpenStatus(true)); if (autoApprove) dispatch(addAutoApproveItem(item.id)); }, - [discoverMap, dispatch, networkType], + [dispatch, networkType], ); return discoverJump; @@ -51,7 +58,7 @@ export const useDiscoverWhiteList = () => { const checkIsInWhiteList = useCallback( (url: string) => { console.log('discoverMap', discoverMap && discoverMap[networkType]?.whiteList); - return discoverMap && discoverMap[networkType]?.whiteList.includes(url); + return discoverMap && discoverMap[networkType]?.whiteList?.includes(url); }, [discoverMap, networkType], ); @@ -83,6 +90,7 @@ export const useBookmarkList = () => { maxResultCount, }, }); + if (skipCount === 0) { clean(); } diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index 10a086deec..eb146f15d4 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -8,7 +8,7 @@ import { useAppDispatch } from 'store/hooks'; import { useGetCurrentCAViewContract } from './contract'; import { useGetGuardiansInfoWriteStore, useGetVerifierServers } from './guardian'; import useEffectOnce from './useEffectOnce'; -import { useBookmarkList } from './discover'; +import { useBookmarkList, useCheckAndInitNetworkDiscoverMap } from './discover'; export default function useInitData() { const dispatch = useAppDispatch(); @@ -18,6 +18,8 @@ export default function useInitData() { const getGuardiansInfoWriteStore = useGetGuardiansInfoWriteStore(); const isMainNetwork = useIsMainnet(); + useCheckAndInitNetworkDiscoverMap(); + const { refresh: loadBookmarkList } = useBookmarkList(); const init = useCallback(async () => { @@ -30,6 +32,7 @@ export default function useInitData() { getCurrentCAViewContract(); dispatch(getWalletNameAsync()); dispatch(getSymbolImagesAsync()); + loadBookmarkList(); // getGuardiansInfoWriteStore after getVerifierServers await getVerifierServers(); diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx index 6775b024d0..45049c2913 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverHome/index.tsx @@ -14,11 +14,14 @@ import { pTd } from 'utils/unit'; import ActionSheet from 'components/ActionSheet'; import { useLanguage } from 'i18n/hooks'; import { DiscoverArchivedSection } from '../components/DiscoverArchivedSection'; +import { useCheckAndInitNetworkDiscoverMap } from 'hooks/discover'; export default function DiscoverHome() { const [, requestQrPermission] = useQrScanPermission(); const { t } = useLanguage(); + useCheckAndInitNetworkDiscoverMap(); + const showDialog = useCallback( () => ActionSheet.alert({ diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 3966038986..6e61f59f6e 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -35,16 +35,17 @@ export const discoverSlice = createSlice({ }, addRecordsItem: (state, { payload }: { payload: ITabItem & { networkType: NetworkType } }) => { const { networkType, url } = payload; - if (!state.discoverMap) return; - if (!state.discoverMap?.[networkType]) state.discoverMap[networkType] = initNetworkData; + if (!state?.discoverMap) state.discoverMap = {}; - const targetItem = state.discoverMap?.[networkType]?.recordsList.find(item => item.url === url); - const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); + if (!state?.discoverMap?.[networkType]) state.discoverMap[networkType] = initNetworkData; + + const targetItem = state.discoverMap?.[networkType]?.recordsList?.find(item => item.url === url); + const targetNetworkDiscover = state?.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); // limit number - if (RECORD_LIMIT <= targetNetworkDiscover.recordsList.length) { - targetNetworkDiscover.tabs.shift(); + if (RECORD_LIMIT <= targetNetworkDiscover?.recordsList.length) { + targetNetworkDiscover?.tabs.shift(); } if (targetItem) { @@ -132,6 +133,8 @@ export const discoverSlice = createSlice({ }; }, cleanBookmarkList: (state, { payload }: { payload: NetworkType }) => { + console.log('cleanBookmarkList1', state.discoverMap); + state.discoverMap = { ...(state.discoverMap || {}), [payload]: { @@ -139,6 +142,8 @@ export const discoverSlice = createSlice({ bookmarkList: [], }, }; + + console.log('cleanBookmarkList2', state.discoverMap); }, addBookmarkList: (state, { payload }: { payload: { networkType: NetworkType; list: IBookmarkItem[] } }) => { const preBookmarkList = state.discoverMap?.[payload.networkType]?.bookmarkList || []; @@ -149,6 +154,7 @@ export const discoverSlice = createSlice({ bookmarkList: preBookmarkList.concat(payload.list), }, }; + console.log('addBookmarkList', state.discoverMap); }, addAutoApproveItem: (state, { payload }: { payload: number }) => { if (!state.autoApproveMap) state.autoApproveMap = {}; From 6dbf88a3c769d6dce26585cc443321a2356f863e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 16:09:24 +0800 Subject: [PATCH 384/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20delete=20consol?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/discover/slice.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 6e61f59f6e..0e8c0df587 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -133,8 +133,6 @@ export const discoverSlice = createSlice({ }; }, cleanBookmarkList: (state, { payload }: { payload: NetworkType }) => { - console.log('cleanBookmarkList1', state.discoverMap); - state.discoverMap = { ...(state.discoverMap || {}), [payload]: { @@ -142,8 +140,6 @@ export const discoverSlice = createSlice({ bookmarkList: [], }, }; - - console.log('cleanBookmarkList2', state.discoverMap); }, addBookmarkList: (state, { payload }: { payload: { networkType: NetworkType; list: IBookmarkItem[] } }) => { const preBookmarkList = state.discoverMap?.[payload.networkType]?.bookmarkList || []; @@ -154,7 +150,6 @@ export const discoverSlice = createSlice({ bookmarkList: preBookmarkList.concat(payload.list), }, }; - console.log('addBookmarkList', state.discoverMap); }, addAutoApproveItem: (state, { payload }: { payload: number }) => { if (!state.autoApproveMap) state.autoApproveMap = {}; From 030090936a3f7654fcabd2efe88ce1ad13408abe Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Mon, 17 Jul 2023 16:10:06 +0800 Subject: [PATCH 385/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/messages/SandboxEventTypes.ts | 2 + .../web/pages/Buy/hooks/useHandleAchSell.ts | 49 +++++++++++-------- .../web-extension-did/app/web/sandboxUtil.ts | 24 +++++++++ .../utils/sandboxUtil/getTransactionRaw.ts | 41 ++++++++++++++++ 4 files changed, 96 insertions(+), 20 deletions(-) create mode 100644 packages/web-extension-did/app/web/utils/sandboxUtil/getTransactionRaw.ts diff --git a/packages/web-extension-did/app/web/messages/SandboxEventTypes.ts b/packages/web-extension-did/app/web/messages/SandboxEventTypes.ts index f6c142e6e1..8fc97c6c0e 100644 --- a/packages/web-extension-did/app/web/messages/SandboxEventTypes.ts +++ b/packages/web-extension-did/app/web/messages/SandboxEventTypes.ts @@ -6,6 +6,8 @@ enum SandboxEventTypes { callSendMethod = 'callSendMethod', // getEncodedTx getTransactionFee = 'getTransactionFee', + // getTransactionRaw + getTransactionRaw = 'getTransactionRaw', initViewContract = 'initViewContract', } diff --git a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts index 4a01cc369f..9a98dc6a47 100644 --- a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts +++ b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts @@ -8,11 +8,14 @@ import { timesDecimals } from '@portkey-wallet/utils/converter'; import { message } from 'antd'; import { useCallback, useMemo } from 'react'; import { useLoading } from 'store/Provider/hooks'; -import sameChainTransfer from 'utils/sandboxUtil/sameChainTransfer'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import aes from '@portkey-wallet/utils/aes'; import InternalMessage from 'messages/InternalMessage'; import InternalMessageTypes from 'messages/InternalMessageTypes'; +import getTransactionRaw from 'utils/sandboxUtil/getTransactionRaw'; +import AElf from 'aelf-sdk'; +import { getWallet } from '@portkey-wallet/utils/aelf'; +import SparkMD5 from 'spark-md5'; export const useHandleAchSell = () => { const { setLoading } = useLoading(); @@ -37,30 +40,36 @@ export const useHandleAchSell = () => { if (!privateKey) throw new Error('Sell Transfer: No PrivateKey'); if (!aelfToken) throw new Error('Sell Transfer: No Token'); + const manager = getWallet(privateKey); + if (!manager?.keyPair) throw new Error('Sell Transfer: No keyPair'); - const transferParams = { - chainInfo, + const rawResult = await getTransactionRaw({ + contractAddress: chainInfo.caContractAddress, + rpcUrl: chainInfo?.endPoint || '', chainType: currentNetwork.walletType, + methodName: 'ManagerForwardCall', privateKey, - tokenInfo: { - id: aelfToken?.id, - chainId: aelfToken.chainId, - decimals: aelfToken.decimals, - address: aelfToken.tokenContractAddress || '', - symbol: aelfToken.symbol, - name: aelfToken?.name, - imageUrl: aelfToken?.imageUrl, - alias: aelfToken?.alias, - tokenId: aelfToken?.tokenId, + paramsOption: { + caHash: wallet?.caHash || '', + contractAddress: aelfToken.tokenContractAddress || '', + methodName: 'Transfer', + args: { + symbol: aelfToken.symbol, + to: `ELF_${params.address}_AELF`, + amount: timesDecimals(params.cryptoAmount, aelfToken.decimals).toNumber(), + }, }, - caHash: wallet?.caHash || '', - amount: timesDecimals(params.cryptoAmount, aelfToken.decimals).toNumber(), - toAddress: `ELF_${params.address}_AELF`, - }; - const res = await sameChainTransfer(transferParams); + }); + if (!rawResult || !rawResult.result) { + throw new Error('Failed to get raw transaction.'); + } + const publicKey = manager.keyPair.getPublic('hex'); + const message = SparkMD5.hash(`${params.orderId}${rawResult.result.data}`); + const signature = AElf.wallet.sign(Buffer.from(message).toString('hex'), manager.keyPair).toString('hex'); return { - error: res?.result?.Error, - transactionId: res?.result.TransactionId || '', + rawTransaction: rawResult.result.data, + publicKey, + signature, }; }, [aelfToken, chainInfo, currentNetwork.walletType, wallet.AESEncryptPrivateKey, wallet?.caHash], diff --git a/packages/web-extension-did/app/web/sandboxUtil.ts b/packages/web-extension-did/app/web/sandboxUtil.ts index 94b52a8d2e..daa13264af 100644 --- a/packages/web-extension-did/app/web/sandboxUtil.ts +++ b/packages/web-extension-did/app/web/sandboxUtil.ts @@ -66,6 +66,9 @@ class SandboxUtil { case SandboxEventTypes.initViewContract: SandboxUtil.initViewContract(event, SandboxUtil.callback); break; + case SandboxEventTypes.getTransactionRaw: + SandboxUtil.getTransactionRaw(event, SandboxUtil.callback); + break; default: break; } @@ -304,6 +307,27 @@ class SandboxUtil { }); } } + + static async getTransactionRaw(event: MessageEvent, callback: SendBack) { + const data = event.data.data ?? {}; + try { + const { rpcUrl, address, paramsOption, chainType, methodName, privateKey } = data; + if (chainType !== 'aelf') throw 'Not support'; + const aelfContract = await SandboxUtil._getELFSendContract(rpcUrl, address, privateKey); + const raw = await aelfContract.encodedTx(methodName, paramsOption); + callback(event, { + code: SandboxErrorCode.success, + message: raw, + sid: data.sid, + }); + } catch (e) { + return callback(event, { + code: SandboxErrorCode.error, + message: e, + sid: data.sid, + }); + } + } } new SandboxUtil(); diff --git a/packages/web-extension-did/app/web/utils/sandboxUtil/getTransactionRaw.ts b/packages/web-extension-did/app/web/utils/sandboxUtil/getTransactionRaw.ts new file mode 100644 index 0000000000..ca1bc2217a --- /dev/null +++ b/packages/web-extension-did/app/web/utils/sandboxUtil/getTransactionRaw.ts @@ -0,0 +1,41 @@ +import { ChainType } from '@portkey-wallet/types'; +import SandboxEventTypes from 'messages/SandboxEventTypes'; +import SandboxEventService, { SandboxErrorCode } from 'service/SandboxEventService'; + +interface GetTransitionFeeParams { + rpcUrl: string; + contractAddress: string; + chainType: ChainType; + paramsOption: any; + methodName: string; + privateKey: string; +} + +const getTransactionRaw = async ({ + rpcUrl, + contractAddress, + paramsOption, + chainType, + methodName, + privateKey, +}: GetTransitionFeeParams) => { + const resMessage = await SandboxEventService.dispatchAndReceive(SandboxEventTypes.getTransactionRaw, { + rpcUrl, + address: contractAddress, + paramsOption, + chainType, + methodName, + privateKey, + }); + + if (resMessage.code === SandboxErrorCode.error) throw resMessage.error; + return { + code: resMessage.code, + result: { + rpcUrl, + ...resMessage.message, + }, + }; +}; + +export default getTransactionRaw; From 1f7c72ad78e8007504fefecb741d0d8993dafaec Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Mon, 17 Jul 2023 16:36:47 +0800 Subject: [PATCH 386/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20bookmar?= =?UTF-8?q?k=20button=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/TabsDrawer/components/TabsOverlay/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index 798adbc6a7..f2d7978522 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -145,6 +145,7 @@ const BrowserEditModal = ({ activeTabId, activeWebviewScreenShot, setPreActiveTabId, + refresh, bookmark?.id, bookmark?.index, ], @@ -238,7 +239,6 @@ const styles = StyleSheet.create({ listWrap: { marginTop: pTd(24), marginBottom: pTd(24), - paddingLeft: pTd(12), display: 'flex', flexWrap: 'wrap', flexDirection: 'row', From 35af86eafb1c41467077cb649783295296216565 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 16:45:49 +0800 Subject: [PATCH 387/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20bookmarks=20style?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiscoverArchivedSection/index.tsx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index dca17fea49..271c7a06e9 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -4,7 +4,7 @@ import { FontStyles } from 'assets/theme/styles'; import { TextM, TextS } from 'components/CommonText'; import { useBookmarkList, useDiscoverJumpWithNetWork, useRecordsList } from 'hooks/discover'; import React, { useCallback, useMemo } from 'react'; -import { StyleSheet, View, Easing, TouchableOpacity } from 'react-native'; +import { StyleSheet, View, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; import { TabView } from '@rneui/base'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; @@ -59,19 +59,14 @@ export function DiscoverArchivedSection() { - setIndex(ArchivedTabEnum.Bookmarks)} - style={[ - GStyles.marginRight(24), - index === ArchivedTabEnum.Bookmarks ? FontStyles.weight500 : FontStyles.font3, - ]}> - Bookmarks - - setIndex(ArchivedTabEnum.History)} - style={index === ArchivedTabEnum.History ? FontStyles.weight500 : FontStyles.font3}> - Records - + setIndex(ArchivedTabEnum.Bookmarks)}> + + Bookmarks + + + setIndex(ArchivedTabEnum.History)}> + Records + See All From 52a72b637ced17a7611192dee55c021561cfc81e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 17:07:50 +0800 Subject: [PATCH 388/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20modal=20title=20s?= =?UTF-8?q?tyle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/Bookmark/components/BookmarksSection.tsx | 2 +- .../js/pages/Discover/Bookmark/components/RecordsSection.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index dc624e6449..002e42ce96 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -126,7 +126,7 @@ function BookmarksSection() { const onDeleteAll = useCallback(() => { ActionSheet.alert({ - title2: Delete all bookmarks?, + title: 'Delete all bookmarks?', buttons: [ { title: 'Cancel', diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index b029eaa9b7..76697ea03c 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -48,7 +48,7 @@ function BookmarksSection() { const onDeleteAll = useCallback(() => { ActionSheet.alert({ - title2: `Delete all records?`, + title: `Delete all records?`, buttons: [ { title: 'Cancel', type: 'outline' }, { title: 'Confirm', type: 'primary', onPress: () => storeDispatch(clearRecordsList({ networkType })) }, From 89c5ca83d1e35c95d1511861739c9dae8213a9df Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 17:46:56 +0800 Subject: [PATCH 389/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20android=20flatlis?= =?UTF-8?q?t=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/Bookmark/components/RecordsSection.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index 76697ea03c..f74e11a7e1 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -140,7 +140,6 @@ const styles = StyleSheet.create({ flatListStyle: { height: '100%', borderRadius: pTd(6), - overflow: 'hidden', }, flatListContent: { backgroundColor: defaultColors.bg1, From 051bbdf75de9f8d0d7408b8bc928bf98010bedc6 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 17:53:16 +0800 Subject: [PATCH 390/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20book=20mark=20sty?= =?UTF-8?q?les?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Discover/Bookmark/components/BookmarksSection.tsx | 5 ++++- .../js/pages/Discover/Bookmark/components/RecordsSection.tsx | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 002e42ce96..de584710f7 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -231,7 +231,10 @@ const styles = StyleSheet.create({ deleteAll: { marginTop: pTd(10), }, - flatListWrap: { borderRadius: pTd(6), overflow: 'hidden', height: '100%' }, + flatListWrap: { + borderRadius: pTd(6), + height: '100%', + }, flatListContent: { backgroundColor: defaultColors.bg1, borderRadius: pTd(6), diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index f74e11a7e1..1857315847 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -136,7 +136,6 @@ const styles = StyleSheet.create({ deleteAll: { marginTop: pTd(10), }, - flatListStyle: { height: '100%', borderRadius: pTd(6), From 19128e6e7aaf1c6bd6cae6014d2b2c6d9e784474 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 17 Jul 2023 18:07:28 +0800 Subject: [PATCH 391/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20tip=20for?= =?UTF-8?q?=20CodeVerifierUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/VerifierAccount/index.less | 25 ++++ .../pages/components/VerifierPage/index.tsx | 1 + packages/web-extension-did/package.json | 2 +- yarn.lock | 130 +++++++++--------- 4 files changed, 92 insertions(+), 66 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.less b/packages/web-extension-did/app/web/pages/VerifierAccount/index.less index f8b203b959..ca911d1481 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.less +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.less @@ -1,3 +1,5 @@ +@import '../../assets/theme/color.less'; + .verifier-account { .verifier-account-content { width: 350px; @@ -7,6 +9,29 @@ width: 0; flex: 1; } + .verifier-account-wrapper { + .password-wrapper { + .resend-btn { + width: auto; + margin: auto; + display: block; + margin-top: 24px; + color: @font-9; + border: none; + background-color: transparent; + &:hover { + background-color: transparent; + } + span { + line-height: 20px; + font-size: 14px; + } + &.resend-after-btn { + color: @font-12; + } + } + } + } } .verifier-account.big-screen-verifier-account { .verifier-account-content { diff --git a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx index b414cc0d4f..e6c9c2947e 100644 --- a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx @@ -145,6 +145,7 @@ export default function VerifierPage({ isLoginGuardian={currentGuardian?.isLoginAccount} accountType={LoginType[currentGuardian?.guardianType as LoginType] as AccountType} code={pinVal} + tipExtra={!isFromLoginOrRegister && 'Please contact your guardians, and enter '} onReSend={resendCode} onCodeFinish={onFinish} onCodeChange={setPinVal} diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 3d0456f770..e72ea4082b 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -20,7 +20,7 @@ "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", "@portkey/providers": "1.0.1-alpha.0", - "@portkey/did-ui-react": "1.0.9", + "@portkey/did-ui-react": "1.0.10", "@reduxjs/toolkit": "^1.8.5", "@sentry/browser": "^7.37.1", "@sentry/core": "^7.37.1", diff --git a/yarn.lock b/yarn.lock index a5221ab979..2de53b5213 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5203,14 +5203,14 @@ resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@portkey/accounts@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/accounts/-/accounts-1.0.9.tgz#e829614313d79b2a595de9e09b7a187f0fc999f7" - integrity sha512-eupcTiqG3fjQFEwVlzMOXE/kX951vEZn1Hdw1wXkvngZw2qwPwa6gJF9cL7/Ov+hv/kOZOlaKmB38AhJfQonYw== +"@portkey/accounts@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/accounts/-/accounts-1.0.10.tgz#07223f759b65b7b1f3499ae420e79f8fc8423e45" + integrity sha512-YuUags/u6nMo4Wfj/M6qUeGEdL9FP1mjgslmuz/zG5y8aDdI85vgkI+Nph1FnBviN9WF2MquQOjqOJbjMMe5Hg== dependencies: - "@portkey/types" "^1.0.9" - "@portkey/utils" "^1.0.9" - "@portkey/validator" "^1.0.9" + "@portkey/types" "^1.0.10" + "@portkey/utils" "^1.0.10" + "@portkey/validator" "^1.0.10" aelf-sdk "^3.2.44" "@portkey/chain@^1.0.1-alpha.0": @@ -5232,24 +5232,24 @@ "@portkey/utils" "^1.0.0-alpha.5" aelf-sdk "^3.2.44" -"@portkey/contracts@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/contracts/-/contracts-1.0.9.tgz#c7729be31a48d0dc20b4acab162cbab0c871308d" - integrity sha512-JGfwu9TQL1nJlLIkBq4kC2iARR5HmtQa/S4EBK1lGsonNA23H9OMFCE9n4Pt0Lb50TcUqHJzwJU3UkQD8Z8Wjg== +"@portkey/contracts@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/contracts/-/contracts-1.0.10.tgz#514c9bc0f1589278dd0f7d069cd9ac8fe9b34ea8" + integrity sha512-m+dI1bmNEVF6SSsjCMXdFfItvhnUjH/Spte8lJkB7IGvhUu9qZPBO9z8kdFYcQxnrdEbyF7hc2oSSIoVsgDdNQ== dependencies: "@portkey/provider-types" "^1.0.1-alpha.0" - "@portkey/types" "^1.0.9" - "@portkey/utils" "^1.0.9" + "@portkey/types" "^1.0.10" + "@portkey/utils" "^1.0.10" aelf-sdk "^3.2.44" -"@portkey/did-ui-react@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/did-ui-react/-/did-ui-react-1.0.9.tgz#0673d65ccd0fe2c543593f0640d4854fa569511f" - integrity sha512-oo5yso/fbzbNnS0AEtixCMx0FzRBYR9Q7lrTNCphtl/pOURniZnJR8xSqWXfQfEnVGSqGS0YGm+p7fWb7tJBaA== +"@portkey/did-ui-react@1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/did-ui-react/-/did-ui-react-1.0.10.tgz#634afbad65605ff3c91512166536455426526013" + integrity sha512-nSXHekicpw5YbkREoOPmrE4Zmo2hlRanQGl82sesZ1qjvkgpAa6BXYmhahgRj2UFqApNW/2+SvtMeKIjjz08zw== dependencies: "@matt-block/react-recaptcha-v2" "^2.0.0" - "@portkey/did" "^1.0.9" - "@portkey/socket" "^1.0.9" + "@portkey/did" "^1.0.10" + "@portkey/socket" "^1.0.10" "@rc-component/portal" "1.0.2" aelf-sdk "^3.2.40" antd "4.24.4" @@ -5264,18 +5264,18 @@ reactjs-social-login "^2.6.2" uuid "^8.3.2" -"@portkey/did@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/did/-/did-1.0.9.tgz#a9dcbe67050ea71f81c6b736c112a96030c9ea2e" - integrity sha512-WpUDRphdJExE9eZMhK5wFZr2q1onlGwr/N+y6DyqzPEJe2reFOupQtCNriPYhmUQlu9IxiTaZOf7sHdL/F9IXA== - dependencies: - "@portkey/accounts" "^1.0.9" - "@portkey/contracts" "^1.0.9" - "@portkey/graphql" "^1.0.9" - "@portkey/request" "^1.0.9" - "@portkey/services" "^1.0.9" - "@portkey/types" "^1.0.9" - "@portkey/utils" "^1.0.9" +"@portkey/did@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/did/-/did-1.0.10.tgz#4f2ae2c7811d15fbdd9ef15964daf16716598ec0" + integrity sha512-0scGW8bTUmrL7QGmFj8RGlgdRkval1OCjJT+B45caIVxOkTAhswTBnWR4im4Rl1A+RwPxKPIVRAVsdlRb2QoMg== + dependencies: + "@portkey/accounts" "^1.0.10" + "@portkey/contracts" "^1.0.10" + "@portkey/graphql" "^1.0.10" + "@portkey/request" "^1.0.10" + "@portkey/services" "^1.0.10" + "@portkey/types" "^1.0.10" + "@portkey/utils" "^1.0.10" aelf-sdk "^3.2.44" react "^18.2.0" @@ -5289,13 +5289,13 @@ "@types/elliptic" "^6.4.14" readable-stream "^4.4.0" -"@portkey/graphql@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/graphql/-/graphql-1.0.9.tgz#ce8776ab4a68dd56525e1bdedd569a86485eb45c" - integrity sha512-F2VN3fJR+Hk9fBH3Z8fbc7QFgxLLSGVOpjGMSOdeSXlLRzqxhYPZ9EWU+dKvo24vGkcO09487dxswHHEVqp85Q== +"@portkey/graphql@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/graphql/-/graphql-1.0.10.tgz#72662045b3a7553028f063ad732ada967404d611" + integrity sha512-A4+S74lT0djD650HfyZJcfWrVRlLpFsn8LMAu2vGwtbuFe2GU0t390DBZWgmWhk8RElI9/oaajUx+tqOXDcGRQ== dependencies: "@apollo/client" "^3.7.3" - "@portkey/types" "^1.0.9" + "@portkey/types" "^1.0.10" graphql "^16.6.0" subscriptions-transport-ws "^0.11.0" @@ -5331,22 +5331,22 @@ lodash "^4.17.21" readable-stream "^4.4.0" -"@portkey/request@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/request/-/request-1.0.9.tgz#80b5395795f74258e9785609fa8cd16b14fc8546" - integrity sha512-73afM9+otCSa4NPrRe4Sw9lDSutNVz1wEoMNI1Yl7+LDm8BrTIKTM4FLYpnobg52cacWO6ZKjK9jhKHWaQH7YQ== +"@portkey/request@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/request/-/request-1.0.10.tgz#9ee906f9dafcb3d9931c23d54ba317730fb6fd6f" + integrity sha512-CR2xukXH1hiF+4S5tTzxDE7LMzgSq6Yh7jQnAOwBvyHD4C6tHZZ4z815T6N2WFuQoi9Dxo+q9UkKbWp8i6BL+A== dependencies: - "@portkey/types" "^1.0.9" + "@portkey/types" "^1.0.10" query-string "^6.14.1" -"@portkey/services@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/services/-/services-1.0.9.tgz#3ff0dc51744839791178387105df4f54d5124e3e" - integrity sha512-N9UEoXZqbgMACklgyN156b3HNtpmdRBWsOu2lUVfZsvmWPPrEopOwLi+iqwiSILcme36ZnFe85whf7kJ4gVgrA== +"@portkey/services@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/services/-/services-1.0.10.tgz#bc30fcc5b28b87a1e17d2d78d8ad406b165a1970" + integrity sha512-WM//e+jvkjDXPz7+lXoWcS23ne4Vk2O3fuXkVW0Zud+EWhhKuz2kPQB+bDV1DTla1Jz4qJvxX8pDc6O7Ok0cJw== dependencies: - "@portkey/graphql" "^1.0.9" - "@portkey/request" "^1.0.9" - "@portkey/types" "^1.0.9" + "@portkey/graphql" "^1.0.10" + "@portkey/request" "^1.0.10" + "@portkey/types" "^1.0.10" aelf-sdk "^3.2.44" query-string "^7.1.1" @@ -5358,13 +5358,13 @@ "@abp/signalr" "^7.0.0" "@portkey/utils" "^1.0.0-alpha.2" -"@portkey/socket@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/socket/-/socket-1.0.9.tgz#e21913615e80deb7692fc5437d265f3209783102" - integrity sha512-bUYNnZR9osUIB2SAuhwoM3OJuSOzFDxxNOlEKJ7fUclEzD7HTJsCPxsLS3J8J3O55tFJzrT8ZJ+MtWrzqQcLAw== +"@portkey/socket@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/socket/-/socket-1.0.10.tgz#d35e022e196eab5c9d4ad9d46ec1e9765d618afc" + integrity sha512-I02Xdqkk8mugTLNtyYpvPHjT1Vn2b7H8P/yrj/Lmk9tmRkyxyBDT1Si7WmOjipSIzsmDuBAY4u8frRUI3NGQeg== dependencies: "@abp/signalr" "^7.0.0" - "@portkey/utils" "^1.0.9" + "@portkey/utils" "^1.0.10" "@portkey/types@1.0.0-alpha.4": version "1.0.0-alpha.4" @@ -5380,10 +5380,10 @@ dependencies: "@types/elliptic" "^6.4.14" -"@portkey/types@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/types/-/types-1.0.9.tgz#da1693c26a07e02af7d51de845bac4c87c28c407" - integrity sha512-imjlIBqNfasQVKlpiRdjSOovgM9uhncDVTxe4WxJA5m2DP6GdR/3lPSu2iOzGKsD2DQWGMCc/oGFbVRjBwl6pw== +"@portkey/types@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/types/-/types-1.0.10.tgz#46bcae30ed4a11fdd38791b169857ac9e0badd9a" + integrity sha512-ngp8F+L6pqXibN6vGdYjv0RvCLSevw5PYUveAs9QQ1zwvBqGv1qz/2Fy4m9eoZb9aLbyQErmw65O/HpGzgRe+g== dependencies: "@types/elliptic" "^6.4.14" @@ -5401,18 +5401,18 @@ dependencies: aelf-sdk "^3.2.44" -"@portkey/utils@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/utils/-/utils-1.0.9.tgz#c120c198be2551b28f3c0b4afa6bff617102edba" - integrity sha512-Hvl2fmSgtfb9MTRzUL6yy3SelJOppwZsRdwzs7EyobRinagyzj3df4b9t+Z7Ppe95e7UUhMnT0XBoJssio+snw== +"@portkey/utils@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/utils/-/utils-1.0.10.tgz#d996f6609816869c35e0881890063f908a0545d3" + integrity sha512-uth4xQHhUrt3WukwigVl2DAT5d2qTkCjOK9opTvnV8YJ29AHFzOKviGtBACq1Ha0ZZzk/lMeHGQW9onPdB1nug== dependencies: aelf-sdk "^3.2.44" query-string "^7.1.1" -"@portkey/validator@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@portkey/validator/-/validator-1.0.9.tgz#61d4233335082abb7e8434dfe8a6a89295ad9d6d" - integrity sha512-HqVdKtWMNe5KWykOlQmoSIgSBNRaclgy6L043swhIH8/KfUMLcOw5P415/8AnHWYC71gIzepk4AFKpe9MB96yw== +"@portkey/validator@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@portkey/validator/-/validator-1.0.10.tgz#416d9ca60c9c631237e106e888933d0f9e837dc2" + integrity sha512-njInql3kgXdy00xsxYRCQkBw/ttAFAld93IkTfcnSPepXAIutCDVtsgMtx6mD3fKJioC+Jb8deliiXl9B5dY7A== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" From 3187bf00d721b2c955684fa18c241b7b01525b58 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 20:09:03 +0800 Subject: [PATCH 392/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20change=20archiv?= =?UTF-8?q?e=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/DiscoverArchivedSection/index.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 271c7a06e9..9da45ee07a 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -54,6 +54,16 @@ export function DiscoverArchivedSection() { }, [refresh]), ); + useFocusEffect( + useCallback(() => { + if (bookmarkList?.length > 0) { + setIndex(ArchivedTabEnum.Bookmarks); + } else { + setIndex(ArchivedTabEnum.History); + } + }, [bookmarkList?.length]), + ); + if (!isShowArchivedSections) return null; return ( From 4041038157d3ee5402f7e4e4b0521bd5c1349ca0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 17 Jul 2023 20:10:09 +0800 Subject: [PATCH 393/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20item=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/components/DiscoverArchivedSection/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 9da45ee07a..4b7cb992af 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -94,7 +94,7 @@ export function DiscoverArchivedSection() { - {item.url} + {item?.name || item?.url} @@ -112,7 +112,7 @@ export function DiscoverArchivedSection() { - {item.url} + {item?.name || item?.url} From 3cf0d35e9467312053c6dbd51215abb5cae2bbac Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 18 Jul 2023 11:30:28 +0800 Subject: [PATCH 394/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20dangerous=20aut?= =?UTF-8?q?o=20approve?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/BrowserTab/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 76ffa71105..6ba824ceb8 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -5,6 +5,7 @@ import { CaptureOptions, captureRef } from 'react-native-view-shot'; import { IBrowserTab, useBrowser } from 'components/TabsDrawer/context'; import Progressbar, { IProgressbar } from 'components/Progressbar'; import HttpModal from './components/HttpModal'; +import { getProtocolAndHost, isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; type BrowserTabProps = { isHidden: boolean; @@ -40,12 +41,12 @@ const BrowserTab = forwardRef(function BrowserTab( }, [isHidden, options]); const onPageLoadEnd = useCallback(() => { - if (!isApproved.current && autoApprove) { + if (!isDangerousLink(getProtocolAndHost(uri)) && !isApproved.current && autoApprove) { isApproved.current = true; webViewRef.current?.autoApprove(); } onLoadEnd?.(); - }, [onLoadEnd, autoApprove]); + }, [autoApprove, uri, onLoadEnd]); return ( Date: Tue, 18 Jul 2023 11:43:28 +0800 Subject: [PATCH 395/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20adjust=20wallet?= =?UTF-8?q?=20&=20errorHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/wallet/index.test.ts | 21 +++++++++++++++++++ .../app/web/utils/errorHandler.test.ts | 11 ---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/store/store-ca/wallet/index.test.ts b/packages/store/store-ca/wallet/index.test.ts index 6f862a4af3..a68605719a 100644 --- a/packages/store/store-ca/wallet/index.test.ts +++ b/packages/store/store-ca/wallet/index.test.ts @@ -522,6 +522,13 @@ describe('setChainListAction', () => { endPoint: 'http://localhost:1235', explorerUrl: 'http://localhost:1235', caContractAddress: 'caContractAddress', + defaultToken: { + address: 'address', + decimals: '8', + imageUrl: 'http://imageurl.icon', + name: 'ELF', + symbol: 'ELF', + }, }, ]; const mockState = { @@ -544,6 +551,13 @@ describe('setChainListAction', () => { endPoint: 'http://localhost:1235', explorerUrl: 'http://localhost:1235', caContractAddress: 'caContractAddress', + defaultToken: { + address: 'address', + decimals: '8', + imageUrl: 'http://imageurl.icon', + name: 'ELF', + symbol: 'ELF', + }, }, ]; const mockState = { @@ -560,6 +574,13 @@ describe('setChainListAction', () => { endPoint: 'http://localhost:1235', explorerUrl: 'http://localhost:1235', caContractAddress: 'caContractAddress', + defaultToken: { + address: 'address', + decimals: '8', + imageUrl: 'http://imageurl.icon', + name: 'ELF', + symbol: 'ELF', + }, }, ], }, diff --git a/packages/web-extension-did/app/web/utils/errorHandler.test.ts b/packages/web-extension-did/app/web/utils/errorHandler.test.ts index 0338a91d73..82d20b74a6 100644 --- a/packages/web-extension-did/app/web/utils/errorHandler.test.ts +++ b/packages/web-extension-did/app/web/utils/errorHandler.test.ts @@ -20,17 +20,6 @@ describe('errorHandler', () => { data: undefined, }); }); - test('should return the correct output for an error number', () => { - const code = 200002; - const error = 200002; - const output = errorHandler(code, error); - expect(output).toEqual({ - error: code, - name: undefined, - message: undefined, - data: undefined, - }); - }); test('should return the correct output for a custom error message', () => { const code = 410002; const error = 'Invalid parameters.'; From 82de42fcfc578b66a0d50ddcd97fd2113915de68 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 18 Jul 2023 14:19:08 +0800 Subject: [PATCH 396/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20item=20name=20sho?= =?UTF-8?q?w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/Bookmark/components/BookmarkItem.tsx | 3 +-- .../js/pages/Discover/Bookmark/components/RecordItem.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 92e38c0625..fc385a7da8 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -103,10 +103,9 @@ export default memo( - {item.url} + {item?.name || item?.url} - {/* drag */} diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx index 5cfbfb152f..97cb269802 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx @@ -108,7 +108,7 @@ export default memo( - {item.url} + {item?.name || item?.url} From a87c13a33d8ed4e83e49439b1fae35125407c899 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 18 Jul 2023 14:19:38 +0800 Subject: [PATCH 397/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20limit=20number?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/discover/slice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 0e8c0df587..10de4ad857 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -45,7 +45,7 @@ export const discoverSlice = createSlice({ // limit number if (RECORD_LIMIT <= targetNetworkDiscover?.recordsList.length) { - targetNetworkDiscover?.tabs.shift(); + targetNetworkDiscover?.recordsList.shift(); } if (targetItem) { From 11eaeaede716388c7acf86d51d109eccfd31138a Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 18 Jul 2023 14:34:26 +0800 Subject: [PATCH 398/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20button=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/Bookmark/components/BookmarksSection.tsx | 1 + .../js/pages/Discover/Bookmark/components/RecordsSection.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index de584710f7..160647e89b 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -219,6 +219,7 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'space-between', borderRadius: pTd(6), + paddingBottom: pTd(18), }, listWrap: { paddingHorizontal: pTd(20), diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx index 1857315847..7a142046c8 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordsSection.tsx @@ -126,6 +126,7 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'space-between', borderRadius: pTd(6), + paddingBottom: pTd(18), }, listWrap: { paddingHorizontal: pTd(20), From ad5e13b80e54ac8f2c2793291a9a0e00dc6a6e2c Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 18 Jul 2023 15:11:05 +0800 Subject: [PATCH 399/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20add=20ut=20case?= =?UTF-8?q?=20and=20fix=20jest=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.js | 28 +++++++++---------- packages/hooks/hooks-ca/chainList.test.ts | 9 ++++++ packages/hooks/hooks-ca/graphql.test.ts | 7 +++++ .../hooks/hooks-ca/useTokensPrice.test.ts | 14 ++++++++++ .../app/web/utils/errorHandler.test.ts | 1 + 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/jest.config.js b/jest.config.js index 5a67f2c7bf..b43294e7e4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,16 +8,17 @@ module.exports = { '**/packages/hooks/hooks-ca/*.{ts,tsx}', '**/packages/store/store-ca/**/slice.{ts,tsx}', '**/packages/utils/wallet/index.ts', + '**/packages/utils/activity.ts', '**/packages/web-extension-did/app/web/store/reducers/**/*.{ts,tsx}', '**/packages/web-extension-did/app/web/controllers/approval/*.ts', '**/packages/web-extension-did/app/web/controllers/methodController/*.ts', - // '**/packages/web-extension-did/app/web/utils/device.ts', - '**/packages/web-extension-did/app/web/utils/errorHandle.ts', + '**/packages/web-extension-did/app/web/utils/device.ts', + '**/packages/web-extension-did/app/web/utils/errorHandler.ts', '**/packages/web-extension-did/app/web/hooks/useActiveLockStatus.ts', '**/packages/web-extension-did/app/web/hooks/useCaInfoOnChain.ts', '**/packages/web-extension-did/app/web/hooks/useNetwork.ts', - '!**/packages/store/store-ca/{cms,dapp,discover,misc}/*.{ts,tsx}', - '!**/packages/hooks/hooks-ca/contact.{ts,tsx}', + '!**/packages/store/store-ca/{cms,dapp,discover,misc,txFee}/*.{ts,tsx}', + '!**/packages/hooks/hooks-ca/{contact,useTxFee}.{ts,tsx}', '!**/node_modules/**', '!**/*.test.{ts,tsx}', ], @@ -53,20 +54,14 @@ module.exports = { testEnvironment: 'jsdom', }, { - displayName: 'web-extension-did-store', - preset: 'ts-jest', - testMatch: ['/packages/web-extension-did/app/web/store/**/*.test.{ts,tsx}'], - testEnvironment: 'jsdom', - }, - { - displayName: 'web-extension-did-hooks', + displayName: 'web-extension-did', preset: 'ts-jest', testMatch: [ '/packages/web-extension-did/app/web/hooks/*.test.{ts,tsx}', + '/packages/web-extension-did/app/web/store/**/*.test.{ts,tsx}', '/packages/web-extension-did/app/web/controllers/approval/*.test.ts', '/packages/web-extension-did/app/web/controllers/methodController/*.test.ts', - '/packages/web-extension-did/app/web/utils/errorHandle.test.ts', - '/packages/web-extension-did/app/web/utils/device.test.ts', + '/packages/web-extension-did/app/web/utils/*.test.ts', ], testEnvironment: 'jsdom', transform: { @@ -86,7 +81,12 @@ module.exports = { '^service/(.*)$': '/packages/web-extension-did/app/web/service/$1', '^controllers/(.*)$': '/packages/web-extension-did/app/web/controllers/$1', }, - coveragePathIgnorePatterns: ['/node_modules/', '/store/', '/hooks-ca/', '/utils/'], + coveragePathIgnorePatterns: [ + '/node_modules/', + '/packages/store/store-ca/', + '/packages/hooks/hooks-ca/', + '/packages/utils/', + ], }, // { // displayName: 'mobile-app-did', diff --git a/packages/hooks/hooks-ca/chainList.test.ts b/packages/hooks/hooks-ca/chainList.test.ts index 4a861ae94c..e057866bcf 100644 --- a/packages/hooks/hooks-ca/chainList.test.ts +++ b/packages/hooks/hooks-ca/chainList.test.ts @@ -2,6 +2,7 @@ import { useChainListFetch, useCurrentChainList, useCurrentChain, + useDefaultToken, useIsValidSuffix, useGetChainInfo, } from './chainList'; @@ -66,6 +67,14 @@ describe('useCurrentChain', () => { }); }); +describe('useDefaultToken', () => { + test('chainInfo have AELF info, and return AELF', () => { + jest.mocked(useOriginChainId).mockReturnValue('AELF'); + const { result } = renderHook(() => useDefaultToken('AELF')); + expect(result.current?.decimals).toBe('8'); + }); +}); + describe('useIsValidSuffix', () => { test('in TESTNET, chainInfo do not have AELF info, cant get suffix', () => { jest.mocked(useCurrentWallet).mockReturnValue(currentWallet('TESTNET')); diff --git a/packages/hooks/hooks-ca/graphql.test.ts b/packages/hooks/hooks-ca/graphql.test.ts index 5561888caa..b595c46d7b 100644 --- a/packages/hooks/hooks-ca/graphql.test.ts +++ b/packages/hooks/hooks-ca/graphql.test.ts @@ -33,6 +33,13 @@ const CHAIN_INFO = { explorerUrl: 'https://localhost', id: 'AELF', lastModifyTime: '2023-02-25T07:15:23.6079047Z', + defaultToken: { + address: '2ZpT...3Udb', + decimals: '8', + imageUrl: 'https://localhost', + name: 'AELF', + symbol: 'ELF', + }, }; const CA_HOLDER_MANAGER_INFO: CaHolderWithGuardian[] = [ diff --git a/packages/hooks/hooks-ca/useTokensPrice.test.ts b/packages/hooks/hooks-ca/useTokensPrice.test.ts index cb911526ac..90c9ca5798 100644 --- a/packages/hooks/hooks-ca/useTokensPrice.test.ts +++ b/packages/hooks/hooks-ca/useTokensPrice.test.ts @@ -52,6 +52,20 @@ describe('useGetCurrentAccountTokenPrice', () => { result.current[1](); expect(assetSlice.fetchTokensPriceAsync).toHaveBeenCalledTimes(0); }); + + test('set ELF params, and call getTokensPrice method', () => { + jest.spyOn(baseHooks, 'useAppCommonDispatch').mockReturnValue(() => async (call: () => void) => { + return call; + }); + const { result } = renderHookWithProvider( + useGetCurrentAccountTokenPrice, + setupStore({ assets: { ...AssetsState.assets, accountToken: [] } }), + ); + expect(result.current[2]).toBeInstanceOf(Function); + + result.current[2](); + expect(assetSlice.fetchTokensPriceAsync).toHaveBeenCalledTimes(1); + }); }); describe('useFreshTokenPrice', () => { diff --git a/packages/web-extension-did/app/web/utils/errorHandler.test.ts b/packages/web-extension-did/app/web/utils/errorHandler.test.ts index 82d20b74a6..f067a3bf8a 100644 --- a/packages/web-extension-did/app/web/utils/errorHandler.test.ts +++ b/packages/web-extension-did/app/web/utils/errorHandler.test.ts @@ -1,4 +1,5 @@ import errorHandler from './errorHandler'; + describe('errorHandler', () => { test('should return the correct output for a success code', () => { const code = 0; From 2e97a5d502846105d04ec7120ceb20a50c37046f Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 18 Jul 2023 15:46:52 +0800 Subject: [PATCH 400/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20android=20webview?= =?UTF-8?q?=20load?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/ProviderWebview/index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx index ddbbdb81d0..1260a06e0b 100644 --- a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx +++ b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx @@ -25,6 +25,8 @@ export interface IWebView { const ProviderWebview = forwardRef(function ProviderWebview(props, forward) { const webViewRef = useRef(null); const operatorRef = useRef(null); + // Android will trigger onLoadEnd before onLoadStart, Mark start status. + const loadStartRef = useRef(false); const [entryScriptWeb3, setEntryScriptWeb3] = useState(); useEffectOnce(() => { const getEntryScriptWeb3 = async () => { @@ -57,6 +59,7 @@ const ProviderWebview = forwardRef(function const onLoadStart = useCallback( ({ nativeEvent }: WebViewNavigationEvent) => { + if (!loadStartRef.current) loadStartRef.current = true; const { origin } = new URL(nativeEvent.url); initOperator(origin); }, @@ -121,15 +124,17 @@ const ProviderWebview = forwardRef(function injectedJavaScriptBeforeContentLoaded={isIos ? entryScriptWeb3 : undefined} applicationNameForUserAgent={'WebView Portkey did Mobile'} {...props} - onLoadEnd={event => { - handleUpdate(event); - props.onLoadEnd?.(event); - }} onLoadStart={event => { onLoadStart(event); props.onLoadStart?.(event); }} + onLoadEnd={event => { + if (!loadStartRef.current) return; + handleUpdate(event); + props.onLoadEnd?.(event); + }} onLoad={event => { + if (!loadStartRef.current) return; handleUpdate(event); props.onLoad?.(event); }} From ac680b85a01931706f4f6d15dfa6a3efe6b7c12d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 18 Jul 2023 16:46:54 +0800 Subject: [PATCH 401/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20records=20name?= =?UTF-8?q?=20and=20toast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/Bookmark/components/BookmarkItem.tsx | 7 +++---- .../Discover/Bookmark/components/BookmarksSection.tsx | 3 +-- .../pages/Discover/Bookmark/components/RecordItem.tsx | 4 ++-- .../Discover/components/SearchRecordItem/index.tsx | 2 +- packages/types/types-ca/discover.ts | 11 +---------- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index fc385a7da8..5c4c08327a 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -18,7 +18,6 @@ import useEffectOnce from 'hooks/useEffectOnce'; import { IBookmarkItem } from '@portkey-wallet/store/store-ca/discover/type'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; -import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; type BookmarkItemProps = RenderItemParams & { onDelete: (item: T) => void; @@ -26,7 +25,7 @@ type BookmarkItemProps = RenderItemParams & { export default memo( function BookmarkItem(props: BookmarkItemProps) { - const { drag, isActive, item, onDelete } = props; + const { item, onDelete } = props; const swipeableRef = useRef(null); const [{ isEdit }] = useBookmark(); const preIsEdit = usePrevious(isEdit); @@ -101,9 +100,9 @@ export default memo( {EditDom} - + - {item?.name || item?.url} + {item?.url} {/* diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index 160647e89b..b6f7ff92b4 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -19,7 +19,6 @@ import CommonToast from 'components/CommonToast'; import { request } from '@portkey-wallet/api/api-did'; import Loading from 'components/Loading'; import ActionSheet from 'components/ActionSheet'; -import { TextXL } from 'components/CommonText'; import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; import { sleep } from '@portkey-wallet/utils'; @@ -116,7 +115,7 @@ function BookmarksSection() { await sleep(100); getBookmarkListRef.current(true); } catch (error) { - CommonToast.failError(error); + CommonToast.failError('Edit failed, please try again'); } Loading.hide(); } diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx index 97cb269802..1ea46800ae 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx @@ -106,9 +106,9 @@ export default memo( imageUrl={getFaviconUrl(item?.url || '')} /> - + - {item?.name || item?.url} + {item?.url} diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx index d5acbdbd73..d4df1642b3 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordItem/index.tsx @@ -22,7 +22,7 @@ const SearchRecordItem: React.FC = props => { - + {item?.url || ''} diff --git a/packages/types/types-ca/discover.ts b/packages/types/types-ca/discover.ts index d2621d62a9..fd6e4f0bb8 100644 --- a/packages/types/types-ca/discover.ts +++ b/packages/types/types-ca/discover.ts @@ -1,14 +1,5 @@ -export interface IGameListItemType { - label: string; - name: string; - title: string; - imgUrl: string; - pngName: string; - introduction: string; - url: string; -} - export interface IRecordsItemType { + name?: string; title?: string; url?: string; id: string | number; From 1c891c9750fb4a02f0de178adba0d9f8b8fe671a Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 18 Jul 2023 17:05:49 +0800 Subject: [PATCH 402/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20change=20jest.co?= =?UTF-8?q?nfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jest.config.js b/jest.config.js index b43294e7e4..bc9c3e62f8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -14,11 +14,11 @@ module.exports = { '**/packages/web-extension-did/app/web/controllers/methodController/*.ts', '**/packages/web-extension-did/app/web/utils/device.ts', '**/packages/web-extension-did/app/web/utils/errorHandler.ts', - '**/packages/web-extension-did/app/web/hooks/useActiveLockStatus.ts', - '**/packages/web-extension-did/app/web/hooks/useCaInfoOnChain.ts', - '**/packages/web-extension-did/app/web/hooks/useNetwork.ts', + // '**/packages/web-extension-did/app/web/hooks/useActiveLockStatus.ts', + // '**/packages/web-extension-did/app/web/hooks/useCaInfoOnChain.ts', + // '**/packages/web-extension-did/app/web/hooks/useNetwork.ts', '!**/packages/store/store-ca/{cms,dapp,discover,misc,txFee}/*.{ts,tsx}', - '!**/packages/hooks/hooks-ca/{contact,useTxFee}.{ts,tsx}', + '!**/packages/hooks/hooks-ca/{contact,useTxFee,wallet}.{ts,tsx}', '!**/node_modules/**', '!**/*.test.{ts,tsx}', ], @@ -57,7 +57,7 @@ module.exports = { displayName: 'web-extension-did', preset: 'ts-jest', testMatch: [ - '/packages/web-extension-did/app/web/hooks/*.test.{ts,tsx}', + // '/packages/web-extension-did/app/web/hooks/*.test.{ts,tsx}', '/packages/web-extension-did/app/web/store/**/*.test.{ts,tsx}', '/packages/web-extension-did/app/web/controllers/approval/*.test.ts', '/packages/web-extension-did/app/web/controllers/methodController/*.test.ts', From d87b0ee97c11b81a41162ed95cf95335be441a48 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 18 Jul 2023 17:33:57 +0800 Subject: [PATCH 403/893] =?UTF-8?q?test:=20=F0=9F=92=8D=20change=20jest.co?= =?UTF-8?q?nfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest.config.js b/jest.config.js index bc9c3e62f8..9ee9eafcb5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -39,6 +39,7 @@ module.exports = { displayName: 'hooks', preset: 'ts-jest', testMatch: ['/packages/hooks/hooks-ca/*.test.{ts,tsx}'], + testPathIgnorePatterns: ['/packages/hooks/hooks-ca/wallet.test.ts'], testEnvironment: 'jsdom', }, { From ca6f971e6a49a395f876476ba88ce7f5c9117ef9 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 18 Jul 2023 18:44:41 +0800 Subject: [PATCH 404/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20default?= =?UTF-8?q?=20fee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Send/SendPreview/index.tsx | 16 +++++++++------- .../js/utils/transfer/crossChainTransfer.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx b/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx index 2000bc531a..486bd21acf 100644 --- a/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx @@ -55,7 +55,7 @@ const SendHome: React.FC = () => { const { sendType, assetInfo, toInfo, transactionFee, sendNumber } = useRouterParams(); useFetchTxFee(); - const { crossChain: crossFee } = useGetTxFee(assetInfo.chainId); + const { crossChain: crossDefaultFee } = useGetTxFee(assetInfo.chainId); const dispatch = useAppCommonDispatch(); const pin = usePin(); @@ -132,6 +132,7 @@ const SendHome: React.FC = () => { tokenInfo: { ...assetInfo, address: assetInfo.tokenContractAddress } as unknown as BaseToken, caHash: wallet.caHash || '', amount, + crossDefaultFee, toAddress: toInfo.address, }); @@ -171,6 +172,7 @@ const SendHome: React.FC = () => { caAddressInfos, caAddresses, chainInfo, + crossDefaultFee, currentNetwork.walletType, dispatch, isCrossChainTransfer, @@ -353,12 +355,12 @@ const SendHome: React.FC = () => { {`${unitConverter( - crossFee, + crossDefaultFee, )} ELF`} {!isTestnet ? ( {`$ ${unitConverter( - ZERO.plus(crossFee).multipliedBy(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(crossDefaultFee).multipliedBy(tokenPriceObject[ELF_SYMBOL]), )}`} ) : ( @@ -376,17 +378,17 @@ const SendHome: React.FC = () => { - {ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossFee)) + {ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossDefaultFee)) ? '0' - : formatAmountShow(ZERO.plus(sendNumber).minus(ZERO.plus(crossFee)))}{' '} + : formatAmountShow(ZERO.plus(sendNumber).minus(ZERO.plus(crossDefaultFee)))}{' '} {'ELF'} {!isTestnet ? ( {`$ ${ - ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossFee)) + ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossDefaultFee)) ? '0' : formatAmountShow( - ZERO.plus(sendNumber).minus(ZERO.plus(crossFee)).times(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(sendNumber).minus(ZERO.plus(crossDefaultFee)).times(tokenPriceObject[ELF_SYMBOL]), 2, ) }`} diff --git a/packages/mobile-app-did/js/utils/transfer/crossChainTransfer.ts b/packages/mobile-app-did/js/utils/transfer/crossChainTransfer.ts index edc146c0c3..8baa6f03e1 100644 --- a/packages/mobile-app-did/js/utils/transfer/crossChainTransfer.ts +++ b/packages/mobile-app-did/js/utils/transfer/crossChainTransfer.ts @@ -7,6 +7,8 @@ import { getChainNumber } from '@portkey-wallet/utils/aelf'; import { ContractBasic } from '@portkey-wallet/contracts/utils/ContractBasic'; import { ZERO } from '@portkey-wallet/constants/misc'; import { timesDecimals } from '@portkey-wallet/utils/converter'; +import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; +import { ELF_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; export interface CrossChainTransferParamsType { tokenInfo: BaseToken; @@ -66,6 +68,7 @@ interface CrossChainTransferParams extends CrossChainTransferParamsType { tokenContract: ContractBasic; contract: ContractBasic; caHash: string; + crossDefaultFee: number; } const crossChainTransfer = async ({ tokenInfo, @@ -77,6 +80,7 @@ const crossChainTransfer = async ({ tokenContract, contract, caHash, + crossDefaultFee, }: CrossChainTransferParams) => { let managerTransferResult; try { @@ -109,7 +113,10 @@ const crossChainTransfer = async ({ tokenInfo, chainType, managerAddress, - amount: tokenInfo.symbol === 'ELF' ? ZERO.plus(amount).minus(timesDecimals(0.35, 8)).toFixed() : amount, + amount: + tokenInfo.symbol === ELF_SYMBOL + ? ZERO.plus(amount).minus(timesDecimals(crossDefaultFee, ELF_DECIMAL)).toFixed() + : amount, memo, toAddress, }; From 30fbe43d5f371eaa9f39d6c51bf61cc07d4ac2b3 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 18 Jul 2023 18:55:15 +0800 Subject: [PATCH 405/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20adjust=20crosscha?= =?UTF-8?q?in=20fee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/utils/sandboxUtil/crossChainTransfer.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts b/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts index 96b6a98e31..6df74a2599 100644 --- a/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts +++ b/packages/web-extension-did/app/web/utils/sandboxUtil/crossChainTransfer.ts @@ -1,5 +1,5 @@ import { ChainItemType } from '@portkey-wallet/store/store-ca/wallet/type'; -import { ChainId, ChainType } from '@portkey-wallet/types'; +import { ChainType } from '@portkey-wallet/types'; import { BaseToken } from '@portkey-wallet/types/types-ca/token'; import { getChainIdByAddress } from '@portkey-wallet/utils'; import { crossChainTransferToCa } from './crossChainTransferToCa'; @@ -117,8 +117,7 @@ const crossChainTransfer = async ({ // return; // TODO Only support chainType: aelf let _amount = amount; - const toChainId = getChainIdByAddress(toAddress, chainType); - const { crossChain: crossChainFee } = getTxFee(toChainId as ChainId); + const { crossChain: crossChainFee } = getTxFee(tokenInfo.chainId); if (tokenInfo.symbol === nativeToken.symbol) { _amount = ZERO.plus(amount).minus(timesDecimals(crossChainFee, 8)).toNumber(); } From 92ce89dda1571b956ee2a7d33fabd49795446de5 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 18 Jul 2023 17:39:58 +0800 Subject: [PATCH 406/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20sell=20quest=20o?= =?UTF-8?q?rder=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constants/constants-ca/payment/index.ts | 2 +- packages/constants/constants-ca/socket.ts | 2 +- packages/hooks/hooks-ca/payment.ts | 99 ++++++++++++------- packages/socket/socket-sell/index.ts | 23 ++++- packages/types/types-ca/payment.ts | 5 +- 5 files changed, 89 insertions(+), 42 deletions(-) diff --git a/packages/constants/constants-ca/payment/index.ts b/packages/constants/constants-ca/payment/index.ts index c648f536da..7b4f83b1f3 100644 --- a/packages/constants/constants-ca/payment/index.ts +++ b/packages/constants/constants-ca/payment/index.ts @@ -31,7 +31,7 @@ export const ACH_MERCHANT_NAME = 'Alchemy'; export const FAUCET_URL = 'https://testnet-faucet.aelf.io/'; -export const SELL_SOCKET_TIMEOUT = 20 * 1000; +export const SELL_SOCKET_TIMEOUT = 15 * 1000; export const BUY_SOON_TEXT = 'On-ramp is currently not supported. It will be launched in the coming weeks.'; diff --git a/packages/constants/constants-ca/socket.ts b/packages/constants/constants-ca/socket.ts index f4ead49bc7..b40419c360 100644 --- a/packages/constants/constants-ca/socket.ts +++ b/packages/constants/constants-ca/socket.ts @@ -3,4 +3,4 @@ import { MINUTE } from '../index'; export const SocketUrl = 'http://192.168.66.38:5577/ca'; export const queryExpirationTime = 5 * MINUTE; -export const sellListenList = ['onAchTxAddressReceived'] as const; +export const sellListenList = ['onAchTxAddressReceived', 'onOrderTransferredReceived'] as const; diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 18844c1551..e90f3d65dc 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -1,12 +1,12 @@ import { useAppCASelector } from '.'; import { getAchToken } from '@portkey-wallet/api/api-did/payment/util'; import { AchTokenInfoType } from '@portkey-wallet/store/store-ca/payment/type'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { useGuardiansInfo } from './guardian'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import { useIsMainnet } from './network'; import { ACH_MERCHANT_NAME, SELL_SOCKET_TIMEOUT } from '@portkey-wallet/constants/constants-ca/payment'; -import { AchTxAddressReceivedType, SellTransferParams } from '@portkey-wallet/types/types-ca/payment'; +import { RequestOrderTransferredType, SellTransferParams } from '@portkey-wallet/types/types-ca/payment'; import signalrSell from '@portkey-wallet/socket/socket-sell'; import { request } from '@portkey-wallet/api/api-did'; import { randomId } from '@portkey-wallet/utils'; @@ -36,15 +36,23 @@ export const useGetAchTokenInfo = () => { }, [userGuardiansList]); }; +enum STAGE { + ACHTXADS, // onAchTxAddressReceived + TRANSACTION, // transaction + ORDER, // onRequestOrderTransferred +} + export const useSellTransfer = () => { const isMainnet = useIsMainnet(); + const [status, setStatus] = useState(STAGE.ACHTXADS); return useCallback( async ({ merchantName, orderId, paymentSellTransfer }: SellTransferParams) => { if (!isMainnet || merchantName !== ACH_MERCHANT_NAME) return; - let achTxAddressReceived: AchTxAddressReceivedType; - let signalrSellRemove: (() => void) | undefined; + let signalrAchTxRemove: (() => void) | undefined; + let signalrOrderRemove: (() => void) | undefined; + try { const clientId = randomId(); await signalrSell.doOpen({ @@ -52,55 +60,70 @@ export const useSellTransfer = () => { clientId, }); - const timerPromise = new Promise(resolve => + const timerPromise = new Promise<'timeout'>(resolve => setTimeout(() => { - resolve(null); + resolve('timeout'); }, SELL_SOCKET_TIMEOUT), ); - const signalrSellPromise = new Promise(resolve => { - const { remove } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, data => { - resolve(data); + + const signalrSellPromise = new Promise(resolve => { + const { remove: removeAchTx } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, async data => { + if (data === null) { + throw new Error('Transaction failed.'); + } + + try { + setStatus(STAGE.TRANSACTION); + const result = await paymentSellTransfer(data); + await request.payment.sendSellTransaction({ + params: { + merchantName: ACH_MERCHANT_NAME, + orderId, + rawTransaction: result.rawTransaction, + signature: result.signature, + publicKey: result.publicKey, + }, + }); + } catch (e) { + throw { + code: 'NO_TX_HASH', + message: 'Transaction failed. Please contact the team for assistance.', + }; + } + + const { remove: removeRes } = signalrSell.onRequestOrderTransferred({ clientId, orderId }, async data => { + setStatus(STAGE.ORDER); + resolve(data); + }); + signalrOrderRemove = removeRes; + signalrSell.RequestOrderTransferred(clientId, orderId); }); - signalrSellRemove = remove; + signalrAchTxRemove = removeAchTx; signalrSell.requestAchTxAddress(clientId, orderId); }); - const signalrSellResult = await Promise.race([timerPromise, signalrSellPromise]); - - if (signalrSellResult === null) { - throw new Error('Transaction failed.'); + if (signalrSellResult === null) throw new Error('Transaction failed.'); + if (signalrSellResult === 'timeout') { + if (status === STAGE.ACHTXADS) throw new Error('Transaction failed.'); + throw { + code: 'TIMEOUT', + message: 'The waiting time is too long, it will be put on hold in the background.', + }; } - achTxAddressReceived = signalrSellResult; + if (signalrSellResult.status === 'TransferFailed') throw new Error('Transaction failed.'); } catch (error) { throw { code: 'TIMEOUT', - message: 'Transaction failed.', + message: 'The waiting time is too long, it will be put on hold in the background.', }; } finally { - signalrSellRemove?.(); - signalrSellRemove = undefined; + signalrAchTxRemove?.(); + signalrAchTxRemove = undefined; + signalrOrderRemove?.(); + signalrOrderRemove = undefined; signalrSell.stop(); } - - try { - const result = await paymentSellTransfer(achTxAddressReceived); - - await request.payment.sendSellTransaction({ - params: { - merchantName: ACH_MERCHANT_NAME, - orderId, - rawTransaction: result.rawTransaction, - signature: result.signature, - publicKey: result.publicKey, - }, - }); - } catch (error) { - throw { - code: 'NO_TX_HASH', - message: 'Transaction failed. Please contact the team for assistance.', - }; - } }, - [isMainnet], + [isMainnet, status], ); }; diff --git a/packages/socket/socket-sell/index.ts b/packages/socket/socket-sell/index.ts index 4a19da4e40..57efc61749 100644 --- a/packages/socket/socket-sell/index.ts +++ b/packages/socket/socket-sell/index.ts @@ -1,5 +1,5 @@ import { sellListenList } from '@portkey-wallet/constants/constants-ca/socket'; -import { AchTxAddressReceivedType } from '@portkey-wallet/types/types-ca/payment'; +import { AchTxAddressReceivedType, RequestOrderTransferredType } from '@portkey-wallet/types/types-ca/payment'; import { BaseSignalr } from '@portkey/socket'; export class SignalrSell extends BaseSignalr { @@ -23,6 +23,27 @@ export class SignalrSell extends BaseSignalr { } }); } + + public RequestOrderTransferred(clientId: string, orderId: string) { + console.log('invoke RequestOrderTransferred', clientId, orderId); + this.invoke('RequestOrderTransferred', { + TargetClientId: clientId, + OrderId: orderId, + }); + } + + public onRequestOrderTransferred( + { orderId }: { clientId: string; orderId: string }, + callback: (data: RequestOrderTransferredType | null) => void, + ) { + return this.listen('onOrderTransferredReceived', (data: { body: RequestOrderTransferredType }) => { + if (data?.body?.orderId === orderId) { + callback(data.body); + } else { + callback(null); + } + }); + } } const signalrSell = new SignalrSell({ diff --git a/packages/types/types-ca/payment.ts b/packages/types/types-ca/payment.ts index 0b5faaa3e8..16d40acd0e 100644 --- a/packages/types/types-ca/payment.ts +++ b/packages/types/types-ca/payment.ts @@ -1,5 +1,4 @@ import { ACH_MERCHANT_NAME } from '@portkey-wallet/constants/constants-ca/payment'; -import { SendResult } from '@portkey-wallet/contracts/types'; export interface CountryItem { country: string; @@ -18,6 +17,10 @@ export interface AchTxAddressReceivedType { address: string; } +export interface RequestOrderTransferredType extends AchTxAddressReceivedType { + status: 'Transferred' | 'TransferFailed'; +} + export interface PaymentSellTransferResult { publicKey: string; signature: string; // sign(md5(orderId + rawTransaction)) From 4829748148d4d6ac9a3a2756c7e2f40250e97f19 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 18 Jul 2023 19:58:11 +0800 Subject: [PATCH 407/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20method?= =?UTF-8?q?=20naming=20and=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 12 ++++++------ packages/socket/socket-sell/index.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index e90f3d65dc..a43725ac02 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -1,7 +1,7 @@ import { useAppCASelector } from '.'; import { getAchToken } from '@portkey-wallet/api/api-did/payment/util'; import { AchTokenInfoType } from '@portkey-wallet/store/store-ca/payment/type'; -import { useCallback, useState } from 'react'; +import { useCallback, useRef } from 'react'; import { useGuardiansInfo } from './guardian'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import { useIsMainnet } from './network'; @@ -44,7 +44,7 @@ enum STAGE { export const useSellTransfer = () => { const isMainnet = useIsMainnet(); - const [status, setStatus] = useState(STAGE.ACHTXADS); + const status = useRef(STAGE.ACHTXADS); return useCallback( async ({ merchantName, orderId, paymentSellTransfer }: SellTransferParams) => { @@ -73,7 +73,7 @@ export const useSellTransfer = () => { } try { - setStatus(STAGE.TRANSACTION); + status.current = STAGE.TRANSACTION; const result = await paymentSellTransfer(data); await request.payment.sendSellTransaction({ params: { @@ -92,11 +92,11 @@ export const useSellTransfer = () => { } const { remove: removeRes } = signalrSell.onRequestOrderTransferred({ clientId, orderId }, async data => { - setStatus(STAGE.ORDER); + status.current = STAGE.ORDER; resolve(data); }); signalrOrderRemove = removeRes; - signalrSell.RequestOrderTransferred(clientId, orderId); + signalrSell.requestOrderTransferred(clientId, orderId); }); signalrAchTxRemove = removeAchTx; signalrSell.requestAchTxAddress(clientId, orderId); @@ -104,7 +104,7 @@ export const useSellTransfer = () => { const signalrSellResult = await Promise.race([timerPromise, signalrSellPromise]); if (signalrSellResult === null) throw new Error('Transaction failed.'); if (signalrSellResult === 'timeout') { - if (status === STAGE.ACHTXADS) throw new Error('Transaction failed.'); + if (status.current === STAGE.ACHTXADS) throw new Error('Transaction failed.'); throw { code: 'TIMEOUT', message: 'The waiting time is too long, it will be put on hold in the background.', diff --git a/packages/socket/socket-sell/index.ts b/packages/socket/socket-sell/index.ts index 57efc61749..bc23d2d355 100644 --- a/packages/socket/socket-sell/index.ts +++ b/packages/socket/socket-sell/index.ts @@ -24,7 +24,7 @@ export class SignalrSell extends BaseSignalr { }); } - public RequestOrderTransferred(clientId: string, orderId: string) { + public requestOrderTransferred(clientId: string, orderId: string) { console.log('invoke RequestOrderTransferred', clientId, orderId); this.invoke('RequestOrderTransferred', { TargetClientId: clientId, From c5ae91687c041ece09b3f6743a09b0bfc59c24dc Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 18 Jul 2023 21:35:19 +0800 Subject: [PATCH 408/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20cross=20transfer?= =?UTF-8?q?=20fee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Send/SendHome/index.tsx | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index c5995082e1..ff13e9d6ee 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -96,21 +96,32 @@ const SendHome: React.FC = () => { account: account, }); - const raw = await contract.encodedTx('ManagerForwardCall', { - caHash: wallet.caHash, - contractAddress: selectedAssets.tokenContractAddress, - methodName: 'Transfer', - args: { - symbol: selectedAssets.symbol, - to: isCross ? wallet.address : selectedToContact.address, - amount: timesDecimals(sendAmount ?? debounceSendNumber, selectedAssets.decimals || '0').toNumber(), - }, - }); - console.log('====raw======', { - symbol: selectedAssets.symbol, - to: isCross ? wallet.address : selectedToContact.address, - amount: timesDecimals(sendAmount ?? debounceSendNumber, selectedAssets.decimals || '0').toNumber(), - }); + const firstMethodName = isCross ? 'ManagerTransfer' : 'ManagerForwardCall'; + const secondParams = isCross + ? { + contractAddress: selectedAssets.tokenContractAddress, + caHash: wallet.caHash, + symbol: selectedAssets.symbol, + to: wallet.address, + amount: timesDecimals(sendAmount ?? debounceSendNumber, selectedAssets.decimals || '0').toNumber(), + memo: '', + } + : { + caHash: wallet.caHash, + contractAddress: selectedAssets.tokenContractAddress, + methodName: 'Transfer', + args: { + symbol: selectedAssets.symbol, + to: selectedToContact.address, + amount: timesDecimals(sendAmount ?? debounceSendNumber, selectedAssets.decimals || '0').toNumber(), + memo: '', + }, + }; + + const raw = await contract.encodedTx(firstMethodName, secondParams); + + console.log('====raw======', firstMethodName, secondParams); + const { TransactionFee } = await customFetch(`${chainInfo?.endPoint}/api/blockChain/calculateTransactionFee`, { method: 'POST', params: { From 4525bb7d7c843740d25bd24bc3448036276c2cd9 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 19 Jul 2023 14:15:29 +0800 Subject: [PATCH 409/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20discover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 20 +++++---- .../TabsDrawer/TabsDrawerContent.tsx | 43 +++++++++++------- packages/mobile-app-did/js/hooks/discover.ts | 45 ++++++++++++++++++- .../Bookmark/components/RecordItem.tsx | 4 +- .../DiscoverArchivedSection/index.tsx | 8 ++-- .../DiscoverCmsListSection/index.tsx | 2 +- .../components/SearchRecordSection/index.tsx | 33 +++++++------- 7 files changed, 107 insertions(+), 48 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 6ba824ceb8..c3ca16978c 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -9,9 +9,10 @@ import { getProtocolAndHost, isDangerousLink } from '@portkey-wallet/utils/dapp/ type BrowserTabProps = { isHidden: boolean; + id: string | number; uri: string; autoApprove?: boolean; - onLoadEnd?: () => void; + onLoadEnd?: (nativeEvent: any) => void; }; const Options: CaptureOptions = { quality: 0.2, format: 'jpg' }; @@ -40,13 +41,16 @@ const BrowserTab = forwardRef(function BrowserTab( // eslint-disable-next-line react-hooks/exhaustive-deps }, [isHidden, options]); - const onPageLoadEnd = useCallback(() => { - if (!isDangerousLink(getProtocolAndHost(uri)) && !isApproved.current && autoApprove) { - isApproved.current = true; - webViewRef.current?.autoApprove(); - } - onLoadEnd?.(); - }, [autoApprove, uri, onLoadEnd]); + const onPageLoadEnd = useCallback( + ({ nativeEvent }: any) => { + if (!isDangerousLink(getProtocolAndHost(uri)) && !isApproved.current && autoApprove) { + isApproved.current = true; + webViewRef.current?.autoApprove(); + } + onLoadEnd?.(nativeEvent); + }, + [autoApprove, uri, onLoadEnd], + ); return ( { const { t } = useLanguage(); @@ -44,11 +45,11 @@ const TabsDrawerContent: React.FC = () => { activeTabId, autoApproveMap, } = useAppCASelector(state => state.discover); - const { tabs } = discoverMap[networkType] ?? {}; + const checkAndUpDateRecordItemName = useCheckAndUpDateRecordItemName(); + const checkAndUpDateTabItemName = useCheckAndUpDateTabItemName(); const tabRef = useRef(null); - const [preActiveTabId, setPreActiveTabId] = useState(activeTabId); const activeWebviewScreenShot = useCallback(async () => { @@ -94,7 +95,16 @@ const TabsDrawerContent: React.FC = () => { return null; }, [activeTabId, activeWebviewScreenShot, tabs]); - const tabsDom = useMemo(() => { + const onBrowserTabLoadEnd = useCallback( + ({ nativeEvent, autoApprove, item }: { nativeEvent: any; item: ITabItem; autoApprove?: boolean }) => { + if (autoApprove) dispatch(removeAutoApproveItem(item.id)); + checkAndUpDateRecordItemName({ id: item.id, name: nativeEvent.title }); + checkAndUpDateTabItemName({ id: item.id, name: nativeEvent.title }); + }, + [checkAndUpDateRecordItemName, checkAndUpDateTabItemName, dispatch], + ); + + const TabsDom = useMemo(() => { return tabs?.map(ele => { const isHidden = activeTabId !== ele.id; const initialized = initializedList?.has(ele.id); @@ -103,14 +113,15 @@ const TabsDrawerContent: React.FC = () => { return ( dispatch(removeAutoApproveItem(ele.id)) : undefined} + onLoadEnd={nativeEvent => onBrowserTabLoadEnd({ nativeEvent, item: ele, autoApprove })} /> ); }); - }, [activeTabId, autoApproveMap, dispatch, initializedList, tabs]); + }, [activeTabId, autoApproveMap, initializedList, onBrowserTabLoadEnd, tabs]); const value = useMemo( () => ({ @@ -147,6 +158,15 @@ const TabsDrawerContent: React.FC = () => { }); }, [dispatch, networkType, t, tabs?.length]); + const onDone = useCallback(() => { + if (tabs?.length === 0) return dispatch(changeDrawerOpenStatus(false)); + if (tabs?.find(ele => ele.id === preActiveTabId)) { + dispatch(setActiveTab({ id: preActiveTabId, networkType })); + } else { + dispatch(setActiveTab({ id: tabs?.[tabs?.length - 1]?.id, networkType })); + } + }, [dispatch, networkType, preActiveTabId, tabs]); + useHardwareBackPress( useMemo(() => { if (isDrawerOpen) { @@ -181,7 +201,7 @@ const TabsDrawerContent: React.FC = () => { containerStyles={styles.container} scrollViewProps={{ disabled: true }} titleDom={activeTabId ? '' : `${tabs?.length} Tabs`}> - {tabsDom} + {TabsDom} {/* card group */} {!activeTabId && isDrawerOpen && ( <> @@ -203,16 +223,7 @@ const TabsDrawerContent: React.FC = () => { onPress={() => dispatch(changeDrawerOpenStatus(false))}> - { - if (tabs?.length === 0) return dispatch(changeDrawerOpenStatus(false)); - if (tabs?.find(ele => ele.id === preActiveTabId)) { - dispatch(setActiveTab({ id: preActiveTabId, networkType })); - } else { - dispatch(setActiveTab({ id: tabs?.[tabs?.length - 1]?.id, networkType })); - } - }}> + {t('Done')} diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index f9c5a01691..9d4fa85178 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -11,8 +11,12 @@ import { cleanBookmarkList, addBookmarkList, addAutoApproveItem, + upDateRecordsItem, + updateTab, } from '@portkey-wallet/store/store-ca/discover/slice'; import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { isUrl } from '@portkey-wallet/utils'; +import { prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; import { useCallback, useEffect, useMemo } from 'react'; @@ -57,7 +61,6 @@ export const useDiscoverWhiteList = () => { const checkIsInWhiteList = useCallback( (url: string) => { - console.log('discoverMap', discoverMap && discoverMap[networkType]?.whiteList); return discoverMap && discoverMap[networkType]?.whiteList?.includes(url); }, [discoverMap, networkType], @@ -124,3 +127,43 @@ export const useRecordsList = (isReverse = true): ITabItem[] => { return list || []; }; + +export const useCheckAndUpDateRecordItemName = () => { + const { networkType } = useCurrentNetworkInfo(); + const { discoverMap } = useAppCASelector(state => state.discover); + const dispatch = useAppCommonDispatch(); + + return useCallback( + ({ id, name }: { id: number; name: string }) => { + try { + const recordsItem = discoverMap?.[networkType]?.recordsList?.find(ele => ele.id === id); + if (recordsItem && (!recordsItem.name || isUrl(prefixUrlWithProtocol(recordsItem.name)))) { + dispatch(upDateRecordsItem({ ...recordsItem, name, networkType })); + } + } catch (err) { + console.log(err); + } + }, + [discoverMap, dispatch, networkType], + ); +}; + +export const useCheckAndUpDateTabItemName = () => { + const { networkType } = useCurrentNetworkInfo(); + const { discoverMap } = useAppCASelector(state => state.discover); + const dispatch = useAppCommonDispatch(); + + return useCallback( + ({ id, name }: { id: number; name: string }) => { + try { + const tabItem = discoverMap?.[networkType]?.tabs?.find(ele => ele.id === id); + if (tabItem && (!tabItem.name || isUrl(prefixUrlWithProtocol(tabItem.name)))) { + dispatch(updateTab({ ...tabItem, name, networkType })); + } + } catch (err) { + console.log(err); + } + }, + [discoverMap, dispatch, networkType], + ); +}; diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx index 1ea46800ae..33ee53dcef 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx @@ -45,8 +45,8 @@ export default memo( discoverJump({ item: { id: Date.now(), - name: i?.title || '', - url: i?.url ?? i?.description, + name: i?.name || '', + url: i?.url, }, }); }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 4b7cb992af..6f2fd81b25 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -12,8 +12,8 @@ import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import navigationService from 'utils/navigationService'; import { ArchivedTabEnum } from 'pages/Discover/types'; import NoDiscoverData from '../NoDiscoverData'; -import { DiscoverItem } from '@portkey-wallet/store/store-ca/cms/types'; import { useFocusEffect } from '@react-navigation/native'; +import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; export function DiscoverArchivedSection() { const discoverJump = useDiscoverJumpWithNetWork(); @@ -36,12 +36,12 @@ export function DiscoverArchivedSection() { }, [index]); const onClickJump = useCallback( - (i: DiscoverItem) => { + (i: ITabItem | IBookmarkItem) => { discoverJump({ item: { id: Date.now(), - name: i?.title || '', - url: i?.url ?? i?.description, + name: i?.name || '', + url: i?.url, }, }); }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 9b8e263612..12b495596f 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -23,7 +23,7 @@ export function DiscoverCmsListSection() { item: { id: Date.now(), name: i.title, - url: i?.url ?? i?.description, + url: i?.url, }, }); }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx index b330ac4592..a2f4f235da 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx @@ -9,14 +9,10 @@ import fonts from 'assets/theme/fonts'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import SearchRecordItem from '../SearchRecordItem'; -import { - addRecordsItem, - changeDrawerOpenStatus, - clearRecordsList, - createNewTab, -} from '@portkey-wallet/store/store-ca/discover/slice'; +import { clearRecordsList } from '@portkey-wallet/store/store-ca/discover/slice'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; export default function SearchRecordSection() { const { t } = useLanguage(); @@ -24,6 +20,7 @@ export default function SearchRecordSection() { const dispatch = useAppCommonDispatch(); const { networkType } = useCurrentNetworkInfo(); const { discoverMap } = useAppCASelector(state => state.discover); + const discoverJump = useDiscoverJumpWithNetWork(); const clearRecord = useCallback(() => { dispatch(clearRecordsList({ networkType })); @@ -34,6 +31,19 @@ export default function SearchRecordSection() { return recordsList.map(ele => ele).reverse(); }, [discoverMap, networkType]); + const onClickJump = useCallback( + (i: ITabItem) => { + discoverJump({ + item: { + id: Date.now(), + name: i?.name || '', + url: i?.url, + }, + }); + }, + [discoverJump], + ); + if (showRecordList?.length === 0) return null; return ( @@ -45,16 +55,7 @@ export default function SearchRecordSection() { {(showRecordList ?? []).map((item, index) => ( - { - const id = Date.now(); - dispatch(addRecordsItem({ ...item, networkType })); - dispatch(createNewTab({ id, url: item.url, name: '', networkType })); - dispatch(changeDrawerOpenStatus(true)); - }} - /> + onClickJump(item)} /> ))} ); From 1773b244285ed6e144caaa1a99b12677b8e30962 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 19 Jul 2023 16:13:15 +0800 Subject: [PATCH 410/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 4 ++-- .../Activity/ViewOnWebView/hooks/useHandleAchSell.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index a43725ac02..dd658eec06 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -110,7 +110,7 @@ export const useSellTransfer = () => { message: 'The waiting time is too long, it will be put on hold in the background.', }; } - if (signalrSellResult.status === 'TransferFailed') throw new Error('Transaction failed.'); + if (signalrSellResult.status !== 'Transferred') throw new Error('Transaction failed.'); } catch (error) { throw { code: 'TIMEOUT', @@ -124,6 +124,6 @@ export const useSellTransfer = () => { signalrSell.stop(); } }, - [isMainnet, status], + [isMainnet], ); }; diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index e7a493b5a4..dfd9797882 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -92,9 +92,13 @@ export const useHandleAchSell = () => { paymentSellTransfer, }); CommonToast.success('Transaction completed.'); - } catch (error) { + } catch (error: any) { console.log('error', error); - CommonToast.failError(error); + if (error?.code === 'TIMEOUT') { + CommonToast.warn(error?.message || 'The waiting time is too long, it will be put on hold in the background.'); + } else { + CommonToast.failError(error); + } } finally { Loading.hide(); } From 5dcbba391088a04c92a894d4d79b0aa73d0b0dd8 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 19 Jul 2023 16:17:02 +0800 Subject: [PATCH 411/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20change=20sty?= =?UTF-8?q?le?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 3 ++- .../TabsDrawer/TabsDrawerContent.tsx | 25 +++++++++++-------- .../Bookmark/components/BookmarkItem.tsx | 4 +-- .../DiscoverCmsListSection/index.tsx | 2 +- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index c3ca16978c..451088ee1e 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -6,6 +6,7 @@ import { IBrowserTab, useBrowser } from 'components/TabsDrawer/context'; import Progressbar, { IProgressbar } from 'components/Progressbar'; import HttpModal from './components/HttpModal'; import { getProtocolAndHost, isDangerousLink } from '@portkey-wallet/utils/dapp/browser'; +import { WebViewErrorEvent, WebViewNavigationEvent } from 'react-native-webview/lib/WebViewTypes'; type BrowserTabProps = { isHidden: boolean; @@ -42,7 +43,7 @@ const BrowserTab = forwardRef(function BrowserTab( }, [isHidden, options]); const onPageLoadEnd = useCallback( - ({ nativeEvent }: any) => { + ({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { if (!isDangerousLink(getProtocolAndHost(uri)) && !isApproved.current && autoApprove) { isApproved.current = true; webViewRef.current?.autoApprove(); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index a022faf944..08095d656f 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -95,15 +95,6 @@ const TabsDrawerContent: React.FC = () => { return null; }, [activeTabId, activeWebviewScreenShot, tabs]); - const onBrowserTabLoadEnd = useCallback( - ({ nativeEvent, autoApprove, item }: { nativeEvent: any; item: ITabItem; autoApprove?: boolean }) => { - if (autoApprove) dispatch(removeAutoApproveItem(item.id)); - checkAndUpDateRecordItemName({ id: item.id, name: nativeEvent.title }); - checkAndUpDateTabItemName({ id: item.id, name: nativeEvent.title }); - }, - [checkAndUpDateRecordItemName, checkAndUpDateTabItemName, dispatch], - ); - const TabsDom = useMemo(() => { return tabs?.map(ele => { const isHidden = activeTabId !== ele.id; @@ -117,11 +108,23 @@ const TabsDrawerContent: React.FC = () => { uri={ele.url} isHidden={isHidden} autoApprove={autoApprove} - onLoadEnd={nativeEvent => onBrowserTabLoadEnd({ nativeEvent, item: ele, autoApprove })} + onLoadEnd={nativeEvent => { + if (autoApprove) dispatch(removeAutoApproveItem(ele.id)); + checkAndUpDateRecordItemName({ id: ele.id, name: nativeEvent.title }); + checkAndUpDateTabItemName({ id: ele.id, name: nativeEvent.title }); + }} /> ); }); - }, [activeTabId, autoApproveMap, initializedList, onBrowserTabLoadEnd, tabs]); + }, [ + activeTabId, + autoApproveMap, + checkAndUpDateRecordItemName, + checkAndUpDateTabItemName, + dispatch, + initializedList, + tabs, + ]); const value = useMemo( () => ({ diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 5c4c08327a..97c2025bef 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -28,7 +28,9 @@ export default memo( const { item, onDelete } = props; const swipeableRef = useRef(null); const [{ isEdit }] = useBookmark(); + const discoverJump = useDiscoverJumpWithNetWork(); const preIsEdit = usePrevious(isEdit); + useEffect(() => { if (!isEdit && isEdit !== preIsEdit) swipeableRef.current?.close(); }, [preIsEdit, isEdit]); @@ -36,7 +38,6 @@ export default memo( const listener = myEvents.bookmark.closeSwipeable.addListener(() => swipeableRef.current?.close()); return () => listener.remove(); }); - const discoverJump = useDiscoverJumpWithNetWork(); const onClickJump = useCallback(() => { if (isEdit) return; @@ -134,7 +135,6 @@ const styles = StyleSheet.create({ itemRow: { padding: pTd(12), height: pTd(72), - // marginVertical: 10, }, deleteIconWrap: { marginRight: pTd(16), diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 12b495596f..0dfa34d7ce 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -42,7 +42,7 @@ export function DiscoverCmsListSection() { - {item.description} + {item?.description} From 07b53dd40e45d405a201387d8ec9efa400187a46 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Wed, 19 Jul 2023 16:41:28 +0800 Subject: [PATCH 412/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 111 +++++++++--------- .../ViewOnWebView/hooks/useHandleAchSell.ts | 2 + 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index dd658eec06..7842863d2c 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -52,77 +52,76 @@ export const useSellTransfer = () => { let signalrAchTxRemove: (() => void) | undefined; let signalrOrderRemove: (() => void) | undefined; + const clientId = randomId(); try { - const clientId = randomId(); await signalrSell.doOpen({ url: `${request.defaultConfig.baseURL}/ca`, clientId, }); + } catch (error) { + throw new Error('Transaction failed.'); + } + + const timerPromise = new Promise<'timeout'>(resolve => + setTimeout(() => { + resolve('timeout'); + }, SELL_SOCKET_TIMEOUT), + ); - const timerPromise = new Promise<'timeout'>(resolve => - setTimeout(() => { - resolve('timeout'); - }, SELL_SOCKET_TIMEOUT), - ); - - const signalrSellPromise = new Promise(resolve => { - const { remove: removeAchTx } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, async data => { - if (data === null) { - throw new Error('Transaction failed.'); - } - - try { - status.current = STAGE.TRANSACTION; - const result = await paymentSellTransfer(data); - await request.payment.sendSellTransaction({ - params: { - merchantName: ACH_MERCHANT_NAME, - orderId, - rawTransaction: result.rawTransaction, - signature: result.signature, - publicKey: result.publicKey, - }, - }); - } catch (e) { - throw { - code: 'NO_TX_HASH', - message: 'Transaction failed. Please contact the team for assistance.', - }; - } - - const { remove: removeRes } = signalrSell.onRequestOrderTransferred({ clientId, orderId }, async data => { - status.current = STAGE.ORDER; - resolve(data); + const signalrSellPromise = new Promise(resolve => { + const { remove: removeAchTx } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, async data => { + if (data === null) { + throw new Error('Transaction failed.'); + } + + try { + status.current = STAGE.TRANSACTION; + const result = await paymentSellTransfer(data); + await request.payment.sendSellTransaction({ + params: { + merchantName: ACH_MERCHANT_NAME, + orderId, + rawTransaction: result.rawTransaction, + signature: result.signature, + publicKey: result.publicKey, + }, }); - signalrOrderRemove = removeRes; - signalrSell.requestOrderTransferred(clientId, orderId); + } catch (e) { + throw { + code: 'NO_TX_HASH', + message: 'Transaction failed. Please contact the team for assistance.', + }; + } + + const { remove: removeRes } = signalrSell.onRequestOrderTransferred({ clientId, orderId }, async data => { + status.current = STAGE.ORDER; + resolve(data); }); - signalrAchTxRemove = removeAchTx; - signalrSell.requestAchTxAddress(clientId, orderId); + signalrOrderRemove = removeRes; + signalrSell.requestOrderTransferred(clientId, orderId); }); - const signalrSellResult = await Promise.race([timerPromise, signalrSellPromise]); - if (signalrSellResult === null) throw new Error('Transaction failed.'); - if (signalrSellResult === 'timeout') { - if (status.current === STAGE.ACHTXADS) throw new Error('Transaction failed.'); - throw { - code: 'TIMEOUT', - message: 'The waiting time is too long, it will be put on hold in the background.', - }; - } - if (signalrSellResult.status !== 'Transferred') throw new Error('Transaction failed.'); - } catch (error) { + signalrAchTxRemove = removeAchTx; + signalrSell.requestAchTxAddress(clientId, orderId); + }); + + const signalrSellResult = await Promise.race([timerPromise, signalrSellPromise]); + + if (signalrSellResult === null) throw new Error('Transaction failed.'); + if (signalrSellResult === 'timeout') { + if (status.current === STAGE.ACHTXADS) throw new Error('Transaction failed.'); throw { code: 'TIMEOUT', message: 'The waiting time is too long, it will be put on hold in the background.', }; - } finally { - signalrAchTxRemove?.(); - signalrAchTxRemove = undefined; - signalrOrderRemove?.(); - signalrOrderRemove = undefined; - signalrSell.stop(); } + if (signalrSellResult.status !== 'Transferred') throw new Error('Transaction failed.'); + + signalrAchTxRemove?.(); + signalrAchTxRemove = undefined; + signalrOrderRemove?.(); + signalrOrderRemove = undefined; + signalrSell.stop(); }, [isMainnet], ); diff --git a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts index dfd9797882..2ca4d003cc 100644 --- a/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts +++ b/packages/mobile-app-did/js/pages/Activity/ViewOnWebView/hooks/useHandleAchSell.ts @@ -84,6 +84,7 @@ export const useHandleAchSell = () => { return useCallback( async (orderId: string) => { + console.log('sell Transfer, Start', Date.now()); try { Loading.show({ text: 'Payment is being processed and may take around 10 seconds to complete.' }); await sellTransfer({ @@ -102,6 +103,7 @@ export const useHandleAchSell = () => { } finally { Loading.hide(); } + console.log('sell Transfer, End', Date.now()); }, [paymentSellTransfer, sellTransfer], ); From 4e60ce427bf562c633be2a842d476679182f3f73 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 19 Jul 2023 17:05:02 +0800 Subject: [PATCH 413/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20judge=20error=20?= =?UTF-8?q?type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/Buy/hooks/useHandleAchSell.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts index 9a98dc6a47..8434a01a89 100644 --- a/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts +++ b/packages/web-extension-did/app/web/pages/Buy/hooks/useHandleAchSell.ts @@ -86,7 +86,11 @@ export const useHandleAchSell = () => { }); message.success('Transaction completed.'); } catch (error: any) { - message.error(error.message); + if (error?.code === 'TIMEOUT') { + message.warn(error?.message || 'The waiting time is too long, it will be put on hold in the background.'); + } else { + message.error(error.message); + } } finally { setLoading(false); } From 8059c4bcf513b14b4e3f7489a9442541cda6cf82 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 19 Jul 2023 20:01:30 +0800 Subject: [PATCH 414/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20elf=20symbol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/AddressInput/index.tsx | 4 +- .../js/components/SendButton/index.tsx | 2 +- .../components/WalletInfoOverlay/index.tsx | 7 +-- .../js/components/TokenListItem/index.tsx | 5 +- .../dapp/components/ConnectOverlay/index.tsx | 7 +-- .../components/TransactionOverlay/index.tsx | 48 +++++++++++++------ .../pages/Activity/ActivityDetail/index.tsx | 10 ++-- .../js/pages/Buy/BuyPreview/index.tsx | 4 +- .../pages/DashBoard/AssetsOverlay/index.tsx | 14 ++---- .../pages/My/Contacts/ContactEdit/index.tsx | 4 +- .../mobile-app-did/js/pages/Receive/index.tsx | 5 +- .../js/pages/Send/AmountToken/index.tsx | 7 +-- .../js/pages/Send/SendHome/index.tsx | 27 ++++++----- .../js/pages/Send/SendPreview/index.tsx | 33 ++++++++----- .../js/pages/Token/TokenDetail/index.tsx | 11 +++-- .../Token/components/TokenItem/index.tsx | 6 ++- packages/types/types-ca/routeParams.ts | 3 +- packages/types/types-ca/send.ts | 1 + 18 files changed, 115 insertions(+), 83 deletions(-) create mode 100644 packages/types/types-ca/send.ts diff --git a/packages/mobile-app-did/js/components/AddressInput/index.tsx b/packages/mobile-app-did/js/components/AddressInput/index.tsx index 12e537cefd..d1d6c558f6 100644 --- a/packages/mobile-app-did/js/components/AddressInput/index.tsx +++ b/packages/mobile-app-did/js/components/AddressInput/index.tsx @@ -3,14 +3,13 @@ import { Input, InputProps } from '@rneui/themed'; import { Text, View } from 'react-native'; import { generalStyles } from './style'; import { TextM } from 'components/CommonText'; -import { defaultColors } from 'assets/theme'; type AelfInputWithAffixProps = InputProps & { affix?: [string, string]; // prefix and suffix }; const AelfInputWithAffix: React.FC = props => { - const { placeholder = 'send', errorMessage = '', affix = ['ELF', 'AELF'] } = props; + const { placeholder = 'send', errorMessage = '', affix = ['', ''] } = props; return ( @@ -19,7 +18,6 @@ const AelfInputWithAffix: React.FC = props => { {affix[0]} { } as unknown as IToSendHomeParamsType); // if (currentTokenList.length === 1) // return navigationService.navigate('SendHome', { tokenItem: currentTokenList?.[0] }); - AssetsOverlay.showAssetList({}); + AssetsOverlay.showAssetList(); }}> diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx index e2c2ffdba8..b6017f16bf 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx @@ -12,15 +12,16 @@ import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; import { ChainId } from '@portkey-wallet/types'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; const MyWalletModal = () => { const { t } = useLanguage(); const caInfo = useCurrentCaInfo(); const { walletName, currentNetwork } = useWallet(); + const defaultToken = useDefaultToken(); const { accountToken: { accountTokenList }, @@ -34,12 +35,12 @@ const MyWalletModal = () => { ? { chaiId: key, caAddress: info.caAddress, - ...accountTokenList.find(token => token.chainId === key && token.symbol === ELF_SYMBOL), + ...accountTokenList.find(token => token.chainId === key && token.symbol === defaultToken.symbol), } : undefined; }) .filter(item => !!item); - }, [accountTokenList, caInfo]); + }, [accountTokenList, caInfo, defaultToken.symbol]); return ( diff --git a/packages/mobile-app-did/js/components/TokenListItem/index.tsx b/packages/mobile-app-did/js/components/TokenListItem/index.tsx index b63f4bd202..d9da22376b 100644 --- a/packages/mobile-app-did/js/components/TokenListItem/index.tsx +++ b/packages/mobile-app-did/js/components/TokenListItem/index.tsx @@ -1,4 +1,3 @@ -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import { defaultColors } from 'assets/theme'; @@ -12,6 +11,7 @@ import { formatChainInfoToShow } from '@portkey-wallet/utils'; import { pTd } from 'utils/unit'; import { useIsTestnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useGetCurrentAccountTokenPrice, useIsTokenHasPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; interface TokenListItemType { noBalanceShow?: boolean; item?: any; @@ -21,6 +21,7 @@ interface TokenListItemType { const TokenListItem: React.FC = props => { const { noBalanceShow = false, onPress, item } = props; const { currentNetwork } = useWallet(); + const defaultToken = useDefaultToken(); const isTokenHasPrice = useIsTokenHasPrice(item?.symbol); const isTestnet = useIsTestnet(); @@ -35,7 +36,7 @@ const TokenListItem: React.FC = props => { style={itemStyle.left} title={item?.symbol} avatarSize={pTd(48)} - svgName={item?.symbol === ELF_SYMBOL ? 'elf-icon' : undefined} + svgName={item?.symbol === defaultToken.symbol ? 'elf-icon' : undefined} imageUrl={symbolImages[item?.symbol]} /> diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 4c226c6a3c..1aa543fc82 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -12,7 +12,6 @@ import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; import { ChainId } from '@portkey-wallet/types'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; @@ -20,6 +19,7 @@ import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { useGStyles } from 'assets/theme/useGStyles'; import { CommonButtonProps } from 'components/CommonButton'; import DappInfoSection from '../DappInfoSection'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; type ConnectModalType = { dappInfo: DappStoreItem; @@ -30,6 +30,7 @@ type ConnectModalType = { const ConnectModal = (props: ConnectModalType) => { const { dappInfo, onReject, onApprove } = props; const { t } = useLanguage(); + const defaultToken = useDefaultToken(); const caInfo = useCurrentCaInfo(); const { walletName, currentNetwork } = useWallet(); const gStyles = useGStyles(); @@ -46,12 +47,12 @@ const ConnectModal = (props: ConnectModalType) => { list.push({ chaiId: key, caAddress: info.caAddress, - ...accountTokenList.find(token => token.chainId === key && token.symbol === ELF_SYMBOL), + ...accountTokenList.find(token => token.chainId === key && token.symbol === defaultToken.symbol), }); } }); return list; - }, [accountTokenList, caInfo]); + }, [accountTokenList, caInfo, defaultToken.symbol]); const buttonList = useMemo( () => [ diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 9751b14996..d1eb49fe03 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -14,7 +14,7 @@ import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { CommonButtonProps } from 'components/CommonButton'; import { SendTransactionParams } from '@portkey/provider-types'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; -import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentChain, useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useAmountInUsdShow, useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import { ZERO } from '@portkey-wallet/constants/misc'; import { usePin } from 'hooks/store'; @@ -44,6 +44,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { const { dappInfo, transactionInfo, onReject, onSign } = props; const { t } = useLanguage(); const isMainnet = useIsMainnet(); + const defaultToken = useDefaultToken(); const pin = usePin(); const { walletName } = useWallet(); @@ -93,18 +94,18 @@ const ConnectModal = (props: TransactionModalPropsType) => { const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { const value = amountInUsdShow(amount, decimals, symbol); - if (symbol === 'ELF') { + if (symbol === defaultToken.symbol) { return value === '$ 0' ? '<$ 0.01' : value; } else { return value; } }, - [amountInUsdShow], + [amountInUsdShow, defaultToken.symbol], ); const transferContent = useMemo(() => { const { symbol, amount } = transactionInfo?.params?.paramsOption || {}; - const decimals = symbol === 'ELF' ? 8 : tokenDecimal; + const decimals = symbol === defaultToken.symbol ? defaultToken.decimals : tokenDecimal; return ( <> @@ -171,7 +172,11 @@ const ConnectModal = (props: TransactionModalPropsType) => { /> )} - {isFetchingFee ? 'ELF' : `${formatAmountShow(divDecimals(fee, ELF_DECIMAL), 8)} ELF`} + {isFetchingFee + ? defaultToken.symbol + : `${formatAmountShow(divDecimals(fee, defaultToken.decimals), defaultToken.decimals)} ${ + defaultToken.symbol + }`} @@ -179,7 +184,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { - {fee === '0' ? '$ 0' : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).toNumber(), 0, 'ELF')} + {fee === '0' + ? '$ 0' + : formatAmountInUsdShow(divDecimals(fee, defaultToken.decimals).toNumber(), 0, defaultToken.symbol)} )} @@ -189,7 +196,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { {isTransfer && ( <> - {symbol === 'ELF' ? ( + {symbol === defaultToken.symbol ? ( {t('Total')} @@ -205,7 +212,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { )} {isFetchingFee - ? 'ELF' + ? defaultToken.symbol : `${formatAmountShow(divDecimals(ZERO.plus(amount).plus(fee), decimals), 8)} ${symbol}`} @@ -237,7 +244,11 @@ const ConnectModal = (props: TransactionModalPropsType) => { /> )} - {isFetchingFee ? 'ELF' : `${formatAmountShow(divDecimals(ZERO.plus(fee), ELF_DECIMAL), 8)} ELF`} + {isFetchingFee + ? defaultToken.symbol + : `${formatAmountShow(divDecimals(ZERO.plus(fee), defaultToken.decimals), 8)} ${ + defaultToken.symbol + }`}
    @@ -247,7 +258,11 @@ const ConnectModal = (props: TransactionModalPropsType) => { {fee === '0' ? '$ 0' - : formatAmountInUsdShow(divDecimals(fee, ELF_DECIMAL).toNumber(), 0, 'ELF')} + : formatAmountInUsdShow( + divDecimals(fee, defaultToken.decimals).toNumber(), + 0, + defaultToken.symbol, + )}
    )} @@ -286,6 +301,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { ); }, [ + defaultToken.decimals, + defaultToken.symbol, errorText, fee, formatAmountInUsdShow, @@ -336,9 +353,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, }); - if (!TransactionFee && !TransactionFee?.ELF) setErrorText(ErrorText.ESTIMATE_ERROR); + if (!TransactionFee && !TransactionFee?.[defaultToken.symbol]) setErrorText(ErrorText.ESTIMATE_ERROR); - setFee(TransactionFee?.ELF || '0'); + setFee(TransactionFee?.[defaultToken.symbol] || '0'); setIsFetchingFee(false); } catch (e) { @@ -349,6 +366,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, [ chainInfo, checkManagerSyncState, + defaultToken.symbol, isCAContract, pin, transactionInfo.chainId, @@ -388,12 +406,12 @@ const ConnectModal = (props: TransactionModalPropsType) => { useEffect(() => { const symbol = transactionInfo?.params?.paramsOption?.symbol; if (!symbol) return; - if (symbol === 'ELF') { + if (symbol === defaultToken.symbol) { getTokenPrice(symbol); } else { - getTokensPrice([symbol, 'ELF']); + getTokensPrice([symbol, defaultToken.symbol]); } - }, [getTokenPrice, getTokensPrice, transactionInfo?.params?.paramsOption?.symbol]); + }, [defaultToken.symbol, getTokenPrice, getTokensPrice, transactionInfo?.params?.paramsOption?.symbol]); return ( diff --git a/packages/mobile-app-did/js/pages/Activity/ActivityDetail/index.tsx b/packages/mobile-app-did/js/pages/Activity/ActivityDetail/index.tsx index ac67d77cbd..f71411b36d 100644 --- a/packages/mobile-app-did/js/pages/Activity/ActivityDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/Activity/ActivityDetail/index.tsx @@ -1,5 +1,5 @@ import { ELF_DECIMAL, TransactionTypes } from '@portkey-wallet/constants/constants-ca/activity'; -import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentChain, useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { useCaAddresses, useCurrentWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; import { fetchActivity } from '@portkey-wallet/store/store-ca/activity/api'; @@ -31,6 +31,8 @@ import { useIsTokenHasPrice, useGetCurrentAccountTokenPrice } from '@portkey-wal const ActivityDetail = () => { const { t } = useLanguage(); + const defaultToken = useDefaultToken(); + const activityItemFromRoute = useRouterParams(); const { transactionId = '', blockHash = '', isReceived: isReceivedParams } = activityItemFromRoute; const caAddresses = useCaAddresses(); @@ -131,7 +133,7 @@ const ActivityDetail = () => { const feeUI = useMemo(() => { const transactionFees = activityItem?.transactionFees?.length === 0 - ? [{ fee: 0, symbol: 'ELF', feeInUsd: 0 }] + ? [{ fee: 0, symbol: defaultToken.symbol, feeInUsd: 0 }] : activityItem?.transactionFees || []; return ( @@ -140,7 +142,7 @@ const ActivityDetail = () => { {t('Transaction Fee')} {activityItem?.isDelegated ? ( - {`0 ELF`} + {`0 ${defaultToken.symbol}`} {!isTestnet && ( {`$ ${formatAmountShow(0, 2)}`} )} @@ -165,7 +167,7 @@ const ActivityDetail = () => {
    ); - }, [activityItem?.isDelegated, activityItem?.transactionFees, isTestnet, t]); + }, [activityItem?.isDelegated, activityItem?.transactionFees, defaultToken.symbol, isTestnet, t]); useEffectOnce(() => { getTokenPrice(activityItem?.symbol); diff --git a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx index 63fd770547..ca65fe4abe 100644 --- a/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Buy/BuyPreview/index.tsx @@ -29,6 +29,7 @@ import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { PaymentTypeEnum } from '@portkey-wallet/types/types-ca/payment'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; interface RouterParams { type?: PaymentTypeEnum; @@ -50,6 +51,7 @@ export default function BuyPreview() { } = useRouterParams(); const { t } = useLanguage(); + const defaultToken = useDefaultToken(); const { rate, receiveAmount } = useReceive(type, amount || '', fiat, token, receiveAmountProps, rateProps); const isBuy = useMemo(() => type === PaymentTypeEnum.BUY, [type]); const apiUrl = useCurrentApiUrl(); @@ -157,7 +159,7 @@ export default function BuyPreview() { return ( diff --git a/packages/mobile-app-did/js/pages/DashBoard/AssetsOverlay/index.tsx b/packages/mobile-app-did/js/pages/DashBoard/AssetsOverlay/index.tsx index 1e42087b3d..037133b7c8 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/AssetsOverlay/index.tsx +++ b/packages/mobile-app-did/js/pages/DashBoard/AssetsOverlay/index.tsx @@ -4,7 +4,6 @@ import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native import { TextL, TextS } from 'components/CommonText'; import { ModalBody } from 'components/ModalBody'; import CommonInput from 'components/CommonInput'; -import { AccountType } from '@portkey-wallet/types/wallet'; import { pTd } from 'utils/unit'; import { useLanguage } from 'i18n/hooks'; import useDebounce from 'hooks/useDebounce'; @@ -26,12 +25,6 @@ import myEvents from 'utils/deviceEvent'; import useEffectOnce from 'hooks/useEffectOnce'; import { useGetCurrentAccountTokenPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; -type onFinishSelectTokenType = (tokenItem: any) => void; -type TokenListProps = { - account?: AccountType; - onFinishSelectToken?: onFinishSelectTokenType; -}; - const AssetItem = (props: { symbol: string; onPress: (item: any) => void; item: IAssetItemType }) => { const { symbol, onPress, item } = props; @@ -84,7 +77,7 @@ const INIT_PAGE_INFO = { isLoading: false, }; -const AssetList = ({ account }: TokenListProps) => { +const AssetList = () => { const { t } = useLanguage(); const caAddresses = useCaAddresses(); const caAddressInfos = useCaAddressInfoList(); @@ -153,7 +146,6 @@ const AssetList = ({ account }: TokenListProps) => { item={item} onPress={() => { OverlayModal.hide(); - // onFinishSelectToken?.(item); const routeParams = { sendType: item?.nftInfo ? 'nft' : 'token', assetInfo: item?.nftInfo @@ -216,8 +208,8 @@ const AssetList = ({ account }: TokenListProps) => { ); }; -export const showAssetList = (props: TokenListProps) => { - OverlayModal.show(, { +export const showAssetList = () => { + OverlayModal.show(, { position: 'bottom', autoKeyboardInsets: false, enabledNestScrollView: true, diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx index 888296ab5e..56aee21986 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/index.tsx @@ -31,6 +31,7 @@ import Loading from 'components/Loading'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { formatChainInfoToShow } from '@portkey-wallet/utils'; import myEvents from 'utils/deviceEvent'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; type RouterParams = { contact?: ContactItemType; @@ -57,6 +58,7 @@ const initEditContact: EditContactType = { const ContactEdit: React.FC = () => { const { contact, addressList } = useRouterParams(); + const defaultToken = useDefaultToken(); const { t } = useLanguage(); const addContactApi = useAddContact(); const editContactApi = useEditContact(); @@ -345,7 +347,7 @@ const ContactEdit: React.FC = () => { }) } addressValue={addressItem.address} - affix={['ELF', addressItem.chainId]} + affix={[defaultToken.symbol, addressItem.chainId]} onAddressChange={onAddressChange} /> ))} diff --git a/packages/mobile-app-did/js/pages/Receive/index.tsx b/packages/mobile-app-did/js/pages/Receive/index.tsx index 8e67bc2bab..e9a435dc02 100644 --- a/packages/mobile-app-did/js/pages/Receive/index.tsx +++ b/packages/mobile-app-did/js/pages/Receive/index.tsx @@ -15,13 +15,14 @@ import GStyles from 'assets/theme/GStyles'; import { TokenItemShowType } from '@portkey-wallet/types/types-ca/token'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import { formatChainInfoToShow } from '@portkey-wallet/utils'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; export default function Receive() { const { t } = useLanguage(); const { currentNetwork } = useWallet(); + const defaultToken = useDefaultToken(); const tokenItem = useRouterParams(); const { chainId, symbol } = tokenItem; @@ -40,7 +41,7 @@ export default function Receive() { style={styles.svgStyle} title={symbol} avatarSize={pTd(32)} - svgName={symbol === ELF_SYMBOL ? 'elf-icon' : undefined} + svgName={symbol === defaultToken.symbol ? 'elf-icon' : undefined} imageUrl={symbolImages?.[symbol] || ''} /> diff --git a/packages/mobile-app-did/js/pages/Send/AmountToken/index.tsx b/packages/mobile-app-did/js/pages/Send/AmountToken/index.tsx index be37560ed0..0c257536fe 100644 --- a/packages/mobile-app-did/js/pages/Send/AmountToken/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/AmountToken/index.tsx @@ -12,13 +12,13 @@ import CommonAvatar from 'components/CommonAvatar'; import { IToSendAssetParamsType } from '@portkey-wallet/types/types-ca/routeParams'; import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import { FontStyles } from 'assets/theme/styles'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { useFocusEffect } from '@react-navigation/native'; import { TextM, TextS } from 'components/CommonText'; import { useIsTestnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useGetCurrentAccountTokenPrice, useIsTokenHasPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import useEffectOnce from 'hooks/useEffectOnce'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; interface AmountTokenProps { onPressMax: () => void; @@ -38,6 +38,7 @@ export default function AmountToken({ selectedToken, }: AmountTokenProps) { const { t } = useLanguage(); + const defaultToken = useDefaultToken(); const iptRef = useRef(); const isTestNet = useIsTestnet(); const isTokenHasPrice = useIsTokenHasPrice(selectedToken?.symbol); @@ -73,10 +74,10 @@ export default function AmountToken({ - {selectedToken.symbol === ELF_SYMBOL ? ( + {selectedToken.symbol === defaultToken.symbol ? ( diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index ff13e9d6ee..d7610d754c 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -22,7 +22,7 @@ import NFTInfo from '../NFTInfo'; import CommonButton from 'components/CommonButton'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { useCurrentChain, useIsValidSuffix } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentChain, useDefaultToken, useIsValidSuffix } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { getManagerAccount } from 'utils/redux'; import { usePin } from 'hooks/store'; import { divDecimals, divDecimalsStr, timesDecimals, unitConverter } from '@portkey-wallet/utils/converter'; @@ -33,7 +33,6 @@ import { getELFChainBalance } from '@portkey-wallet/utils/balance'; import { BGStyles } from 'assets/theme/styles'; import { RouteProp, useRoute } from '@react-navigation/native'; import Loading from 'components/Loading'; -import { DEFAULT_DECIMAL } from '@portkey-wallet/constants/constants-ca/activity'; import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import { @@ -43,18 +42,16 @@ import { AddressErrorArray, } from '@portkey-wallet/constants/constants-ca/send'; import { getAddressChainId, isSameAddresses } from '@portkey-wallet/utils'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { useCheckManagerSyncState } from 'hooks/wallet'; const SendHome: React.FC = () => { - const { t } = useLanguage(); - useFetchTxFee(); - - const isValidChainId = useIsValidSuffix(); - const { params: { sendType = 'token', toInfo, assetInfo }, } = useRoute>(); + const { t } = useLanguage(); + useFetchTxFee(); + const isValidChainId = useIsValidSuffix(); + const defaultToken = useDefaultToken(); const wallet = useCurrentWalletInfo(); const chainInfo = useCurrentChain(assetInfo?.chainId); @@ -132,16 +129,17 @@ const SendHome: React.FC = () => { console.log( '====TransactionFee======', TransactionFee, - unitConverter(ZERO.plus(TransactionFee?.ELF || '0').div('1e8')), + unitConverter(ZERO.plus(TransactionFee?.[defaultToken.decimals] || '0').div(`1e${defaultToken.decimals}`)), ); if (!TransactionFee) throw { code: 500, message: 'no enough fee' }; - return unitConverter(ZERO.plus(TransactionFee.ELF).div('1e8')); + return unitConverter(ZERO.plus(TransactionFee?.[defaultToken.decimals]).div(`1e${defaultToken.decimals}`)); }, [ chainInfo, debounceSendNumber, + defaultToken.decimals, pin, selectedAssets.decimals, selectedAssets.symbol, @@ -163,7 +161,7 @@ const SendHome: React.FC = () => { if (divDecimals(selectedAssets.balance, selectedAssets.decimals).isEqualTo(0)) return setSendNumber('0'); // other tokens - if (selectedAssets.symbol !== ELF_SYMBOL) + if (selectedAssets.symbol !== defaultToken.symbol) return setSendNumber(divDecimals(selectedAssets.balance, selectedAssets.decimals || '0').toString()); // elf <= maxFee @@ -197,6 +195,7 @@ const SendHome: React.FC = () => { }, [ chainInfo?.chainId, checkManagerSyncState, + defaultToken.symbol, getTransactionFee, maxFee, selectedAssets.balance, @@ -359,14 +358,14 @@ const SendHome: React.FC = () => { // input check if (sendType === 'token') { // token - if (assetInfo.symbol === 'ELF') { + if (assetInfo.symbol === defaultToken.symbol) { // ELF if (sendBigNumber.isGreaterThan(assetBalanceBigNumber)) { setErrorMessage([TransactionError.TOKEN_NOT_ENOUGH]); return { status: false }; } - if (isCross && sendBigNumber.isLessThanOrEqualTo(timesDecimals(crossFee, DEFAULT_DECIMAL))) { + if (isCross && sendBigNumber.isLessThanOrEqualTo(timesDecimals(crossFee, defaultToken.decimals))) { setErrorMessage([TransactionError.CROSS_NOT_ENOUGH]); return { status: false }; } @@ -408,6 +407,8 @@ const SendHome: React.FC = () => { chainInfo?.chainId, checkManagerSyncState, crossFee, + defaultToken.decimals, + defaultToken.symbol, getTransactionFee, selectedAssets.balance, selectedAssets.decimals, diff --git a/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx b/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx index 486bd21acf..f0e49f976b 100644 --- a/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendPreview/index.tsx @@ -14,7 +14,7 @@ import GStyles from 'assets/theme/GStyles'; import fonts from 'assets/theme/fonts'; import { Image } from '@rneui/base'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; -import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentChain, useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; import { usePin } from 'hooks/store'; import { useCaAddressInfoList, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { getManagerAccount } from 'utils/redux'; @@ -44,13 +44,13 @@ import { sleep } from '@portkey-wallet/utils'; import { FontStyles } from 'assets/theme/styles'; import { ChainId } from '@portkey-wallet/types'; import { useGetCurrentAccountTokenPrice, useIsTokenHasPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import useEffectOnce from 'hooks/useEffectOnce'; import { useFetchTxFee, useGetTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; const SendHome: React.FC = () => { const { t } = useLanguage(); const isTestnet = useIsTestnet(); + const defaultToken = useDefaultToken(); const { sendType, assetInfo, toInfo, transactionFee, sendNumber } = useRouterParams(); @@ -254,7 +254,7 @@ const SendHome: React.FC = () => { useEffectOnce(() => { getTokenPrice(assetInfo.symbol); - getTokenPrice(ELF_SYMBOL); + getTokenPrice(defaultToken.symbol); }); return ( @@ -334,20 +334,23 @@ const SendHome: React.FC = () => { {t('Transaction Fee')} - {`${transactionFee} ${'ELF'}`} + {`${transactionFee} ${defaultToken.symbol}`} {!isTestnet && ( {`$ ${unitConverter( - ZERO.plus(transactionFee).multipliedBy(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(transactionFee).multipliedBy(tokenPriceObject[defaultToken.symbol]), )}`} )} - {isCrossChainTransfer && assetInfo.symbol === 'ELF' && } - {isCrossChainTransfer && assetInfo.symbol === 'ELF' && ( + {isCrossChainTransfer && assetInfo.symbol === defaultToken.symbol && ( + + )} + {isCrossChainTransfer && assetInfo.symbol === defaultToken.symbol && ( @@ -356,11 +359,11 @@ const SendHome: React.FC = () => { {`${unitConverter( crossDefaultFee, - )} ELF`} + )} ${defaultToken.symbol}`} {!isTestnet ? ( {`$ ${unitConverter( - ZERO.plus(crossDefaultFee).multipliedBy(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(crossDefaultFee).multipliedBy(tokenPriceObject[defaultToken.symbol]), )}`} ) : ( @@ -369,8 +372,10 @@ const SendHome: React.FC = () => { )} - {isCrossChainTransfer && assetInfo.symbol === 'ELF' && } - {isCrossChainTransfer && assetInfo.symbol === 'ELF' && ( + {isCrossChainTransfer && assetInfo.symbol === defaultToken.symbol && ( + + )} + {isCrossChainTransfer && assetInfo.symbol === defaultToken.symbol && ( @@ -381,14 +386,16 @@ const SendHome: React.FC = () => { {ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossDefaultFee)) ? '0' : formatAmountShow(ZERO.plus(sendNumber).minus(ZERO.plus(crossDefaultFee)))}{' '} - {'ELF'} + {defaultToken.symbol} {!isTestnet ? ( {`$ ${ ZERO.plus(sendNumber).isLessThanOrEqualTo(ZERO.plus(crossDefaultFee)) ? '0' : formatAmountShow( - ZERO.plus(sendNumber).minus(ZERO.plus(crossDefaultFee)).times(tokenPriceObject[ELF_SYMBOL]), + ZERO.plus(sendNumber) + .minus(ZERO.plus(crossDefaultFee)) + .times(tokenPriceObject[defaultToken.symbol]), 2, ) }`} diff --git a/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx b/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx index 58f4d1fd3f..33417ff910 100644 --- a/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/TokenDetail/index.tsx @@ -28,11 +28,11 @@ import fonts from 'assets/theme/fonts'; import { fetchTokenListAsync } from '@portkey-wallet/store/store-ca/assets/slice'; import { formatChainInfoToShow } from '@portkey-wallet/utils'; import BuyButton from 'components/BuyButton'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useGetCurrentAccountTokenPrice, useIsTokenHasPrice } from '@portkey-wallet/hooks/hooks-ca/useTokensPrice'; import FaucetButton from 'components/FaucetButton'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; interface RouterParams { tokenInfo: TokenItemShowType; @@ -47,6 +47,7 @@ const INIT_PAGE_INFO = { const TokenDetail: React.FC = () => { const { t } = useLanguage(); const { tokenInfo } = useRouterParams(); + const defaultToken = useDefaultToken(); const isMainnet = useIsMainnet(); const currentWallet = useCurrentWallet(); @@ -127,13 +128,13 @@ const TokenDetail: React.FC = () => { }); const isBuyButtonShow = useMemo( - () => tokenInfo.symbol === ELF_SYMBOL && tokenInfo.chainId === 'AELF' && isBuyButtonShowStore, - [isBuyButtonShowStore, tokenInfo.chainId, tokenInfo.symbol], + () => tokenInfo.symbol === defaultToken.symbol && tokenInfo.chainId === 'AELF' && isBuyButtonShowStore, + [defaultToken.symbol, isBuyButtonShowStore, tokenInfo.chainId, tokenInfo.symbol], ); const isFaucetButtonShow = useMemo( - () => !isMainnet && tokenInfo.symbol === ELF_SYMBOL && tokenInfo.chainId === 'AELF', - [isMainnet, tokenInfo.chainId, tokenInfo.symbol], + () => !isMainnet && tokenInfo.symbol === defaultToken.symbol && tokenInfo.chainId === 'AELF', + [defaultToken.symbol, isMainnet, tokenInfo.chainId, tokenInfo.symbol], ); return ( diff --git a/packages/mobile-app-did/js/pages/Token/components/TokenItem/index.tsx b/packages/mobile-app-did/js/pages/Token/components/TokenItem/index.tsx index a1e89e6169..5135d44ae5 100644 --- a/packages/mobile-app-did/js/pages/Token/components/TokenItem/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/components/TokenItem/index.tsx @@ -10,8 +10,8 @@ import CommonSwitch from 'components/CommonSwitch'; import CommonAvatar from 'components/CommonAvatar'; import { formatChainInfoToShow } from '@portkey-wallet/utils'; import { FontStyles } from 'assets/theme/styles'; -import { ELF_SYMBOL } from '@portkey-wallet/constants/constants-ca/assets'; import { NetworkType } from '@portkey-wallet/types'; +import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; type TokenItemProps = { networkType: NetworkType; @@ -21,13 +21,15 @@ type TokenItemProps = { const TokenItem = ({ networkType, item, onHandleToken }: TokenItemProps) => { const symbolImages = useSymbolImages(); + const defaultToken = useDefaultToken(); + return ( Date: Wed, 19 Jul 2023 20:02:17 +0800 Subject: [PATCH 415/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20delete=20effect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx index 20b078045f..835580d277 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx +++ b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback } from 'react'; import { View, Text } from 'react-native'; import Svg from 'components/Svg'; import { styles } from './style'; From 893d4a0482158bffb4b5ab81ab338922c67cae2a Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 19 Jul 2023 20:05:13 +0800 Subject: [PATCH 416/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20header=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/utils/fetch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/fetch.ts b/packages/utils/fetch.ts index 446752044f..84640cf2de 100644 --- a/packages/utils/fetch.ts +++ b/packages/utils/fetch.ts @@ -15,7 +15,7 @@ const defaultHeaders = { Accept: 'text/plain;v=1.0', 'Content-Type': 'application/json', // FIXME: delete - version: 'v1.3.2', + version: 'v1.3.4', }; function formatResponse(response: string) { From 6b947447f37310c93ae0951c24ee4f19c0d0a98b Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 20 Jul 2023 10:30:59 +0800 Subject: [PATCH 417/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20sell=20?= =?UTF-8?q?error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index 7842863d2c..eb42f3190f 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -88,10 +88,8 @@ export const useSellTransfer = () => { }, }); } catch (e) { - throw { - code: 'NO_TX_HASH', - message: 'Transaction failed. Please contact the team for assistance.', - }; + resolve(null); + return; } const { remove: removeRes } = signalrSell.onRequestOrderTransferred({ clientId, orderId }, async data => { From f326c414c205d1c9ea735683b9bd25bacf951afd Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 15:16:28 +0800 Subject: [PATCH 418/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20cahnge=20decimals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/Records/index.tsx | 83 ------------------- .../js/pages/Send/SendHome/index.tsx | 3 +- packages/store/store-ca/discover/slice.ts | 14 ++-- 3 files changed, 9 insertions(+), 91 deletions(-) delete mode 100644 packages/mobile-app-did/js/pages/Discover/Records/index.tsx diff --git a/packages/mobile-app-did/js/pages/Discover/Records/index.tsx b/packages/mobile-app-did/js/pages/Discover/Records/index.tsx deleted file mode 100644 index 5a7696ae9c..0000000000 --- a/packages/mobile-app-did/js/pages/Discover/Records/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { StyleSheet, View } from 'react-native'; -import GStyles from 'assets/theme/GStyles'; -import { BGStyles } from 'assets/theme/styles'; -import PageContainer from 'components/PageContainer'; -import { useLanguage } from 'i18n/hooks'; -import { pTd } from 'utils/unit'; -import { TextM } from 'components/CommonText'; -import fonts from 'assets/theme/fonts'; -import { clearRecordsList } from '@portkey-wallet/store/store-ca/discover/slice'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; -import { useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; -import CommonButton from 'components/CommonButton'; -import { ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; - -export default function RecordsPage() { - const { t } = useLanguage(); - - const dispatch = useAppCommonDispatch(); - const { networkType } = useCurrentNetworkInfo(); - const { discoverMap } = useAppCASelector(state => state.discover); - - const showRecordList = useMemo(() => { - const recordsList = (discoverMap?.[networkType]?.recordsList as ITabItem[]) || []; - return recordsList.map(ele => ele).reverse(); - }, [discoverMap, networkType]); - - const clearRecord = useCallback(() => { - dispatch(clearRecordsList({ networkType })); - }, [dispatch, networkType]); - if (showRecordList?.length === 0) return null; - - return ( - - - {showRecordList.map((ele, index) => ( - {'111'} - ))} - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - paddingLeft: 0, - paddingRight: 0, - }, - inputContainer: { - ...GStyles.paddingArg(8, 20), - }, - inputStyle: { - width: pTd(280), - }, - sectionWrap: { - ...GStyles.paddingArg(24, 20), - }, - rnInputStyle: { - fontSize: pTd(14), - }, - headerWrap: { - height: pTd(22), - }, - header: { - ...fonts.mediumFont, - lineHeight: pTd(24), - }, - cancelButton: { - paddingLeft: pTd(12), - lineHeight: pTd(36), - }, - rightIconContainerStyle: { - marginRight: pTd(10), - }, -}); diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index d7610d754c..29ea2b7f92 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -134,12 +134,13 @@ const SendHome: React.FC = () => { if (!TransactionFee) throw { code: 500, message: 'no enough fee' }; - return unitConverter(ZERO.plus(TransactionFee?.[defaultToken.decimals]).div(`1e${defaultToken.decimals}`)); + return unitConverter(ZERO.plus(TransactionFee?.[defaultToken.symbol]).div(`1e${defaultToken.decimals}`)); }, [ chainInfo, debounceSendNumber, defaultToken.decimals, + defaultToken.symbol, pin, selectedAssets.decimals, selectedAssets.symbol, diff --git a/packages/store/store-ca/discover/slice.ts b/packages/store/store-ca/discover/slice.ts index 10de4ad857..b18d8a66be 100644 --- a/packages/store/store-ca/discover/slice.ts +++ b/packages/store/store-ca/discover/slice.ts @@ -49,7 +49,7 @@ export const discoverSlice = createSlice({ } if (targetItem) { - const arr = state.discoverMap?.[networkType]?.recordsList.filter(item => item.url !== url) || []; + const arr = state.discoverMap?.[networkType]?.recordsList?.filter(item => item.url !== url) || []; arr.push(targetItem); targetNetworkDiscover.recordsList = arr; } else { @@ -60,7 +60,7 @@ export const discoverSlice = createSlice({ const { networkType } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - targetNetworkDiscover.recordsList = targetNetworkDiscover?.recordsList.map(item => { + targetNetworkDiscover.recordsList = targetNetworkDiscover?.recordsList?.map(item => { return item.url === payload.url ? { ...item, ...payload } : item; }); }, @@ -73,7 +73,7 @@ export const discoverSlice = createSlice({ idsObj[id] = id; }); - targetNetworkDiscover.recordsList = targetNetworkDiscover.recordsList.filter(ele => !idsObj?.[ele?.id]); + targetNetworkDiscover.recordsList = targetNetworkDiscover?.recordsList?.filter(ele => !idsObj?.[ele?.id]); }, clearRecordsList: (state, { payload }: { payload: { networkType: NetworkType } }) => { const { networkType } = payload; @@ -96,9 +96,9 @@ export const discoverSlice = createSlice({ targetNetworkDiscover.tabs = [{ ...payload }]; } else { if (TAB_LIMIT <= targetNetworkDiscover.tabs.length) { - targetNetworkDiscover.tabs.shift(); + targetNetworkDiscover?.tabs?.shift(); } - targetNetworkDiscover.tabs.push({ ...payload }); + targetNetworkDiscover?.tabs?.push({ ...payload }); } state.activeTabId = id; @@ -107,7 +107,7 @@ export const discoverSlice = createSlice({ const { networkType, id } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.filter(item => item.id !== id); + targetNetworkDiscover.tabs = targetNetworkDiscover?.tabs?.filter(item => item.id !== id); }, setActiveTab: (state, { payload }: { payload: { id: number | undefined; networkType: NetworkType } }) => { const { id } = payload; @@ -119,7 +119,7 @@ export const discoverSlice = createSlice({ const { networkType, id } = payload; const targetNetworkDiscover = state.discoverMap?.[networkType] || ({} as IDiscoverNetworkStateType); - targetNetworkDiscover.tabs = targetNetworkDiscover.tabs.map(item => + targetNetworkDiscover.tabs = targetNetworkDiscover?.tabs?.map(item => item.id === id ? { ...item, ...payload } : item, ); }, From d2b0d217a25a28195a2d32b9bdc934c5aad574f3 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 20 Jul 2023 15:58:08 +0800 Subject: [PATCH 419/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20social=20account?= =?UTF-8?q?=20register=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/SelectVerifier/index.tsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx index 3e2f7149df..6142edb002 100644 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx +++ b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx @@ -6,9 +6,12 @@ import { useAppDispatch, useGuardiansInfo, useLoginInfo } from 'store/Provider/h import PortKeyTitle from 'pages/components/PortKeyTitle'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import { setRegisterVerifierAction } from 'store/reducers/loginCache/actions'; -import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { VerifierSelect } from '@portkey/did-ui-react'; import { VerifierItem } from '@portkey-wallet/types/verifier'; +import InternalMessage from 'messages/InternalMessage'; +import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; +import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQueryResult'; import './index.less'; interface ConfirmResultInfo { @@ -24,6 +27,8 @@ export default function SelectVerifier() { const navigate = useNavigate(); const dispatch = useAppDispatch(); const originChainId = useOriginChainId(); + const { walletInfo } = useCurrentWallet(); + const onManagerAddressAndQueryResult = useOnManagerAddressAndQueryResult('register'); const onConfirm = useCallback( async (result: ConfirmResultInfo) => { @@ -55,12 +60,21 @@ export default function SelectVerifier() { signature: result.signature, }), ); - navigate('/login/set-pin/register'); + const res = await InternalMessage.payload(PortkeyMessageTypes.CHECK_WALLET_STATUS).send(); + if (walletInfo.address && res.data.privateKey) { + onManagerAddressAndQueryResult(res.data.privateKey, { + verifierId: result.verifier.id, + verificationDoc: result.verificationDoc, + signature: result.signature, + }); + } else { + navigate('/login/set-pin/register'); + } } else { message.error('Verification failed, please try again later'); } }, - [dispatch, loginAccount, navigate], + [dispatch, loginAccount, navigate, onManagerAddressAndQueryResult, walletInfo.address], ); const authorized = useMemo( From 262366a415fc75487711630406778e81ae01ba6d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 16:37:43 +0800 Subject: [PATCH 420/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20cms=20graph=20ql?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/graphql/schema/cms_schema.graphql | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/graphql/schema/cms_schema.graphql b/packages/graphql/schema/cms_schema.graphql index aa055e6a95..5e583c68af 100644 --- a/packages/graphql/schema/cms_schema.graphql +++ b/packages/graphql/schema/cms_schema.graphql @@ -31,6 +31,9 @@ type Query { officialSocialMedia(filter: officialSocialMedia_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [officialSocialMedia!]! officialSocialMedia_by_id(id: ID!): officialSocialMedia officialSocialMedia_aggregated(groupBy: [String], filter: officialSocialMedia_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [officialSocialMedia_aggregated!]! + rememberMeBlackListSites(filter: rememberMeBlackListSites_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [rememberMeBlackListSites!]! + rememberMeBlackListSites_by_id(id: ID!): rememberMeBlackListSites + rememberMeBlackListSites_aggregated(groupBy: [String], filter: rememberMeBlackListSites_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [rememberMeBlackListSites_aggregated!]! socialMedia(filter: socialMedia_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [socialMedia!]! socialMedia_by_id(id: ID!): socialMedia socialMedia_aggregated(groupBy: [String], filter: socialMedia_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [socialMedia_aggregated!]! @@ -634,6 +637,50 @@ type officialSocialMedia_aggregated_fields { sort: Float } +type rememberMeBlackListSites { + date_created: Date + date_created_func: datetime_functions + date_updated: Date + date_updated_func: datetime_functions + id: ID! + name: String + sort: Int + status: String + url: String + user_created: String + user_updated: String +} + +type rememberMeBlackListSites_aggregated { + group: JSON + countAll: Int + count: rememberMeBlackListSites_aggregated_count + countDistinct: rememberMeBlackListSites_aggregated_count + avg: rememberMeBlackListSites_aggregated_fields + sum: rememberMeBlackListSites_aggregated_fields + avgDistinct: rememberMeBlackListSites_aggregated_fields + sumDistinct: rememberMeBlackListSites_aggregated_fields + min: rememberMeBlackListSites_aggregated_fields + max: rememberMeBlackListSites_aggregated_fields +} + +type rememberMeBlackListSites_aggregated_count { + date_created: Int + date_updated: Int + id: Int + name: Int + sort: Int + status: Int + url: Int + user_created: Int + user_updated: Int +} + +type rememberMeBlackListSites_aggregated_fields { + id: Float + sort: Float +} + type socialMedia { date_created: Date date_created_func: datetime_functions @@ -1147,6 +1194,22 @@ input officialSocialMedia_filter { _or: [officialSocialMedia_filter] } +input rememberMeBlackListSites_filter { + date_created: date_filter_operators + date_created_func: datetime_function_filter_operators + date_updated: date_filter_operators + date_updated_func: datetime_function_filter_operators + id: number_filter_operators + name: string_filter_operators + sort: number_filter_operators + status: string_filter_operators + url: string_filter_operators + user_created: string_filter_operators + user_updated: string_filter_operators + _and: [rememberMeBlackListSites_filter] + _or: [rememberMeBlackListSites_filter] +} + input socialMedia_filter { date_created: date_filter_operators date_created_func: datetime_function_filter_operators From 052cc9b0ffc2a0102b1b2c8c256f3d8cc8ee0b2d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 16:39:33 +0800 Subject: [PATCH 421/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20cms=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/cms/actions.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/store/store-ca/cms/actions.ts b/packages/store/store-ca/cms/actions.ts index b81ecf0315..464f18876a 100644 --- a/packages/store/store-ca/cms/actions.ts +++ b/packages/store/store-ca/cms/actions.ts @@ -101,3 +101,20 @@ export const getBuyButtonAsync = createAsyncThunk>, NetworkType>( + 'cms/getRememberMeBlackListAsync', + async (network: NetworkType) => { + const result = await getBuyButton(network, {}); + + if (result.data.buyButton) { + return { + buyButtonNetMap: { + [network]: result.data.buyButton, + }, + }; + } else { + throw new Error('discoverGroupListNetMap error'); + } + }, +); From 16b1e7c4e914238490e124311b6d52cf6d14e98c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 16:50:24 +0800 Subject: [PATCH 422/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20input=20ui=20ch?= =?UTF-8?q?ange?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/components/ContactList/index.tsx | 2 +- .../mobile-app-did/js/pages/Token/ManageTokenList/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/components/ContactList/index.tsx b/packages/mobile-app-did/js/components/ContactList/index.tsx index 17aba6338c..fe3d86ba5e 100644 --- a/packages/mobile-app-did/js/components/ContactList/index.tsx +++ b/packages/mobile-app-did/js/components/ContactList/index.tsx @@ -130,7 +130,7 @@ const ContactsList: React.FC = ({ return ( {isSearchShow && ( - + Date: Thu, 20 Jul 2023 16:58:21 +0800 Subject: [PATCH 423/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20useDiscoverGroup?= =?UTF-8?q?List=20cms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index 511ad17959..2aa48607ed 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -57,11 +57,11 @@ export function useDiscoverGroupList(isInit = false) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // useEffect(() => { - // if (!isInit) { - // dispatch(getDiscoverGroupAsync(networkType)); - // } - // }, [dispatch, isInit, networkType]); + useEffect(() => { + if (!isInit) { + dispatch(getDiscoverGroupAsync(networkType)); + } + }, [dispatch, isInit, networkType]); return discoverGroupList || []; } From 16de72ae61c79cd6eb7a5cff7708bfdd55cdb5a4 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 20 Jul 2023 17:16:40 +0800 Subject: [PATCH 424/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20side=20chain=20id?= =?UTF-8?q?=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx index 9db2bcedfc..f3a34278ef 100644 --- a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx +++ b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx @@ -203,6 +203,7 @@ export default function SetWalletPin() { Date: Thu, 20 Jul 2023 18:04:01 +0800 Subject: [PATCH 425/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20delete=20dis?= =?UTF-8?q?cover=20record=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiscoverRecordListSection/index.tsx | 83 ------------------- 1 file changed, 83 deletions(-) delete mode 100644 packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx deleted file mode 100644 index f772b20cfa..0000000000 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverRecordListSection/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { defaultColors } from 'assets/theme'; -import GStyles from 'assets/theme/GStyles'; -import { TextM, TextS } from 'components/CommonText'; -import React from 'react'; -import { ScrollView, StyleSheet, View } from 'react-native'; -import { pTd } from 'utils/unit'; -import { useLanguage } from 'i18n/hooks'; - -export function DiscoverRecordListSection() { - const { t } = useLanguage(); - - return ( - - - {t('Records')} - {t('See All')} - - - - {[1, 2, 3].map(ele => ( - {ele} - ))} - - - {/* {[].map((group, index) => ( - - {group.title} - - {group.items.map((item, i) => ( - onClickJump(item)}> - - - - - {item.description} - - - - ))} - - - ))} */} - - ); -} - -const styles = StyleSheet.create({ - wrap: { - ...GStyles.paddingArg(8, 20), - flex: 1, - }, - header: { - display: 'flex', - }, - groupTitle: { - marginBottom: pTd(8), - marginTop: pTd(16), - }, - itemsGroup: { - borderRadius: pTd(6), - backgroundColor: defaultColors.bg1, - overflow: 'hidden', - }, - itemWrap: { - backgroundColor: defaultColors.bg1, - display: 'flex', - flexDirection: 'row', - ...GStyles.paddingArg(16, 12), - width: '100%', - }, - image: { - width: pTd(40), - height: pTd(40), - marginRight: pTd(16), - borderRadius: pTd(20), - }, - right: { - flex: 1, - display: 'flex', - flexDirection: 'column', - justifyContent: 'space-between', - }, -}); From c21e8d45709b5d4685352b1ccf9b03ab3335f73e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 18:07:32 +0800 Subject: [PATCH 426/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20div=20decima?= =?UTF-8?q?ls=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Send/SendHome/index.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx index 6c92e4309a..8e77244134 100644 --- a/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Send/SendHome/index.tsx @@ -117,8 +117,6 @@ const SendHome: React.FC = () => { const raw = await contract.encodedTx(firstMethodName, secondParams); - console.log('====raw======', firstMethodName, secondParams); - const { TransactionFee } = await customFetch(`${chainInfo?.endPoint}/api/blockChain/calculateTransactionFee`, { method: 'POST', params: { @@ -126,15 +124,9 @@ const SendHome: React.FC = () => { }, }); - console.log( - '====TransactionFee======', - TransactionFee, - unitConverter(ZERO.plus(TransactionFee?.[defaultToken.decimals] || '0').div(`1e${defaultToken.decimals}`)), - ); - if (!TransactionFee) throw { code: 500, message: 'no enough fee' }; - return unitConverter(ZERO.plus(TransactionFee?.[defaultToken.symbol]).div(`1e${defaultToken.decimals}`)); + return unitConverter(divDecimals(TransactionFee?.[defaultToken.symbol], defaultToken.decimals)); }, [ chainInfo, From c665fb713eb38303fdabdfef82cf233ba09ea65b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 18:24:29 +0800 Subject: [PATCH 427/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20inner=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Token/ManageTokenList/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx index a68164debf..79f265bf72 100644 --- a/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/ManageTokenList/index.tsx @@ -134,7 +134,7 @@ const ManageTokenList: React.FC = () => { const RightDom = useMemo( () => ( { navigationService.navigate('CustomToken'); }}> @@ -201,4 +201,7 @@ export const pageStyles = StyleSheet.create({ loadingIcon: { width: pTd(20), }, + rightIconStyle: { + padding: pTd(16), + }, }); From b251251bda50a970002f65600e28d2bc13886244 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 18:39:52 +0800 Subject: [PATCH 428/893] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20id=20date=20?= =?UTF-8?q?now?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/discover.ts | 12 +++++++----- packages/mobile-app-did/js/hooks/useScheme.ts | 1 - .../Discover/Bookmark/components/BookmarkItem.tsx | 1 - .../Discover/Bookmark/components/RecordItem.tsx | 1 - .../js/pages/Discover/DiscoverSearch/index.tsx | 1 - .../components/DiscoverArchivedSection/index.tsx | 1 - .../components/DiscoverCmsListSection/index.tsx | 1 - .../components/SearchDiscoverSection/index.tsx | 1 - .../components/SearchRecordSection/index.tsx | 1 - packages/mobile-app-did/js/pages/QrScanner/index.tsx | 1 - 10 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 9d4fa85178..784d0c2eb0 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -39,12 +39,14 @@ export const useDiscoverJumpWithNetWork = () => { const dispatch = useAppCommonDispatch(); const discoverJump = useCallback( - ({ item, autoApprove }: { item: ITabItem; autoApprove?: boolean }) => { - dispatch(createNewTab({ ...item, networkType })); - dispatch(setActiveTab({ ...item, networkType })); - dispatch(addRecordsItem({ ...item, networkType })); + ({ item, autoApprove }: { item: Omit; autoApprove?: boolean }) => { + const id = Date.now(); + + dispatch(createNewTab({ ...item, id, networkType })); + dispatch(setActiveTab({ ...item, id, networkType })); + dispatch(addRecordsItem({ ...item, id, networkType })); dispatch(changeDrawerOpenStatus(true)); - if (autoApprove) dispatch(addAutoApproveItem(item.id)); + if (autoApprove) dispatch(addAutoApproveItem(id)); }, [dispatch, networkType], ); diff --git a/packages/mobile-app-did/js/hooks/useScheme.ts b/packages/mobile-app-did/js/hooks/useScheme.ts index 00430dae27..6119a70da2 100644 --- a/packages/mobile-app-did/js/hooks/useScheme.ts +++ b/packages/mobile-app-did/js/hooks/useScheme.ts @@ -30,7 +30,6 @@ export function useHandleParsedUrl() { const fixUrl = prefixUrlWithProtocol(url); jumpToWebview({ item: { - id: Date.now(), name: fixUrl, url: fixUrl, }, diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index 97c2025bef..d2dd36d53c 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -43,7 +43,6 @@ export default memo( if (isEdit) return; discoverJump({ item: { - id: Date.now(), name: item?.name || '', url: item?.url || '', }, diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx index 33ee53dcef..0fbf0e0fc1 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/RecordItem.tsx @@ -44,7 +44,6 @@ export default memo( discoverJump({ item: { - id: Date.now(), name: i?.name || '', url: i?.url, }, diff --git a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx index 1691c8dbf0..90bb9ebf90 100644 --- a/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/DiscoverSearch/index.tsx @@ -52,7 +52,6 @@ export default function DiscoverSearch() { (name: string, url: string) => { jumpToWebview({ item: { - id: Date.now(), name, url, }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 6f2fd81b25..e8731c9bd9 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -39,7 +39,6 @@ export function DiscoverArchivedSection() { (i: ITabItem | IBookmarkItem) => { discoverJump({ item: { - id: Date.now(), name: i?.name || '', url: i?.url, }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx index 0dfa34d7ce..c802505195 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverCmsListSection/index.tsx @@ -21,7 +21,6 @@ export function DiscoverCmsListSection() { (i: DiscoverItem) => { discoverJump({ item: { - id: Date.now(), name: i.title, url: i?.url, }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx index 9370913c96..1d89d2b4ea 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchDiscoverSection/index.tsx @@ -27,7 +27,6 @@ export default function SearchDiscoverSection(props: ISearchDiscoverSectionProps (i: DiscoverItem) => { jumpToWebview({ item: { - id: Date.now(), name: i.title, url: i?.url ?? i?.description, }, diff --git a/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx index a2f4f235da..c187b45f9c 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/SearchRecordSection/index.tsx @@ -35,7 +35,6 @@ export default function SearchRecordSection() { (i: ITabItem) => { discoverJump({ item: { - id: Date.now(), name: i?.name || '', url: i?.url, }, diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 0086b0db5f..3cb3e16a6c 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -51,7 +51,6 @@ const QrScanner: React.FC = () => { if (checkIsUrl(str)) { jumpToWebview({ item: { - id: Date.now(), name: str, url: str, }, From bb6dc5a8f892984a2fa4cc99f50ba636297f97e0 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 18:40:54 +0800 Subject: [PATCH 429/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20change=20the=20?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Token/SearchTokenList/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx index 246cffe13c..d33994ce2e 100644 --- a/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx +++ b/packages/mobile-app-did/js/pages/Token/SearchTokenList/index.tsx @@ -19,6 +19,7 @@ import Svg from 'components/Svg'; import FilterTokenSection from '../components/FilterToken'; import Lottie from 'lottie-react-native'; import { handleErrorMessage } from '@portkey-wallet/utils'; +import { TextInput } from 'react-native-gesture-handler'; interface ManageTokenListProps { route?: any; @@ -26,7 +27,7 @@ interface ManageTokenListProps { const SearchTokenList: React.FC = () => { const { t } = useLanguage(); const timerRef = useRef(null); - const iptRef = useRef(); + const iptRef = useRef(); const chainIdList = useChainIdList(); @@ -126,7 +127,7 @@ const SearchTokenList: React.FC = () => { useEffect(() => { // input focus timerRef.current = setTimeout(() => { - if (iptRef && iptRef?.current) iptRef.current.focus(); + iptRef.current?.focus(); }, 200); return () => { From d844149b49110bd16334f0a2c892b587e62188ad Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Thu, 20 Jul 2023 19:09:12 +0800 Subject: [PATCH 430/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20cms?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/rememberMeBlackListSites.ts | 147 +++++++++++++ .../hooks/rememberMeBlackListSitesCustom.ts | 101 +++++++++ .../rememberMeBlackListSites_aggregated.ts | 207 ++++++++++++++++++ .../hooks/rememberMeBlackListSites_by_id.ts | 131 +++++++++++ .../__generated__/operation/queries/index.js | 9 + .../queries/rememberMeBlackListSites.gql | 33 +++ .../rememberMeBlackListSites_aggregated.gql | 52 +++++ .../rememberMeBlackListSites_by_id.gql | 33 +++ packages/graphql/cms/__generated__/types.ts | 90 ++++++++ .../graphql/cms/custom/buyButtonCustom.gql | 1 + .../custom/rememberMeBlackListSitesCustom.gql | 7 + packages/graphql/cms/queries.ts | 23 +- packages/hooks/hooks-ca/cms.ts | 40 +++- .../js/components/Updater/index.tsx | 8 +- packages/mobile-app-did/js/hooks/discover.ts | 16 ++ .../js/pages/DashBoard/Card/index.tsx | 5 +- packages/store/store-ca/cms/actions.ts | 46 ++-- packages/store/store-ca/cms/slice.ts | 18 +- packages/store/store-ca/cms/types.ts | 8 + 19 files changed, 950 insertions(+), 25 deletions(-) create mode 100644 packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites.ts create mode 100644 packages/graphql/cms/__generated__/hooks/rememberMeBlackListSitesCustom.ts create mode 100644 packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_aggregated.ts create mode 100644 packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_by_id.ts create mode 100644 packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_aggregated.gql create mode 100644 packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_by_id.gql create mode 100644 packages/graphql/cms/custom/rememberMeBlackListSitesCustom.gql diff --git a/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites.ts b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites.ts new file mode 100644 index 0000000000..8e28af5420 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites.ts @@ -0,0 +1,147 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type RememberMeBlackListSitesQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; +}>; + +export type RememberMeBlackListSitesQuery = { + __typename?: 'Query'; + rememberMeBlackListSites: Array<{ + __typename?: 'rememberMeBlackListSites'; + date_created?: any | null; + date_updated?: any | null; + id: string; + name?: string | null; + sort?: number | null; + status?: string | null; + url?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + }>; +}; + +export const RememberMeBlackListSitesDocument = gql` + query rememberMeBlackListSites( + $filter: rememberMeBlackListSites_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + ) { + rememberMeBlackListSites( + filter: $filter + sort: $sort + limit: $limit + offset: $offset + page: $page + search: $search + ) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + name + sort + status + url + user_created + user_updated + } + } +`; + +/** + * __useRememberMeBlackListSitesQuery__ + * + * To run a query within a React component, call `useRememberMeBlackListSitesQuery` and pass it any options that fit your needs. + * When your component renders, `useRememberMeBlackListSitesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useRememberMeBlackListSitesQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * }, + * }); + */ +export function useRememberMeBlackListSitesQuery( + baseOptions?: Apollo.QueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + RememberMeBlackListSitesDocument, + options, + ); +} +export function useRememberMeBlackListSitesLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + RememberMeBlackListSitesDocument, + options, + ); +} +export type RememberMeBlackListSitesQueryHookResult = ReturnType; +export type RememberMeBlackListSitesLazyQueryHookResult = ReturnType; +export type RememberMeBlackListSitesQueryResult = Apollo.QueryResult< + RememberMeBlackListSitesQuery, + RememberMeBlackListSitesQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSitesCustom.ts b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSitesCustom.ts new file mode 100644 index 0000000000..357236b81a --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSitesCustom.ts @@ -0,0 +1,101 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type RememberMeBlackListSitesCustomQueryVariables = Types.Exact<{ + filter?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; +}>; + +export type RememberMeBlackListSitesCustomQuery = { + __typename?: 'Query'; + rememberMeBlackListSites: Array<{ + __typename?: 'rememberMeBlackListSites'; + id: string; + name?: string | null; + url?: string | null; + }>; +}; + +export const RememberMeBlackListSitesCustomDocument = gql` + query rememberMeBlackListSitesCustom( + $filter: rememberMeBlackListSites_filter + $sort: [String] + $limit: Int + $offset: Int + $page: Int + $search: String + ) { + rememberMeBlackListSites( + filter: $filter + sort: $sort + limit: $limit + offset: $offset + page: $page + search: $search + ) { + id + name + url + } + } +`; + +/** + * __useRememberMeBlackListSitesCustomQuery__ + * + * To run a query within a React component, call `useRememberMeBlackListSitesCustomQuery` and pass it any options that fit your needs. + * When your component renders, `useRememberMeBlackListSitesCustomQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useRememberMeBlackListSitesCustomQuery({ + * variables: { + * filter: // value for 'filter' + * sort: // value for 'sort' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * }, + * }); + */ +export function useRememberMeBlackListSitesCustomQuery( + baseOptions?: Apollo.QueryHookOptions< + RememberMeBlackListSitesCustomQuery, + RememberMeBlackListSitesCustomQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + RememberMeBlackListSitesCustomDocument, + options, + ); +} +export function useRememberMeBlackListSitesCustomLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + RememberMeBlackListSitesCustomQuery, + RememberMeBlackListSitesCustomQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + RememberMeBlackListSitesCustomDocument, + options, + ); +} +export type RememberMeBlackListSitesCustomQueryHookResult = ReturnType; +export type RememberMeBlackListSitesCustomLazyQueryHookResult = ReturnType< + typeof useRememberMeBlackListSitesCustomLazyQuery +>; +export type RememberMeBlackListSitesCustomQueryResult = Apollo.QueryResult< + RememberMeBlackListSitesCustomQuery, + RememberMeBlackListSitesCustomQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_aggregated.ts b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_aggregated.ts new file mode 100644 index 0000000000..8106ad3e50 --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_aggregated.ts @@ -0,0 +1,207 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type RememberMeBlackListSites_AggregatedQueryVariables = Types.Exact<{ + groupBy?: Types.InputMaybe< + Array> | Types.InputMaybe + >; + filter?: Types.InputMaybe; + limit?: Types.InputMaybe; + offset?: Types.InputMaybe; + page?: Types.InputMaybe; + search?: Types.InputMaybe; + sort?: Types.InputMaybe> | Types.InputMaybe>; +}>; + +export type RememberMeBlackListSites_AggregatedQuery = { + __typename?: 'Query'; + rememberMeBlackListSites_aggregated: Array<{ + __typename?: 'rememberMeBlackListSites_aggregated'; + group?: any | null; + countAll?: number | null; + count?: { + __typename?: 'rememberMeBlackListSites_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + name?: number | null; + sort?: number | null; + status?: number | null; + url?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + countDistinct?: { + __typename?: 'rememberMeBlackListSites_aggregated_count'; + date_created?: number | null; + date_updated?: number | null; + id?: number | null; + name?: number | null; + sort?: number | null; + status?: number | null; + url?: number | null; + user_created?: number | null; + user_updated?: number | null; + } | null; + avg?: { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: number | null; + sort?: number | null; + } | null; + sum?: { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: number | null; + sort?: number | null; + } | null; + avgDistinct?: { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: number | null; + sort?: number | null; + } | null; + sumDistinct?: { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: number | null; + sort?: number | null; + } | null; + min?: { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: number | null; + sort?: number | null; + } | null; + max?: { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: number | null; + sort?: number | null; + } | null; + }>; +}; + +export const RememberMeBlackListSites_AggregatedDocument = gql` + query rememberMeBlackListSites_aggregated( + $groupBy: [String] + $filter: rememberMeBlackListSites_filter + $limit: Int + $offset: Int + $page: Int + $search: String + $sort: [String] + ) { + rememberMeBlackListSites_aggregated( + groupBy: $groupBy + filter: $filter + limit: $limit + offset: $offset + page: $page + search: $search + sort: $sort + ) { + group + countAll + count { + date_created + date_updated + id + name + sort + status + url + user_created + user_updated + } + countDistinct { + date_created + date_updated + id + name + sort + status + url + user_created + user_updated + } + avg { + id + sort + } + sum { + id + sort + } + avgDistinct { + id + sort + } + sumDistinct { + id + sort + } + min { + id + sort + } + max { + id + sort + } + } + } +`; + +/** + * __useRememberMeBlackListSites_AggregatedQuery__ + * + * To run a query within a React component, call `useRememberMeBlackListSites_AggregatedQuery` and pass it any options that fit your needs. + * When your component renders, `useRememberMeBlackListSites_AggregatedQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useRememberMeBlackListSites_AggregatedQuery({ + * variables: { + * groupBy: // value for 'groupBy' + * filter: // value for 'filter' + * limit: // value for 'limit' + * offset: // value for 'offset' + * page: // value for 'page' + * search: // value for 'search' + * sort: // value for 'sort' + * }, + * }); + */ +export function useRememberMeBlackListSites_AggregatedQuery( + baseOptions?: Apollo.QueryHookOptions< + RememberMeBlackListSites_AggregatedQuery, + RememberMeBlackListSites_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + RememberMeBlackListSites_AggregatedDocument, + options, + ); +} +export function useRememberMeBlackListSites_AggregatedLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + RememberMeBlackListSites_AggregatedQuery, + RememberMeBlackListSites_AggregatedQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery< + RememberMeBlackListSites_AggregatedQuery, + RememberMeBlackListSites_AggregatedQueryVariables + >(RememberMeBlackListSites_AggregatedDocument, options); +} +export type RememberMeBlackListSites_AggregatedQueryHookResult = ReturnType< + typeof useRememberMeBlackListSites_AggregatedQuery +>; +export type RememberMeBlackListSites_AggregatedLazyQueryHookResult = ReturnType< + typeof useRememberMeBlackListSites_AggregatedLazyQuery +>; +export type RememberMeBlackListSites_AggregatedQueryResult = Apollo.QueryResult< + RememberMeBlackListSites_AggregatedQuery, + RememberMeBlackListSites_AggregatedQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_by_id.ts b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_by_id.ts new file mode 100644 index 0000000000..df5eab7f4f --- /dev/null +++ b/packages/graphql/cms/__generated__/hooks/rememberMeBlackListSites_by_id.ts @@ -0,0 +1,131 @@ +import * as Types from '../types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type RememberMeBlackListSites_By_IdQueryVariables = Types.Exact<{ + id: Types.Scalars['ID']; +}>; + +export type RememberMeBlackListSites_By_IdQuery = { + __typename?: 'Query'; + rememberMeBlackListSites_by_id?: { + __typename?: 'rememberMeBlackListSites'; + date_created?: any | null; + date_updated?: any | null; + id: string; + name?: string | null; + sort?: number | null; + status?: string | null; + url?: string | null; + user_created?: string | null; + user_updated?: string | null; + date_created_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + date_updated_func?: { + __typename?: 'datetime_functions'; + year?: number | null; + month?: number | null; + week?: number | null; + day?: number | null; + weekday?: number | null; + hour?: number | null; + minute?: number | null; + second?: number | null; + } | null; + } | null; +}; + +export const RememberMeBlackListSites_By_IdDocument = gql` + query rememberMeBlackListSites_by_id($id: ID!) { + rememberMeBlackListSites_by_id(id: $id) { + date_created + date_created_func { + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func { + year + month + week + day + weekday + hour + minute + second + } + id + name + sort + status + url + user_created + user_updated + } + } +`; + +/** + * __useRememberMeBlackListSites_By_IdQuery__ + * + * To run a query within a React component, call `useRememberMeBlackListSites_By_IdQuery` and pass it any options that fit your needs. + * When your component renders, `useRememberMeBlackListSites_By_IdQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useRememberMeBlackListSites_By_IdQuery({ + * variables: { + * id: // value for 'id' + * }, + * }); + */ +export function useRememberMeBlackListSites_By_IdQuery( + baseOptions: Apollo.QueryHookOptions< + RememberMeBlackListSites_By_IdQuery, + RememberMeBlackListSites_By_IdQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery( + RememberMeBlackListSites_By_IdDocument, + options, + ); +} +export function useRememberMeBlackListSites_By_IdLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + RememberMeBlackListSites_By_IdQuery, + RememberMeBlackListSites_By_IdQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery( + RememberMeBlackListSites_By_IdDocument, + options, + ); +} +export type RememberMeBlackListSites_By_IdQueryHookResult = ReturnType; +export type RememberMeBlackListSites_By_IdLazyQueryHookResult = ReturnType< + typeof useRememberMeBlackListSites_By_IdLazyQuery +>; +export type RememberMeBlackListSites_By_IdQueryResult = Apollo.QueryResult< + RememberMeBlackListSites_By_IdQuery, + RememberMeBlackListSites_By_IdQueryVariables +>; diff --git a/packages/graphql/cms/__generated__/operation/queries/index.js b/packages/graphql/cms/__generated__/operation/queries/index.js index cc08c922f1..9638f680b1 100644 --- a/packages/graphql/cms/__generated__/operation/queries/index.js +++ b/packages/graphql/cms/__generated__/operation/queries/index.js @@ -51,6 +51,15 @@ module.exports.officialSocialMedia_aggregated = fs.readFileSync( path.join(__dirname, 'officialSocialMedia_aggregated.gql'), 'utf8', ); +module.exports.rememberMeBlackListSites = fs.readFileSync(path.join(__dirname, 'rememberMeBlackListSites.gql'), 'utf8'); +module.exports.rememberMeBlackListSites_by_id = fs.readFileSync( + path.join(__dirname, 'rememberMeBlackListSites_by_id.gql'), + 'utf8', +); +module.exports.rememberMeBlackListSites_aggregated = fs.readFileSync( + path.join(__dirname, 'rememberMeBlackListSites_aggregated.gql'), + 'utf8', +); module.exports.socialMedia = fs.readFileSync(path.join(__dirname, 'socialMedia.gql'), 'utf8'); module.exports.socialMedia_by_id = fs.readFileSync(path.join(__dirname, 'socialMedia_by_id.gql'), 'utf8'); module.exports.socialMedia_aggregated = fs.readFileSync(path.join(__dirname, 'socialMedia_aggregated.gql'), 'utf8'); diff --git a/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites.gql b/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites.gql new file mode 100644 index 0000000000..5b32e44292 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites.gql @@ -0,0 +1,33 @@ +query rememberMeBlackListSites($filter: rememberMeBlackListSites_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String){ + rememberMeBlackListSites(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + name + sort + status + url + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_aggregated.gql b/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_aggregated.gql new file mode 100644 index 0000000000..399ab6cd0e --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_aggregated.gql @@ -0,0 +1,52 @@ +query rememberMeBlackListSites_aggregated($groupBy: [String], $filter: rememberMeBlackListSites_filter, $limit: Int, $offset: Int, $page: Int, $search: String, $sort: [String]){ + rememberMeBlackListSites_aggregated(groupBy: $groupBy, filter: $filter, limit: $limit, offset: $offset, page: $page, search: $search, sort: $sort){ + group + countAll + count{ + date_created + date_updated + id + name + sort + status + url + user_created + user_updated + } + countDistinct{ + date_created + date_updated + id + name + sort + status + url + user_created + user_updated + } + avg{ + id + sort + } + sum{ + id + sort + } + avgDistinct{ + id + sort + } + sumDistinct{ + id + sort + } + min{ + id + sort + } + max{ + id + sort + } + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_by_id.gql b/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_by_id.gql new file mode 100644 index 0000000000..e112f505b6 --- /dev/null +++ b/packages/graphql/cms/__generated__/operation/queries/rememberMeBlackListSites_by_id.gql @@ -0,0 +1,33 @@ +query rememberMeBlackListSites_by_id($id: ID!){ + rememberMeBlackListSites_by_id(id: $id){ + date_created + date_created_func{ + year + month + week + day + weekday + hour + minute + second + } + date_updated + date_updated_func{ + year + month + week + day + weekday + hour + minute + second + } + id + name + sort + status + url + user_created + user_updated + } +} \ No newline at end of file diff --git a/packages/graphql/cms/__generated__/types.ts b/packages/graphql/cms/__generated__/types.ts index 91aafbe85a..d82016ebd2 100644 --- a/packages/graphql/cms/__generated__/types.ts +++ b/packages/graphql/cms/__generated__/types.ts @@ -54,6 +54,9 @@ export type Query = { officialSocialMedia: Array; officialSocialMedia_aggregated: Array; officialSocialMedia_by_id?: Maybe; + rememberMeBlackListSites: Array; + rememberMeBlackListSites_aggregated: Array; + rememberMeBlackListSites_by_id?: Maybe; socialMedia: Array; socialMedia_aggregated: Array; socialMedia_by_id?: Maybe; @@ -301,6 +304,29 @@ export type QueryOfficialSocialMedia_By_IdArgs = { id: Scalars['ID']; }; +export type QueryRememberMeBlackListSitesArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryRememberMeBlackListSites_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + +export type QueryRememberMeBlackListSites_By_IdArgs = { + id: Scalars['ID']; +}; + export type QuerySocialMediaArgs = { filter?: InputMaybe; limit?: InputMaybe; @@ -1408,6 +1434,70 @@ export type OfficialSocialMedia_Filter = { user_updated?: InputMaybe; }; +export type RememberMeBlackListSites = { + __typename?: 'rememberMeBlackListSites'; + date_created?: Maybe; + date_created_func?: Maybe; + date_updated?: Maybe; + date_updated_func?: Maybe; + id: Scalars['ID']; + name?: Maybe; + sort?: Maybe; + status?: Maybe; + url?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type RememberMeBlackListSites_Aggregated = { + __typename?: 'rememberMeBlackListSites_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type RememberMeBlackListSites_Aggregated_Count = { + __typename?: 'rememberMeBlackListSites_aggregated_count'; + date_created?: Maybe; + date_updated?: Maybe; + id?: Maybe; + name?: Maybe; + sort?: Maybe; + status?: Maybe; + url?: Maybe; + user_created?: Maybe; + user_updated?: Maybe; +}; + +export type RememberMeBlackListSites_Aggregated_Fields = { + __typename?: 'rememberMeBlackListSites_aggregated_fields'; + id?: Maybe; + sort?: Maybe; +}; + +export type RememberMeBlackListSites_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_created?: InputMaybe; + date_created_func?: InputMaybe; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + id?: InputMaybe; + name?: InputMaybe; + sort?: InputMaybe; + status?: InputMaybe; + url?: InputMaybe; + user_created?: InputMaybe; + user_updated?: InputMaybe; +}; + export type SocialMedia = { __typename?: 'socialMedia'; date_created?: Maybe; diff --git a/packages/graphql/cms/custom/buyButtonCustom.gql b/packages/graphql/cms/custom/buyButtonCustom.gql index fc8c84c079..c6ec0a1145 100644 --- a/packages/graphql/cms/custom/buyButtonCustom.gql +++ b/packages/graphql/cms/custom/buyButtonCustom.gql @@ -6,3 +6,4 @@ query buyButtonCustom { status } } + diff --git a/packages/graphql/cms/custom/rememberMeBlackListSitesCustom.gql b/packages/graphql/cms/custom/rememberMeBlackListSitesCustom.gql new file mode 100644 index 0000000000..7bfd791002 --- /dev/null +++ b/packages/graphql/cms/custom/rememberMeBlackListSitesCustom.gql @@ -0,0 +1,7 @@ +query rememberMeBlackListSitesCustom($filter: rememberMeBlackListSites_filter, $sort: [String], $limit: Int, $offset: Int, $page: Int, $search: String){ + rememberMeBlackListSites(filter: $filter, sort: $sort, limit: $limit, offset: $offset, page: $page, search: $search){ + id + name + url + } +} \ No newline at end of file diff --git a/packages/graphql/cms/queries.ts b/packages/graphql/cms/queries.ts index af59f812aa..fe1df4a0fc 100644 --- a/packages/graphql/cms/queries.ts +++ b/packages/graphql/cms/queries.ts @@ -23,6 +23,12 @@ import { BuyButtonCustomQueryVariables, } from './__generated__/hooks/buyButtonCustom'; +import { + RememberMeBlackListSitesCustomDocument, + RememberMeBlackListSitesCustomQuery, + RememberMeBlackListSitesCustomQueryVariables, +} from './__generated__/hooks/rememberMeBlackListSitesCustom'; + // SocialMedia const getSocialMedia = async (network: NetworkType, params: SocialMediaCustomQueryVariables) => { const apolloClient = getApolloClient(network); @@ -43,6 +49,7 @@ const getTabMenu = async (network: NetworkType, params: TabMenuCustomQueryVariab return result; }; +// discover Group const getDiscoverGroup = async (network: NetworkType, params: DiscoverGroupCustomQueryVariables) => { const apolloClient = getApolloClient(network); const result = await apolloClient.query({ @@ -52,6 +59,7 @@ const getDiscoverGroup = async (network: NetworkType, params: DiscoverGroupCusto return result; }; +// buy button show const getBuyButton = async (network: NetworkType, params: BuyButtonCustomQueryVariables) => { const apolloClient = getApolloClient(network); const result = await apolloClient.query({ @@ -61,4 +69,17 @@ const getBuyButton = async (network: NetworkType, params: BuyButtonCustomQueryVa return result; }; -export { getSocialMedia, getTabMenu, getDiscoverGroup, getBuyButton }; +// buy button show +const getRememberMeBlackListSites = async ( + network: NetworkType, + params: RememberMeBlackListSitesCustomQueryVariables, +) => { + const apolloClient = getApolloClient(network); + const result = await apolloClient.query({ + query: RememberMeBlackListSitesCustomDocument, + variables: params, + }); + return result; +}; + +export { getSocialMedia, getTabMenu, getDiscoverGroup, getBuyButton, getRememberMeBlackListSites }; diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index 511ad17959..a9c34e73cb 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -6,6 +6,7 @@ import { getDiscoverGroupAsync, getSocialMediaAsync, getBuyButtonAsync, + getRememberMeBlackListAsync, } from '@portkey-wallet/store/store-ca/cms/actions'; import { BuyButtonType } from '@portkey-wallet/store/store-ca/cms/types'; @@ -57,11 +58,11 @@ export function useDiscoverGroupList(isInit = false) { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // useEffect(() => { - // if (!isInit) { - // dispatch(getDiscoverGroupAsync(networkType)); - // } - // }, [dispatch, isInit, networkType]); + useEffect(() => { + if (!isInit) { + dispatch(getDiscoverGroupAsync(networkType)); + } + }, [dispatch, isInit, networkType]); return discoverGroupList || []; } @@ -129,3 +130,32 @@ export const useBuyButtonShow = () => { refreshBuyButton, }; }; + +export const useRememberMeBlackList = (isInit = false) => { + const dispatch = useAppCommonDispatch(); + const { rememberMeBlackListMap } = useCMS(); + const { networkType } = useCurrentNetworkInfo(); + const networkList = useNetworkList(); + + const rememberMeBlackList = useMemo( + () => rememberMeBlackListMap?.[networkType]?.map(ele => ele?.url) || [], + [networkType, rememberMeBlackListMap], + ); + + useEffect(() => { + if (isInit) { + networkList.forEach(item => { + dispatch(getRememberMeBlackListAsync(item.networkType)); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (!isInit) { + dispatch(getRememberMeBlackListAsync(networkType)); + } + }, [dispatch, isInit, networkType]); + + return rememberMeBlackList || []; +}; diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index 312270be4b..893db56ea6 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -15,7 +15,12 @@ import { useCheckManagerOnLogout } from 'hooks/useLogOut'; import socket from '@portkey-wallet/socket/socket-did'; import CommonToast from 'components/CommonToast'; import { usePhoneCountryCode } from '@portkey-wallet/hooks/hooks-ca/misc'; -import { useBuyButton, useDiscoverGroupList, useSocialMediaList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { + useBuyButton, + useDiscoverGroupList, + useSocialMediaList, + useRememberMeBlackList, +} from '@portkey-wallet/hooks/hooks-ca/cms'; import { useTabMenuList } from 'hooks/cms'; import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; @@ -66,5 +71,6 @@ export default function Updater() { useTabMenuList(true); useDiscoverGroupList(true); useBuyButton(true); + useRememberMeBlackList(true); return null; } diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index 9d4fa85178..fbd973f42f 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -1,5 +1,6 @@ import { request } from '@portkey-wallet/api/api-did'; import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { useRememberMeBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { addUrlToWhiteList, @@ -167,3 +168,18 @@ export const useCheckAndUpDateTabItemName = () => { [discoverMap, dispatch, networkType], ); }; + +export const useCheckSiteIsInBlackList = () => { + const list = useRememberMeBlackList(); + + return useCallback( + (url: string) => { + try { + return list.indexOf(url) >= 0; + } catch (err) { + console.log(err); + } + }, + [list], + ); +}; diff --git a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx index 835580d277..733a7f6bc1 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx +++ b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx @@ -18,7 +18,7 @@ import BuyButton from 'components/BuyButton'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; import FaucetButton from 'components/FaucetButton'; -import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useBuyButtonShow, useRememberMeBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; const Card: React.FC = () => { const { t } = useLanguage(); @@ -28,6 +28,9 @@ const Card: React.FC = () => { const [, requestQrPermission] = useQrScanPermission(); const { isBuyButtonShow } = useBuyButtonShow(); + const list = useRememberMeBlackList(); + console.log('list!!!!', list); + const showDialog = useCallback( () => ActionSheet.alert({ diff --git a/packages/store/store-ca/cms/actions.ts b/packages/store/store-ca/cms/actions.ts index 464f18876a..2310778611 100644 --- a/packages/store/store-ca/cms/actions.ts +++ b/packages/store/store-ca/cms/actions.ts @@ -1,7 +1,13 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { CMSState } from './types'; import { NetworkType } from '@portkey-wallet/types'; -import { getDiscoverGroup, getSocialMedia, getTabMenu, getBuyButton } from '@portkey-wallet/graphql/cms/queries'; +import { + getDiscoverGroup, + getSocialMedia, + getTabMenu, + getBuyButton, + getRememberMeBlackListSites, +} from '@portkey-wallet/graphql/cms/queries'; export const getSocialMediaAsync = createAsyncThunk>, NetworkType>( 'cms/getSocialMediaAsync', @@ -102,19 +108,27 @@ export const getBuyButtonAsync = createAsyncThunk>, NetworkType>( - 'cms/getRememberMeBlackListAsync', - async (network: NetworkType) => { - const result = await getBuyButton(network, {}); +export const getRememberMeBlackListAsync = createAsyncThunk< + Required>, + NetworkType +>('cms/getRememberMeBlackListAsync', async (network: NetworkType) => { + const result = await getRememberMeBlackListSites(network, { + filter: { + status: { + _eq: 'published', + }, + }, + }); - if (result.data.buyButton) { - return { - buyButtonNetMap: { - [network]: result.data.buyButton, - }, - }; - } else { - throw new Error('discoverGroupListNetMap error'); - } - }, -); + console.log('getRememberMeBlackListSites result', result); + + if (result.data.rememberMeBlackListSites) { + return { + rememberMeBlackListMap: { + [network]: result.data.rememberMeBlackListSites, + }, + }; + } else { + throw new Error('rememberMeBlackListMap error'); + } +}); diff --git a/packages/store/store-ca/cms/slice.ts b/packages/store/store-ca/cms/slice.ts index 69e0963f22..c03a54305a 100644 --- a/packages/store/store-ca/cms/slice.ts +++ b/packages/store/store-ca/cms/slice.ts @@ -1,5 +1,11 @@ import { createSlice } from '@reduxjs/toolkit'; -import { getDiscoverGroupAsync, getSocialMediaAsync, getTabMenuAsync, getBuyButtonAsync } from './actions'; +import { + getDiscoverGroupAsync, + getSocialMediaAsync, + getTabMenuAsync, + getBuyButtonAsync, + getRememberMeBlackListAsync, +} from './actions'; import { CMSState } from './types'; const initialState: CMSState = { @@ -7,6 +13,7 @@ const initialState: CMSState = { tabMenuListNetMap: {}, discoverGroupListNetMap: {}, buyButtonNetMap: {}, + rememberMeBlackListMap: {}, }; export const cmsSlice = createSlice({ name: 'cms', @@ -49,6 +56,15 @@ export const cmsSlice = createSlice({ }) .addCase(getBuyButtonAsync.rejected, (_state, action) => { console.log('getBuyButtonAsync error', action); + }) + .addCase(getRememberMeBlackListAsync.fulfilled, (state, action) => { + state.rememberMeBlackListMap = { + ...state.rememberMeBlackListMap, + ...action.payload.rememberMeBlackListMap, + }; + }) + .addCase(getRememberMeBlackListAsync.rejected, (_state, action) => { + console.log('getRememberMeBlackListAsync error', action); }); }, }); diff --git a/packages/store/store-ca/cms/types.ts b/packages/store/store-ca/cms/types.ts index 98c6eafe3a..63db701142 100644 --- a/packages/store/store-ca/cms/types.ts +++ b/packages/store/store-ca/cms/types.ts @@ -39,6 +39,11 @@ export interface BuyButtonType { isSellSectionShow: boolean; } +export interface RememberMeBlackListSiteItem { + name: string; + url: string; +} + export interface CMSState { socialMediaListNetMap: { [T in NetworkType]?: SocialMediaItem[]; @@ -52,4 +57,7 @@ export interface CMSState { buyButtonNetMap?: { [T in NetworkType]?: BuyButtonType; }; + rememberMeBlackListMap?: { + [T in NetworkType]?: RememberMeBlackListSiteItem[]; + }; } From 74f93436b7faedb47313a6aab0cb8cf90c89cb12 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 20 Jul 2023 19:12:22 +0800 Subject: [PATCH 431/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20sell=20cr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/payment.ts | 14 ++++++++++---- .../TabsDrawer/components/TabsOverlay/index.tsx | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/hooks/hooks-ca/payment.ts b/packages/hooks/hooks-ca/payment.ts index eb42f3190f..e948346747 100644 --- a/packages/hooks/hooks-ca/payment.ts +++ b/packages/hooks/hooks-ca/payment.ts @@ -52,6 +52,7 @@ export const useSellTransfer = () => { let signalrAchTxRemove: (() => void) | undefined; let signalrOrderRemove: (() => void) | undefined; + let timer: NodeJS.Timeout | undefined = undefined; const clientId = randomId(); try { @@ -63,11 +64,11 @@ export const useSellTransfer = () => { throw new Error('Transaction failed.'); } - const timerPromise = new Promise<'timeout'>(resolve => - setTimeout(() => { + const timerPromise = new Promise<'timeout'>(resolve => { + timer = setTimeout(() => { resolve('timeout'); - }, SELL_SOCKET_TIMEOUT), - ); + }, SELL_SOCKET_TIMEOUT); + }); const signalrSellPromise = new Promise(resolve => { const { remove: removeAchTx } = signalrSell.onAchTxAddressReceived({ clientId, orderId }, async data => { @@ -119,6 +120,11 @@ export const useSellTransfer = () => { signalrAchTxRemove = undefined; signalrOrderRemove?.(); signalrOrderRemove = undefined; + + if (timer) { + clearTimeout(timer); + timer = undefined; + } signalrSell.stop(); }, [isMainnet], diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx index f2d7978522..198b7460ec 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/TabsOverlay/index.tsx @@ -183,7 +183,7 @@ const BrowserEditModal = ({ {handleArray.map((ele, index) => ( - handleUrl(ele.title)}> + handleUrl(ele.title)}> From 99055c148c7745e7cba6ce17c2b1c0aa3819225d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Fri, 21 Jul 2023 14:33:41 +0800 Subject: [PATCH 432/893] =?UTF-8?q?chore:=20=F0=9F=A4=96=20blackList=20=20?= =?UTF-8?q?getProtocolAndHost?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/hooks/discover.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index fbd973f42f..701728f87b 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -17,7 +17,7 @@ import { } from '@portkey-wallet/store/store-ca/discover/slice'; import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { isUrl } from '@portkey-wallet/utils'; -import { prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; +import { getProtocolAndHost, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; import { useCallback, useEffect, useMemo } from 'react'; @@ -175,7 +175,7 @@ export const useCheckSiteIsInBlackList = () => { return useCallback( (url: string) => { try { - return list.indexOf(url) >= 0; + return list.indexOf(getProtocolAndHost(url)) >= 0; } catch (err) { console.log(err); } From 55b9c81861956d3d0ca3fe2421bba2d00b210820 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Sat, 22 Jul 2023 20:30:28 +0800 Subject: [PATCH 433/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20adjust=20bookmark?= =?UTF-8?q?=20delete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Discover/Bookmark/components/BookmarksSection.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx index b6f7ff92b4..aa5b0c061f 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarksSection.tsx @@ -112,6 +112,7 @@ function BookmarksSection() { })), }, }); + setList(pre => pre.filter(item => !deleteList.current.some(_item => _item.id === item.id))); await sleep(100); getBookmarkListRef.current(true); } catch (error) { @@ -137,6 +138,7 @@ function BookmarksSection() { Loading.show(); try { await request.discover.deleteAllBookmark(); + setList([]); await sleep(100); getBookmarkListRef.current(true); } catch (error) { From a95c758b76fe3fca2715e3300dd4069ace5f9b95 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Sun, 23 Jul 2023 10:34:42 +0800 Subject: [PATCH 434/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20scan=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/QrScanner/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 3cb3e16a6c..0a8503e6a7 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -18,7 +18,7 @@ import { isIos, screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/d import { Camera } from 'expo-camera'; import { expandQrData } from '@portkey-wallet/utils/qrCode'; -import { checkIsUrl } from '@portkey-wallet/utils/dapp/browser'; +import { checkIsUrl, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import Loading from 'components/Loading'; interface QrScannerProps { @@ -36,6 +36,8 @@ const QrScanner: React.FC = () => { console.log(previousRouteInfo, '=====previousRouteInfo'); const [refresh, setRefresh] = useState(); + const [scanUrlFinish, setScanUrlFinish] = useState(false); + useFocusEffect( useCallback(() => { setRefresh(false); @@ -47,14 +49,17 @@ const QrScanner: React.FC = () => { if (typeof data !== 'string') return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); try { - const str = data.replace(/("|')/g, ''); + const str = prefixUrlWithProtocol(data.replace(/("|'|\s)/g, '')); + if (checkIsUrl(str)) { + if (scanUrlFinish) return; jumpToWebview({ item: { name: str, url: str, }, }); + setScanUrlFinish(true); return navigationService.goBack(); } From 68211cf55d5110e9e2b3e394968a927962121a05 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 24 Jul 2023 11:00:09 +0800 Subject: [PATCH 435/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20change=20to=20thr?= =?UTF-8?q?ottle=20callback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/js/pages/QrScanner/index.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index 0a8503e6a7..adf8aea35e 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -21,6 +21,7 @@ import { expandQrData } from '@portkey-wallet/utils/qrCode'; import { checkIsUrl, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import Loading from 'components/Loading'; +import { useThrottleCallback } from '@portkey-wallet/hooks'; interface QrScannerProps { route?: any; } @@ -36,7 +37,6 @@ const QrScanner: React.FC = () => { console.log(previousRouteInfo, '=====previousRouteInfo'); const [refresh, setRefresh] = useState(); - const [scanUrlFinish, setScanUrlFinish] = useState(false); useFocusEffect( useCallback(() => { @@ -44,27 +44,23 @@ const QrScanner: React.FC = () => { }, []), ); - const handleBarCodeScanned = useCallback( + const handleBarCodeScanned = useThrottleCallback( ({ data = '' }) => { if (typeof data !== 'string') return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); try { const str = prefixUrlWithProtocol(data.replace(/("|'|\s)/g, '')); - if (checkIsUrl(str)) { - if (scanUrlFinish) return; jumpToWebview({ item: { name: str, url: str, }, }); - setScanUrlFinish(true); return navigationService.goBack(); } const qrCodeData = expandQrData(JSON.parse(data)); - // if not currentNetwork if (currentNetwork !== qrCodeData.netWorkType) return invalidQRCode( From 52228bdc9d34d3c41999ed4be93ab808105c570f Mon Sep 17 00:00:00 2001 From: Ian-potter Date: Mon, 24 Jul 2023 15:36:06 +0800 Subject: [PATCH 436/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20open=20min?= =?UTF-8?q?imized=20prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/service/NotificationService/index.ts | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/web-extension-did/app/web/service/NotificationService/index.ts b/packages/web-extension-did/app/web/service/NotificationService/index.ts index 439fadd172..fc362c3fd5 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/index.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/index.ts @@ -11,6 +11,7 @@ export interface NotificationType { sendResponse?: SendResponseFun; message: PromptMessage & { externalLink?: string }; promptType?: CreatePromptType; + promptConfig?: chrome.windows.CreateData; } export interface CloseParams extends SendResponseParams { @@ -58,6 +59,8 @@ export default class NotificationService { showWindow = async (notification: Omit): Promise => { try { + const promptConfig = notification?.promptConfig; + const { height, width, top, left, isFullscreen } = await getPromptConfig({ message: notification.message, }); @@ -77,19 +80,28 @@ export default class NotificationService { } // create new notification popup - const popupWindow = await this.platform.openWindow({ - url, - height, - width, - type: 'popup', - focused: true, - state: isFullscreen ? 'fullscreen' : 'normal', - top, - left, - }); + let config: chrome.windows.CreateData = { url }; + if (promptConfig?.state === 'minimized') { + config = { ...config, ...promptConfig }; + } else { + config = { + ...config, + height, + width, + type: 'popup', + focused: true, + state: isFullscreen ? 'fullscreen' : 'normal', + top, + left, + ...promptConfig, + }; + } + + // create new notification popup + const popupWindow = await this.platform.openWindow(config); // Firefox currently ignores left/top for create, but it works for update - if (popupWindow.left !== left && popupWindow.state !== 'fullscreen') { + if (popupWindow.left !== left && popupWindow.state !== 'fullscreen' && popupWindow.state !== 'minimized') { if (!popupWindow.id) return; await this.platform.updateWindowPosition(popupWindow.id, left, top); } @@ -164,6 +176,7 @@ export default class NotificationService { openPrompt = ( message: NotificationType['message'], promptType: CreatePromptType = 'windows', + promptConfig?: NotificationType['promptConfig'], ): Promise => { return new Promise((resolve) => { console.log(message, 'openPrompt==message'); @@ -174,6 +187,7 @@ export default class NotificationService { }, message, promptType, + promptConfig, }; this.open(promptParam); }); From 3efebae7e9165e3aa2b467f7f23c476ffe148c06 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 24 Jul 2023 16:20:40 +0800 Subject: [PATCH 437/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/dapp.ts | 59 ++++++++++++++++++- .../js/dapp/dappMobileOperator.ts | 30 +++++++++- packages/mobile-app-did/js/utils/redux.ts | 6 +- packages/store/store-ca/dapp/actions.ts | 7 +++ packages/store/store-ca/dapp/slice.ts | 11 +++- packages/store/store-ca/dapp/type.ts | 2 + packages/types/index.ts | 2 + packages/types/session.ts | 15 +++++ packages/types/types-ca/dapp.ts | 2 + packages/utils/dapp/dappManager.ts | 5 ++ packages/utils/session.ts | 57 ++++++++++++++++++ 11 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 packages/types/session.ts create mode 100644 packages/utils/session.ts diff --git a/packages/hooks/hooks-ca/dapp.ts b/packages/hooks/hooks-ca/dapp.ts index bb1b692b30..b0ef3dc1c7 100644 --- a/packages/hooks/hooks-ca/dapp.ts +++ b/packages/hooks/hooks-ca/dapp.ts @@ -1,7 +1,13 @@ -import { useMemo } from 'react'; +import { useMemo, useCallback } from 'react'; import { useAppCASelector } from '.'; -import { useWallet } from './wallet'; - +import { useCurrentWalletInfo, useWallet } from './wallet'; +import { useAppCommonDispatch } from '../index'; +import { updateSessionInfo } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { useCurrentNetworkInfo } from './network'; +import { NetworkType } from '@portkey-wallet/types'; +import { SessionExpiredPlan, SessionInfo } from '@portkey-wallet/types/session'; +import { formatExpiredTime, signSession } from '@portkey-wallet/utils/session'; +import { AElfWallet } from '@portkey-wallet/types/aelf'; export const useDapp = () => useAppCASelector(state => state.dapp); export const useCurrentDappList = () => { @@ -11,3 +17,50 @@ export const useCurrentDappList = () => { return dappMap[currentNetwork]; }, [currentNetwork, dappMap]); }; + +export const useCurrentDappInfo = (origin: string) => { + const list = useCurrentDappList(); + return useMemo(() => list?.find(item => item.origin === origin), [list, origin]); +}; + +export const useUpdateSessionInfo = () => { + const dispatch = useAppCommonDispatch(); + const { networkType } = useCurrentNetworkInfo(); + const { caHash } = useCurrentWalletInfo(); + return useCallback( + (params: { networkType?: NetworkType; origin: string; expiredPlan?: SessionExpiredPlan; manager?: AElfWallet }) => { + if (!caHash) return; + let sessionInfo: SessionInfo | undefined = undefined; + if (params.expiredPlan) { + const { manager, expiredPlan } = params; + if (!manager?.keyPair) return; + const expiredTime = formatExpiredTime(expiredPlan); + + const baseSession = { + origin: params.origin, + expiredPlan, + expiredTime, + keyPair: manager.keyPair, + managerAddress: manager.address, + caHash, + }; + + const signature = signSession(baseSession); + sessionInfo = { + signature, + expiredPlan, + expiredTime, + }; + } + if (params.manager) delete params.manager; + return dispatch( + updateSessionInfo({ + ...params, + networkType: params.networkType || networkType, + sessionInfo, + }), + ); + }, + [caHash, dispatch, networkType], + ); +}; diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 76754bf987..f81b92f3fa 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -18,10 +18,11 @@ import { IDappOverlay } from './dappOverlay'; import { Operator } from '@portkey/providers'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { getContractBasic } from '@portkey-wallet/contracts/utils'; -import { getManagerAccount, getPin } from 'utils/redux'; +import { getCurrentCaHash, getManagerAccount, getPin } from 'utils/redux'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { isEqDapp } from '@portkey-wallet/utils/dapp/browser'; import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; +import { hasSessionInfoExpired, verifySession } from '@portkey-wallet/utils/session'; const SEND_METHOD: { [key: string]: true } = { [MethodsBase.SEND_TRANSACTION]: true, [MethodsBase.REQUEST_ACCOUNTS]: true, @@ -241,6 +242,11 @@ export default class DappMobileOperator extends Operator { method: keyof IDappOverlay; callBack: SendRequest; }) { + const validSession = await this.verifySessionInfo(); + + // valid session + if (validSession) return callBack(eventName, params); + // user confirm const response = await this.userConfirmation({ eventName, method, params }); if (response) return response; @@ -342,4 +348,26 @@ export default class DappMobileOperator extends Operator { const isActive = await this.isActive(); if (isActive) this.dappManager.updateDapp(dapp); }; + + public verifySessionInfo = async () => { + try { + const sessionInfo = await this.dappManager.getSessionInfo(this.dapp.origin); + const manager = getManager(); + const caHash = getCurrentCaHash(); + if (!manager?.keyPair || !caHash || !sessionInfo) return false; + const valid = verifySession({ + keyPair: manager.keyPair, + origin: this.dapp.origin, + managerAddress: manager.address, + caHash, + expiredPlan: sessionInfo.expiredPlan, + expiredTime: sessionInfo.expiredTime, + signature: sessionInfo.signature, + }); + if (!valid) return valid; + return hasSessionInfoExpired(sessionInfo); + } catch (error) { + return false; + } + }; } diff --git a/packages/mobile-app-did/js/utils/redux.ts b/packages/mobile-app-did/js/utils/redux.ts index ed81d3c992..509ce7a876 100644 --- a/packages/mobile-app-did/js/utils/redux.ts +++ b/packages/mobile-app-did/js/utils/redux.ts @@ -49,9 +49,13 @@ export const getManagerAccount = (password: string): AElfWallet | undefined => { }; export const isCurrentCaHash = (caHash: string) => { + return getCurrentCaHash() === caHash; +}; + +export const getCurrentCaHash = () => { const wallet = getWallet(); const { walletInfo, currentNetwork } = wallet || {}; const caInfo = walletInfo?.caInfo?.[currentNetwork]; const originChainId = wallet.originChainId || caInfo?.originChainId; - return caInfo?.[originChainId || DefaultChainId]?.caHash === caHash; + return caInfo?.[originChainId || DefaultChainId]?.caHash; }; diff --git a/packages/store/store-ca/dapp/actions.ts b/packages/store/store-ca/dapp/actions.ts index 14da89100d..b7e48556c2 100644 --- a/packages/store/store-ca/dapp/actions.ts +++ b/packages/store/store-ca/dapp/actions.ts @@ -1,6 +1,7 @@ import { NetworkType } from '@portkey-wallet/types'; import { createAction } from '@reduxjs/toolkit'; import { DappStoreItem } from './type'; +import { SessionInfo } from '@portkey-wallet/types/session'; export const addDapp = createAction<{ networkType: NetworkType; @@ -21,3 +22,9 @@ export const updateDapp = createAction<{ export const resetDappList = createAction('dapp/resetDappList'); export const resetDapp = createAction('dapp/resetDapp'); + +export const updateSessionInfo = createAction<{ + networkType: NetworkType; + origin: string; + sessionInfo?: SessionInfo; +}>('dapp/updateSessionInfo'); diff --git a/packages/store/store-ca/dapp/slice.ts b/packages/store/store-ca/dapp/slice.ts index d830b72635..d349d6839e 100644 --- a/packages/store/store-ca/dapp/slice.ts +++ b/packages/store/store-ca/dapp/slice.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { IDappStoreState } from './type'; import { createSlice } from '@reduxjs/toolkit'; -import { addDapp, removeDapp, resetDapp, resetDappList, updateDapp } from './actions'; +import { addDapp, removeDapp, resetDapp, resetDappList, updateDapp, updateSessionInfo } from './actions'; const initialState: IDappStoreState = { dappMap: {}, @@ -35,6 +35,15 @@ export const dappSlice = createSlice({ return item; }); }) + .addCase(updateSessionInfo, (state, action) => { + const { networkType, origin, sessionInfo } = action.payload; + const dappList = state.dappMap[networkType]; + if (!dappList || !dappList.some(item => item.origin === origin)) throw Error('origin does not exist'); + state.dappMap[networkType] = dappList.map(item => { + if (item.origin === origin) return { ...item, sessionInfo }; + return item; + }); + }) .addCase(resetDappList, (state, action) => { state.dappMap[action.payload] = []; }) diff --git a/packages/store/store-ca/dapp/type.ts b/packages/store/store-ca/dapp/type.ts index f888c6deee..c91b4d0f59 100644 --- a/packages/store/store-ca/dapp/type.ts +++ b/packages/store/store-ca/dapp/type.ts @@ -1,9 +1,11 @@ import { NetworkType } from '@portkey-wallet/types'; +import { SessionInfo } from '@portkey-wallet/types/session'; export type DappStoreItem = { origin: string; name?: string; icon?: string; + sessionInfo?: SessionInfo; }; export interface IDappStoreState { diff --git a/packages/types/index.ts b/packages/types/index.ts index 6ce2a42b21..675c5617cc 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -10,3 +10,5 @@ export type ConsoleLike = Pick { getState(): Promise; isLogged(): Promise; @@ -21,6 +22,7 @@ export interface IDappManager { getCaInfo(chainId: ChainId): Promise; networkType(): Promise; walletName(): Promise; + getSessionInfo(origin: string): Promise; } export interface IDappManagerStore { getState(): Promise; diff --git a/packages/utils/dapp/dappManager.ts b/packages/utils/dapp/dappManager.ts index 47c7deb0f7..01eb64be75 100644 --- a/packages/utils/dapp/dappManager.ts +++ b/packages/utils/dapp/dappManager.ts @@ -9,6 +9,7 @@ import { handleAccounts, handleChainIds, handleCurrentCAInfo, handleOriginInfo } import { isEqDapp } from './browser'; import { NetworkType } from '@portkey-wallet/types'; import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; +import { SessionInfo } from '@portkey-wallet/types/session'; export abstract class BaseDappManager { protected store: T; @@ -113,4 +114,8 @@ export abstract class DappManager async getRpcUrl(chainId: ChainId): Promise { return (await this.getChainInfo(chainId))?.endPoint; } + async getSessionInfo(origin: string): Promise { + const originInfo = await this.getOriginInfo(origin); + return originInfo?.sessionInfo; + } } diff --git a/packages/utils/session.ts b/packages/utils/session.ts new file mode 100644 index 0000000000..cb10b557dd --- /dev/null +++ b/packages/utils/session.ts @@ -0,0 +1,57 @@ +import type { ec } from 'elliptic'; +import { SessionExpiredPlan, SessionInfo } from '../types/session'; +import { Timestamp } from '../types'; +import AElf from 'aelf-sdk'; + +const HOUR = 60 * 60 * 1000; + +interface IBaseSessionParams { + origin: string; + managerAddress: string; + caHash: string; + expiredPlan: SessionExpiredPlan; + expiredTime: Timestamp; +} + +interface ISignSessionParams extends IBaseSessionParams { + keyPair: ec.KeyPair; +} + +interface IVerifySessionParams extends IBaseSessionParams { + keyPair: ec.KeyPair; + signature: string; +} +export function formatSession({ origin, managerAddress, caHash, expiredPlan, expiredTime }: IBaseSessionParams) { + const msg = JSON.stringify({ + origin, + managerAddress, + caHash, + expiredPlan, + expiredTime, + }); + return AElf.utils.sha256(msg); +} + +export function signSession(params: ISignSessionParams): string { + const signOptions = params.keyPair.sign(formatSession(params)); + return Buffer.from(signOptions.toDER()).toString('hex'); +} + +export function verifySession(params: IVerifySessionParams) { + return params.keyPair.verify(formatSession(params), params.signature); +} + +export function hasSessionInfoExpired(sessionInfo: SessionInfo) { + // always + if (sessionInfo.expiredPlan === SessionExpiredPlan.always) return true; + if (Date.now() < sessionInfo.expiredTime) return true; + return false; +} + +export function formatExpiredTime(plan: SessionExpiredPlan) { + if (typeof plan === 'number') { + return Date.now() + plan * HOUR; + } else { + return Date.now(); + } +} From 1b303242bcf5ee6af5e3e367bbfcc15c054eaa10 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 24 Jul 2023 17:54:35 +0800 Subject: [PATCH 438/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20lock=20dapp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/BrowserTab/index.tsx | 5 +++-- .../js/components/ProviderWebview/index.tsx | 13 +++++++++++-- .../mobile-app-did/js/dapp/dappMobileOperator.ts | 6 ++++++ packages/utils/check.ts | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/mobile-app-did/js/components/BrowserTab/index.tsx b/packages/mobile-app-did/js/components/BrowserTab/index.tsx index 451088ee1e..ead5bf2be3 100644 --- a/packages/mobile-app-did/js/components/BrowserTab/index.tsx +++ b/packages/mobile-app-did/js/components/BrowserTab/index.tsx @@ -46,11 +46,11 @@ const BrowserTab = forwardRef(function BrowserTab( ({ nativeEvent }: WebViewNavigationEvent | WebViewErrorEvent) => { if (!isDangerousLink(getProtocolAndHost(uri)) && !isApproved.current && autoApprove) { isApproved.current = true; - webViewRef.current?.autoApprove(); + if (!isHidden) webViewRef.current?.autoApprove(); } onLoadEnd?.(nativeEvent); }, - [autoApprove, uri, onLoadEnd], + [uri, autoApprove, isHidden, onLoadEnd], ); return ( (function BrowserTab( progressbarRef.current?.changeInnerBarWidth(nativeEvent.progress)} /> diff --git a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx index 1260a06e0b..c9f66887a8 100644 --- a/packages/mobile-app-did/js/components/ProviderWebview/index.tsx +++ b/packages/mobile-app-did/js/components/ProviderWebview/index.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, memo, useCallback, useImperativeHandle, useRef, useState } from 'react'; +import React, { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { StyleSheet } from 'react-native'; import WebView, { WebViewProps } from 'react-native-webview'; import useEffectOnce from 'hooks/useEffectOnce'; @@ -22,7 +22,12 @@ export interface IWebView { autoApprove: () => void; } -const ProviderWebview = forwardRef(function ProviderWebview(props, forward) { +const ProviderWebview = forwardRef< + IWebView | undefined, + WebViewProps & { + isHidden?: boolean; + } +>(function ProviderWebview(props, forward) { const webViewRef = useRef(null); const operatorRef = useRef(null); // Android will trigger onLoadEnd before onLoadStart, Mark start status. @@ -41,6 +46,10 @@ const ProviderWebview = forwardRef(function }; }); + useEffect(() => { + operatorRef.current?.setIsLockDapp(!!props.isHidden); + }, [props.isHidden]); + const initOperator = useCallback( (origin: string) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index f81b92f3fa..a342454503 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -54,6 +54,7 @@ export default class DappMobileOperator extends Operator { protected stream: IDappInteractionStream; protected dappManager: IDappManager; protected dappOverlay: IDappOverlay; + public isLockDapp?: boolean; constructor({ stream, origin, dappManager, dappOverlay }: DappMobileOperatorOptions) { super(stream); this.dapp = { origin }; @@ -318,6 +319,7 @@ export default class DappMobileOperator extends Operator { handleRequest = async (request: IRequestParams): Promise => { if (SEND_METHOD[request.method]) return this.handleSendRequest(request); + if (this.isLockDapp) return this.userDenied(request.eventName); return this.handleViewRequest(request); }; @@ -370,4 +372,8 @@ export default class DappMobileOperator extends Operator { return false; } }; + + public setIsLockDapp = (isLockDapp: boolean) => { + this.isLockDapp = isLockDapp; + }; } diff --git a/packages/utils/check.ts b/packages/utils/check.ts index 91787c13de..b2bc5f6f28 100644 --- a/packages/utils/check.ts +++ b/packages/utils/check.ts @@ -6,7 +6,7 @@ export enum EmailError { noAccount = 'Failed to log in with this email. Please use your login account.', } -export function checkEmail(email?: string): void | string { +export function checkEmail(email?: string): undefined | string { if (!email) return EmailError.noEmail; if (!isValidEmail(email)) return EmailError.invalidEmail; } From 3d28e9b7759bc03a21bd0694445df732ff3c716d Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Mon, 24 Jul 2023 17:58:45 +0800 Subject: [PATCH 439/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20createTime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/dapp.ts | 1 + packages/types/session.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/hooks/hooks-ca/dapp.ts b/packages/hooks/hooks-ca/dapp.ts index b0ef3dc1c7..d7743be671 100644 --- a/packages/hooks/hooks-ca/dapp.ts +++ b/packages/hooks/hooks-ca/dapp.ts @@ -47,6 +47,7 @@ export const useUpdateSessionInfo = () => { const signature = signSession(baseSession); sessionInfo = { + createTime: Date.now(), signature, expiredPlan, expiredTime, diff --git a/packages/types/session.ts b/packages/types/session.ts index a2d896061b..d3c5db9971 100644 --- a/packages/types/session.ts +++ b/packages/types/session.ts @@ -9,6 +9,7 @@ export enum SessionExpiredPlan { always = 'Infinity', } export type SessionInfo = { + createTime: Timestamp; expiredPlan: SessionExpiredPlan; expiredTime: Timestamp; signature: string; From 3c1da024bfddd15c4d76f3d4cc5f584635fcb3fd Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 24 Jul 2023 19:13:43 +0800 Subject: [PATCH 440/893] =?UTF-8?q?style:=20=F0=9F=92=84=20change=20discov?= =?UTF-8?q?er=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiscoverArchivedSection/index.tsx | 51 ++++++++++--------- .../components/NoDiscoverData/index.tsx | 23 +++++++-- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index e8731c9bd9..61a1f912f8 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -77,40 +77,40 @@ export function DiscoverArchivedSection() { Records - - See All - + + See All + - - + {index === ArchivedTabEnum.Bookmarks && ( + <> {bookmarkList?.length === 0 ? ( - + ) : ( {bookmarkList.map((item, idx) => ( onClickJump(item)}> - - {item?.name || item?.url} - + {item?.name || item?.url} ))} )} - - + + )} + {index === ArchivedTabEnum.History && ( + <> {recordsList?.length === 0 ? ( - + ) : ( {recordsList.map((item, idx) => ( onClickJump(item)}> - + {item?.name || item?.url} @@ -118,8 +118,8 @@ export function DiscoverArchivedSection() { ))} )} - - + + )} ); @@ -127,7 +127,6 @@ export function DiscoverArchivedSection() { const styles = StyleSheet.create({ wrap: { - height: pTd(144), marginBottom: pTd(16), ...GStyles.paddingArg(0, 20), }, @@ -145,25 +144,29 @@ const styles = StyleSheet.create({ width: '100%', overflow: 'hidden', borderRadius: pTd(6), + paddingVertical: pTd(20), + paddingHorizontal: pTd(12), + backgroundColor: defaultColors.bg1, flex: 1, }, tabListWrap: { - width: '100%', - height: '100%', - backgroundColor: defaultColors.bg1, - paddingHorizontal: pTd(12), flexDirection: 'row', overflow: 'hidden', }, tabItemWrap: { width: '25%', - justifyContent: 'center', + justifyContent: 'flex-start', alignItems: 'center', }, tabItemContent: { alignItems: 'center', - maxWidth: pTd(72), - height: pTd(76), - justifyContent: 'space-between', + justifyContent: 'flex-start', + }, + websiteName: { + marginTop: pTd(4), + textAlign: 'center', + }, + noData: { + height: pTd(60), }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx index 62449c5dc5..fdc1077696 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx @@ -13,10 +13,19 @@ export interface INoDiscoverDataProps { size?: 'small' | 'large'; location?: 'top' | 'center'; backgroundColor?: string; + style?: any; + iconStyle?: any; } const NoDiscoverData = (props: INoDiscoverDataProps) => { - const { type = 'noBookmarks', size = 'small', location = 'center', backgroundColor = defaultColors.bg1 } = props; + const { + type = 'noBookmarks', + size = 'small', + location = 'center', + backgroundColor = defaultColors.bg1, + style = {}, + iconStyle = {}, + } = props; const iconName = type === 'noBookmarks' ? 'no-bookmarks' : 'no-records'; const noDataText = type === 'noBookmarks' ? 'No Bookmarks' : 'No Records'; @@ -25,8 +34,16 @@ const NoDiscoverData = (props: INoDiscoverDataProps) => { }; return ( - - + + {noDataText} ); From 3ae8230fc0dbd865c7b3b59945a9edb89a36bf6f Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 24 Jul 2023 19:13:43 +0800 Subject: [PATCH 441/893] =?UTF-8?q?style:=20=F0=9F=92=84=20change=20discov?= =?UTF-8?q?er=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiscoverArchivedSection/index.tsx | 51 ++++++++++--------- .../components/NoDiscoverData/index.tsx | 23 +++++++-- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index e8731c9bd9..61a1f912f8 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -77,40 +77,40 @@ export function DiscoverArchivedSection() { Records - - See All - + + See All + - - + {index === ArchivedTabEnum.Bookmarks && ( + <> {bookmarkList?.length === 0 ? ( - + ) : ( {bookmarkList.map((item, idx) => ( onClickJump(item)}> - - {item?.name || item?.url} - + {item?.name || item?.url} ))} )} - - + + )} + {index === ArchivedTabEnum.History && ( + <> {recordsList?.length === 0 ? ( - + ) : ( {recordsList.map((item, idx) => ( onClickJump(item)}> - + {item?.name || item?.url} @@ -118,8 +118,8 @@ export function DiscoverArchivedSection() { ))} )} - - + + )} ); @@ -127,7 +127,6 @@ export function DiscoverArchivedSection() { const styles = StyleSheet.create({ wrap: { - height: pTd(144), marginBottom: pTd(16), ...GStyles.paddingArg(0, 20), }, @@ -145,25 +144,29 @@ const styles = StyleSheet.create({ width: '100%', overflow: 'hidden', borderRadius: pTd(6), + paddingVertical: pTd(20), + paddingHorizontal: pTd(12), + backgroundColor: defaultColors.bg1, flex: 1, }, tabListWrap: { - width: '100%', - height: '100%', - backgroundColor: defaultColors.bg1, - paddingHorizontal: pTd(12), flexDirection: 'row', overflow: 'hidden', }, tabItemWrap: { width: '25%', - justifyContent: 'center', + justifyContent: 'flex-start', alignItems: 'center', }, tabItemContent: { alignItems: 'center', - maxWidth: pTd(72), - height: pTd(76), - justifyContent: 'space-between', + justifyContent: 'flex-start', + }, + websiteName: { + marginTop: pTd(4), + textAlign: 'center', + }, + noData: { + height: pTd(60), }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx index 62449c5dc5..fdc1077696 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/NoDiscoverData/index.tsx @@ -13,10 +13,19 @@ export interface INoDiscoverDataProps { size?: 'small' | 'large'; location?: 'top' | 'center'; backgroundColor?: string; + style?: any; + iconStyle?: any; } const NoDiscoverData = (props: INoDiscoverDataProps) => { - const { type = 'noBookmarks', size = 'small', location = 'center', backgroundColor = defaultColors.bg1 } = props; + const { + type = 'noBookmarks', + size = 'small', + location = 'center', + backgroundColor = defaultColors.bg1, + style = {}, + iconStyle = {}, + } = props; const iconName = type === 'noBookmarks' ? 'no-bookmarks' : 'no-records'; const noDataText = type === 'noBookmarks' ? 'No Bookmarks' : 'No Records'; @@ -25,8 +34,16 @@ const NoDiscoverData = (props: INoDiscoverDataProps) => { }; return ( - - + + {noDataText} ); From b42590cc4544468238aa135bb09ff8640b37dfb2 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 24 Jul 2023 20:15:21 +0800 Subject: [PATCH 442/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20style=20line2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Discover/components/DiscoverArchivedSection/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 61a1f912f8..600d7c2f18 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -92,7 +92,9 @@ export function DiscoverArchivedSection() { onClickJump(item)}> - {item?.name || item?.url} + + {item?.name || item?.url} + ))} From 87949853f8bcd5f4e68acc3d242cc6b473bdfc37 Mon Sep 17 00:00:00 2001 From: ykx Date: Mon, 24 Jul 2023 20:39:15 +0800 Subject: [PATCH 443/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20select?= =?UTF-8?q?=20verifier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/verification/index.ts | 1 + .../app/web/Prompt/routes/index.tsx | 5 - .../app/web/hooks/useVerifier.ts | 136 ++++++++++++++++++ .../app/web/pages/RegisterStart/index.tsx | 77 +++++++++- .../app/web/pages/SelectVerifier/index.less | 13 -- .../app/web/pages/SelectVerifier/index.tsx | 108 -------------- .../app/web/pages/SetWalletPin/index.tsx | 2 +- .../app/web/pages/VerifierAccount/index.tsx | 2 +- 8 files changed, 211 insertions(+), 133 deletions(-) create mode 100644 packages/web-extension-did/app/web/hooks/useVerifier.ts delete mode 100644 packages/web-extension-did/app/web/pages/SelectVerifier/index.less delete mode 100644 packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx diff --git a/packages/api/api-did/verification/index.ts b/packages/api/api-did/verification/index.ts index 2888f07092..c350278677 100644 --- a/packages/api/api-did/verification/index.ts +++ b/packages/api/api-did/verification/index.ts @@ -10,4 +10,5 @@ export default { config: { method: 'GET' }, }, checkGoogleRecaptcha: '/api/app/account/isGoogleRecaptchaOpen', + getVerifierServer: 'api/app/account/getVerifierServer', } as const; diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index 8263b3b1b3..cb90391c29 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -1,5 +1,4 @@ import RegisterStart from 'pages/RegisterStart'; -import SelectVerifier from 'pages/SelectVerifier'; import { useRoutes } from 'react-router-dom'; import ScreenOpeningPage from 'pages/ScreenOpening'; import VerifierAccount from 'pages/VerifierAccount'; @@ -70,10 +69,6 @@ export const PageRouter = () => { path: '/register/start/:type', element: , }, - { - path: '/register/select-verifier', - element: , - }, { path: '/register/verifier-account', element: , diff --git a/packages/web-extension-did/app/web/hooks/useVerifier.ts b/packages/web-extension-did/app/web/hooks/useVerifier.ts new file mode 100644 index 0000000000..26b785f129 --- /dev/null +++ b/packages/web-extension-did/app/web/hooks/useVerifier.ts @@ -0,0 +1,136 @@ +import { verification } from 'utils/api'; +import { OperationTypeEnum, VerifierItem } from '@portkey-wallet/types/verifier'; +import { setCurrentGuardianAction } from '@portkey-wallet/store/store-ca/guardians/actions'; +import { handleError, verifyErrorHandler } from '@portkey/did-ui-react'; +import { useVerifyToken } from 'hooks/authentication'; +import InternalMessage from 'messages/InternalMessage'; +import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; +import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQueryResult'; +import { useAppDispatch, useLoading, useLoginInfo } from 'store/Provider/hooks'; +import { useCallback } from 'react'; +import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; +import { message } from 'antd'; +import { useNavigate } from 'react-router'; +import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; +import { setRegisterVerifierAction } from 'store/reducers/loginCache/actions'; + +/** + * Provides two verification processes + * @returns [checkAuth, sendVerifyCodeHandler] + * @desc checkAuth: for social login + * @desc sendVerifyCodeHandler: for Ordinary email address and mobile phone number + */ +const useCheckVerifier = () => { + const { setLoading } = useLoading(); + const navigate = useNavigate(); + const dispatch = useAppDispatch(); + const { loginAccount } = useLoginInfo(); + const originChainId = useOriginChainId(); + const { address: managerAddress } = useCurrentWalletInfo(); + const onManagerAddressAndQueryResult = useOnManagerAddressAndQueryResult('register'); + // Send verifier verification code + const sendVerifyCodeHandler = useCallback( + async (verifierItem: VerifierItem) => { + try { + if (!loginAccount || !LoginType[loginAccount.loginType] || !loginAccount.guardianAccount) + return message.error( + 'User registration information is invalid, please fill in the registration method again', + ); + if (!verifierItem.id || !verifierItem.name) return message.error('Can not get verification'); + + setLoading(true); + + const result = await verification.sendVerificationCode({ + params: { + guardianIdentifier: loginAccount.guardianAccount.replaceAll(' ', ''), + type: LoginType[loginAccount.loginType], + verifierId: verifierItem.id, + chainId: DefaultChainId, + operationType: OperationTypeEnum.register, + }, + }); + setLoading(false); + if (result.verifierSessionId) { + const _key = `${loginAccount.guardianAccount}&${verifierItem.name}`; + dispatch( + setCurrentGuardianAction({ + isLoginAccount: true, + verifier: verifierItem, + guardianAccount: loginAccount.guardianAccount, + guardianType: loginAccount.loginType, + verifierInfo: { + sessionId: result.verifierSessionId, + endPoint: result.endPoint, + }, + key: _key, + identifierHash: '', + salt: '', + }), + ); + navigate('/register/verifier-account', { state: 'register' }); + } + } catch (error: any) { + setLoading(false); + console.log(error, 'verifyHandler'); + const _error = verifyErrorHandler(error); + message.error(_error); + } + }, + [dispatch, loginAccount, navigate, setLoading], + ); + + const verifyToken = useVerifyToken(); + + const checkAuth = useCallback( + async (verifierItem: VerifierItem) => { + try { + setLoading(true); + if (!loginAccount?.loginType) throw 'loginType is invalid'; + const rst = await verifyToken(loginAccount.loginType, { + accessToken: loginAccount.authenticationInfo?.[loginAccount.guardianAccount || ''], + id: loginAccount.guardianAccount, + verifierId: verifierItem?.id, + chainId: originChainId, + operationType: OperationTypeEnum.register, + }); + dispatch( + setRegisterVerifierAction({ + verifierId: verifierItem?.id as string, + verificationDoc: rst.verificationDoc, + signature: rst.signature, + }), + ); + const res = await InternalMessage.payload(PortkeyMessageTypes.CHECK_WALLET_STATUS).send(); + setLoading(false); + if (managerAddress && res.data.privateKey) { + onManagerAddressAndQueryResult(res.data.privateKey, { + verifierId: verifierItem?.id as string, + verificationDoc: rst.verificationDoc, + signature: rst.signature, + }); + } else { + navigate('/login/set-pin/register'); + } + } catch (error) { + const msg = handleError(error); + message.error(msg); + setLoading(false); + } + }, + [ + dispatch, + loginAccount, + managerAddress, + navigate, + onManagerAddressAndQueryResult, + originChainId, + setLoading, + verifyToken, + ], + ); + + return [checkAuth, sendVerifyCodeHandler]; +}; + +export default useCheckVerifier; diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx index cf5d11525e..9646df7de7 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx @@ -6,7 +6,7 @@ import ScanCard from './components/ScanCard'; import SignCard from './components/SignCard'; import { useCurrentNetworkInfo, useIsMainnet, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCallback, useMemo, useRef, useState } from 'react'; -import { useAppDispatch, useLoading } from 'store/Provider/hooks'; +import { useAppDispatch, useLoading, useLoginInfo } from 'store/Provider/hooks'; import { setOriginChainId } from '@portkey-wallet/store/store-ca/wallet/actions'; import { NetworkType } from '@portkey-wallet/types'; import CommonSelect from 'components/CommonSelect1'; @@ -17,7 +17,7 @@ import { setLoginAccountAction } from 'store/reducers/loginCache/actions'; import { resetGuardians } from '@portkey-wallet/store/store-ca/guardians/actions'; import useGuardianList from 'hooks/useGuardianList'; import { handleErrorCode, handleErrorMessage } from '@portkey-wallet/utils'; -import { message } from 'antd'; +import { Button, message } from 'antd'; import { getHolderInfo } from 'utils/sandboxUtil/getHolderInfo'; import { SocialLoginFinishHandler } from 'types/wallet'; import { getGoogleUserInfo, parseAppleIdentityToken } from '@portkey-wallet/utils/authentication'; @@ -29,6 +29,11 @@ import CustomModal from 'pages/components/CustomModal'; import { IconType } from 'types/icon'; import LoginModal from './components/LoginModal'; import './index.less'; +import { request } from '@portkey-wallet/api/api-did'; +import useCheckVerifier from 'hooks/useVerifier'; +import CommonModal from 'components/CommonModal'; +import { useTranslation } from 'react-i18next'; +import { VerifierItem } from '@portkey-wallet/types/verifier'; export default function RegisterStart() { const { type } = useParams(); @@ -41,6 +46,17 @@ export default function RegisterStart() { const changeNetworkModalText = useChangeNetworkText(); const isMainnet = useIsMainnet(); const [open, setOpen] = useState(); + const { t } = useTranslation(); + const { loginAccount } = useLoginInfo(); + const [checkAuth, sendVerifyCodeHandler] = useCheckVerifier(); + const [openSendVerifyCode, setOpenSendVerifyCode] = useState(false); + const [verifierItem, setVerifierItem] = useState({ + id: '', + name: '', + imageUrl: '', + endPoints: [], + verifierAddresses: [], + }); const networkList = useNetworkList(); @@ -128,15 +144,45 @@ export default function RegisterStart() { [dispatch], ); + // According to the login type, execute different verifier judgment logic + const confirmRegisterOrLogin = useCallback(async () => { + switch (loginAccount?.loginType) { + case LoginType.Apple: + case LoginType.Google: + checkAuth(verifierItem); + break; + default: + setOpenSendVerifyCode(true); + break; + } + }, [checkAuth, loginAccount?.loginType, verifierItem]); + + const timer = useRef(); const onSignFinish = useCallback( - (data: LoginInfo) => { + async (data: LoginInfo) => { dispatch(setOriginChainId(DefaultChainId)); saveState(data); dispatch(resetGuardians()); - navigate('/register/select-verifier'); + + setLoading(true, 'Allocating verifier on-chain...'); + timer.current = setTimeout(() => { + clearTimeout(timer.current); + setLoading(false); + }, 2000); + + // Get the assigned verifier data from the backend api + const verifierReq = await request.verify.getVerifierServer({ + params: { + chainId: DefaultChainId, + }, + }); + + setVerifierItem(verifierReq); + confirmRegisterOrLogin(); + setLoading(false); }, - [dispatch, navigate, saveState, setLoading], + [confirmRegisterOrLogin, dispatch, saveState, setLoading], ); const onLoginFinish = useCallback( @@ -269,8 +315,29 @@ export default function RegisterStart() { if (!loginInfoRef.current) return setOpen(false); if (isHasAccount?.current) return onLoginFinish(loginInfoRef.current); onSignFinish(loginInfoRef.current); + setOpen(false); }} /> + {loginAccount && ( + setOpen(false)}> +

    + {`${t('verificationCodeTip1', { verifier: verifierItem?.name })} `} + {loginAccount.guardianAccount} + {` ${t('verificationCodeTip2', { type: LoginType[loginAccount.loginType] })}`} +

    +
    + + +
    +
    + )}
    ); } diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.less b/packages/web-extension-did/app/web/pages/SelectVerifier/index.less deleted file mode 100644 index 1b56927c2e..0000000000 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.less +++ /dev/null @@ -1,13 +0,0 @@ -@import '../../assets/theme/color.less'; - -.select-verifier-wrapper { - .portkey-ui-verifier-selector { - width: 350px; - margin: auto; - } - -.select-verifier-description { - width: 600px; - margin: auto; - } -} diff --git a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx b/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx deleted file mode 100644 index 6142edb002..0000000000 --- a/packages/web-extension-did/app/web/pages/SelectVerifier/index.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { setCurrentGuardianAction } from '@portkey-wallet/store/store-ca/guardians/actions'; -import { message } from 'antd'; -import { useCallback, useMemo } from 'react'; -import { useNavigate } from 'react-router'; -import { useAppDispatch, useGuardiansInfo, useLoginInfo } from 'store/Provider/hooks'; -import PortKeyTitle from 'pages/components/PortKeyTitle'; -import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; -import { setRegisterVerifierAction } from 'store/reducers/loginCache/actions'; -import { useCurrentWallet, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { VerifierSelect } from '@portkey/did-ui-react'; -import { VerifierItem } from '@portkey-wallet/types/verifier'; -import InternalMessage from 'messages/InternalMessage'; -import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; -import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQueryResult'; -import './index.less'; - -interface ConfirmResultInfo { - verifier: VerifierItem; - verifierSessionId?: string; - verificationDoc?: string; - signature?: string; -} - -export default function SelectVerifier() { - const { loginAccount } = useLoginInfo(); - const { verifierMap } = useGuardiansInfo(); - const navigate = useNavigate(); - const dispatch = useAppDispatch(); - const originChainId = useOriginChainId(); - const { walletInfo } = useCurrentWallet(); - const onManagerAddressAndQueryResult = useOnManagerAddressAndQueryResult('register'); - - const onConfirm = useCallback( - async (result: ConfirmResultInfo) => { - console.log(result, 'result==onConfirm'); - if (!loginAccount) - return message.error('User registration information is invalid, please fill in the registration method again'); - if (result.verifierSessionId) { - const _key = `${loginAccount.guardianAccount}&${result.verifier.name}`; - dispatch( - setCurrentGuardianAction({ - isLoginAccount: true, - verifier: result.verifier, - guardianAccount: loginAccount.guardianAccount, - guardianType: loginAccount.loginType, - verifierInfo: { - sessionId: result.verifierSessionId, - }, - key: _key, - identifierHash: '', - salt: '', - }), - ); - navigate('/register/verifier-account', { state: 'register' }); - } else if (result.verificationDoc && result.signature) { - dispatch( - setRegisterVerifierAction({ - verifierId: result.verifier.id, - verificationDoc: result.verificationDoc, - signature: result.signature, - }), - ); - const res = await InternalMessage.payload(PortkeyMessageTypes.CHECK_WALLET_STATUS).send(); - if (walletInfo.address && res.data.privateKey) { - onManagerAddressAndQueryResult(res.data.privateKey, { - verifierId: result.verifier.id, - verificationDoc: result.verificationDoc, - signature: result.signature, - }); - } else { - navigate('/login/set-pin/register'); - } - } else { - message.error('Verification failed, please try again later'); - } - }, - [dispatch, loginAccount, navigate, onManagerAddressAndQueryResult, walletInfo.address], - ); - - const authorized = useMemo( - () => loginAccount?.authenticationInfo?.[loginAccount?.guardianAccount || ''], - [loginAccount?.authenticationInfo, loginAccount?.guardianAccount], - ); - - const verifierList = useMemo(() => Object.values(verifierMap ?? {}), [verifierMap]); - - return ( -
    - navigate('/register/start')} /> - - { - console.log(err, 'VerifierSelect==='); - }} - /> -
    - ); -} diff --git a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx index f3a34278ef..10d7139dfd 100644 --- a/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx +++ b/packages/web-extension-did/app/web/pages/SetWalletPin/index.tsx @@ -170,7 +170,7 @@ export default function SetWalletPin() { const backHandler = useCallback(async () => { switch (state) { case 'register': - navigate('/register/select-verifier'); + navigate('/register/start/create'); break; case 'login': navigate('/login/guardian-approval'); diff --git a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx index b4b1905f89..f1788872a1 100644 --- a/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx +++ b/packages/web-extension-did/app/web/pages/VerifierAccount/index.tsx @@ -176,7 +176,7 @@ export default function VerifierAccount() { const handleBack = useCallback(() => { if (state === 'register') { - navigate('/register/select-verifier'); + navigate('/register/start/create'); } else if (state === 'login') { navigate('/login/guardian-approval'); } else if (state === 'guardians/add' && !userGuardianStatus?.[opGuardian?.key || '']?.signature) { From 9d0022097958c811655a319ed850ec99553d48dd Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 25 Jul 2023 10:43:02 +0800 Subject: [PATCH 444/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dapp=20session?= =?UTF-8?q?=20UI=20&=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/Popup/routes/index.tsx | 5 + .../app/web/Prompt/routes/index.tsx | 15 ++- .../web-extension-did/app/web/manifest.json | 2 +- .../app/web/pages/ConnectWallet/index.less | 3 +- .../app/web/pages/ConnectWallet/index.tsx | 15 ++- .../app/web/pages/DappAutoTx/index.less | 39 +++++++ .../app/web/pages/DappAutoTx/index.tsx | 20 ++++ .../ConnectedSites/Popup/index.tsx | 20 ++-- .../ConnectedSites/Prompt/index.tsx | 19 ++-- .../SiteDetail/Popup/index.less | 15 +++ .../ConnectedSites/SiteDetail/Popup/index.tsx | 22 ++++ .../SiteDetail/Prompt/index.less | 11 ++ .../SiteDetail/Prompt/index.tsx | 15 +++ .../ConnectedSites/SiteDetail/index.tsx | 30 +++++ .../WalletSecurity/ConnectedSites/index.less | 61 +++++++++++ .../WalletSecurity/ConnectedSites/index.tsx | 59 +++++++--- .../components/ConnectedSiteList/index.less | 80 -------------- .../components/ConnectedSiteList/index.tsx | 46 -------- .../components/SiteItem/index.less | 103 ++++++++++++++++++ .../components/SiteItem/index.tsx | 102 +++++++++++++++++ .../pages/components/DappSession/index.less | 35 ++++++ .../pages/components/DappSession/index.tsx | 65 +++++++++++ packages/web-extension-did/package.json | 2 +- 23 files changed, 621 insertions(+), 163 deletions(-) create mode 100644 packages/web-extension-did/app/web/pages/DappAutoTx/index.less create mode 100644 packages/web-extension-did/app/web/pages/DappAutoTx/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.less create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.less create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.less delete mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less delete mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.less create mode 100644 packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.tsx create mode 100644 packages/web-extension-did/app/web/pages/components/DappSession/index.less create mode 100644 packages/web-extension-did/app/web/pages/components/DappSession/index.tsx diff --git a/packages/web-extension-did/app/web/Popup/routes/index.tsx b/packages/web-extension-did/app/web/Popup/routes/index.tsx index 6727014842..515d1b8fa4 100644 --- a/packages/web-extension-did/app/web/Popup/routes/index.tsx +++ b/packages/web-extension-did/app/web/Popup/routes/index.tsx @@ -32,6 +32,7 @@ import SwitchNetworks from 'pages/Wallet/SwitchNetwork'; import WalletName from 'pages/Wallet/WalletName'; import RecentDetail from 'pages/Send/components/RecentDetail'; import ConnectedSites from 'pages/WalletSecurity/ConnectedSites'; +import SiteDetail from 'pages/WalletSecurity/ConnectedSites/SiteDetail'; export const PageRouter = () => useRoutes([ @@ -171,6 +172,10 @@ export const PageRouter = () => path: '/setting/wallet-security/connected-sites', element: , }, + { + path: '/setting/wallet-security/connected-sites/:origin', + element: , + }, { path: '/unlock', element: , diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index 8263b3b1b3..5d56636428 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -47,8 +47,10 @@ import RecentDetail from 'pages/Send/components/RecentDetail'; import Permission from 'pages/Permission'; import ConnectWallet from 'pages/ConnectWallet'; import ConnectedSites from 'pages/WalletSecurity/ConnectedSites'; +import SiteDetail from 'pages/WalletSecurity/ConnectedSites/SiteDetail'; import SendTransactions from 'pages/SendTransactions'; import GetSignature from 'pages/GetSignature'; +import DappAutoTx from 'pages/DappAutoTx'; export const PageRouter = () => { const { isNotLessThan768 } = useCommonState(); @@ -176,7 +178,10 @@ export const PageRouter = () => { path: '/get-signature', element: , }, - + { + path: '/auto-tx', + element: , + }, { path: '*', element: , @@ -283,6 +288,10 @@ export const PageRouter = () => { path: '/setting/wallet-security/connected-sites', element: , }, + { + path: '/setting/wallet-security/connected-sites/:origin', + element: , + }, ], }, ], @@ -381,6 +390,10 @@ export const PageRouter = () => { path: '/setting/wallet-security/connected-sites', element: , }, + { + path: '/setting/wallet-security/connected-sites/:origin', + element: , + }, ]; const promptRoutes = useRoutes([...commonRoutes, ...settingPromptRoutes]); diff --git a/packages/web-extension-did/app/web/manifest.json b/packages/web-extension-did/app/web/manifest.json index bded597c90..37a68b90fd 100644 --- a/packages/web-extension-did/app/web/manifest.json +++ b/packages/web-extension-did/app/web/manifest.json @@ -4,7 +4,7 @@ "extension_pages": "script-src 'self'; object-src 'self'" }, "name": "Portkey: DID & Crypto & NFT", - "version": "1.3.4", + "version": "1.3.5", "description": "Identity System for Social Recover and Asset Management Tool", "icons": { "16": "assets/images/extension_logo.png", diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less index 9ff957d724..76a2ecf1f7 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.less +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.less @@ -38,7 +38,7 @@ } .allow { width: 100%; - margin-bottom: 40px; + margin-bottom: 32px; font-size: 12px; line-height: 16px; color: @font-13; @@ -72,6 +72,7 @@ } .btn{ width: 100%; + margin-top: 40px; .@{app-prefix}-btn { width: 156px; height: 48px; diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index 550dfe59a0..e3a037b31f 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -3,11 +3,12 @@ import { Button } from 'antd'; import CustomSvg from 'components/CustomSvg'; import usePromptSearch from 'hooks/usePromptSearch'; import ImageDisplay from 'pages/components/ImageDisplay'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; +import DappSession from 'pages/components/DappSession'; import './index.less'; const allowItem = ['view wallet balance and activities', 'send you transaction requests']; @@ -18,6 +19,8 @@ export default function ConnectWallet() { const dispatch = useAppDispatch(); const { currentNetwork } = useWalletInfo(); const disabled = useMemo(() => !detail.appHref, [detail]); + const [open, setOpen] = useState(false); + const [exp, setExp] = useState(''); const renderSite = useMemo( () => @@ -47,6 +50,15 @@ export default function ConnectWallet() { [t], ); + const handleSessionChange = useCallback( + (flag: boolean, time: string) => { + setOpen(flag); + setExp(time); + console.log(open, exp); + }, + [exp, open], + ); + const handleSign = useCallback(async () => { try { dispatch( @@ -73,6 +85,7 @@ export default function ConnectWallet() { {renderSite}
    {t('Connect with Portkey')}
    {renderAllow} +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx index 479ab75da0..f063066078 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Popup/index.tsx @@ -1,17 +1,15 @@ import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import { BaseHeaderProps } from 'types/UI'; -import ConnectedSiteList, { IConnectedSiteListProps } from 'pages/WalletSecurity/components/ConnectedSiteList'; +import MenuList, { IMenuItemProps } from 'pages/components/MenuList'; +import { useTranslation } from 'react-i18next'; import './index.less'; -export default function ConnectedSitesPopup({ - headerTitle, - goBack, - list, - onDisconnect, -}: BaseHeaderProps & IConnectedSiteListProps) { +export default function ConnectedSitesPopup({ headerTitle, goBack, list }: BaseHeaderProps & IMenuItemProps) { + const { t } = useTranslation(); + return ( -
    +
    } />
    - + {list.length === 0 ? ( +
    {t('No Connected Sites')}
    + ) : ( + + )}
    ); } diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx index 834bdd6c1f..cce8717264 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/Prompt/index.tsx @@ -1,20 +1,21 @@ import SecondPageHeader from 'pages/components/SecondPageHeader'; -import ConnectedSiteList, { IConnectedSiteListProps } from 'pages/WalletSecurity/components/ConnectedSiteList'; import { Outlet } from 'react-router'; import { BaseHeaderProps } from 'types/UI'; +import MenuList, { IMenuItemProps } from 'pages/components/MenuList'; +import { useTranslation } from 'react-i18next'; import './index.less'; -export default function ConnectedSitesPrompt({ - headerTitle, - goBack, - list, - onDisconnect, -}: BaseHeaderProps & IConnectedSiteListProps) { +export default function ConnectedSitesPrompt({ headerTitle, goBack, list }: BaseHeaderProps & IMenuItemProps) { + const { t } = useTranslation(); return ( -
    +
    - + {list.length === 0 ? ( +
    {t('No Connected Sites')}
    + ) : ( + + )}
    diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.less new file mode 100644 index 0000000000..5f99d1fd51 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.less @@ -0,0 +1,15 @@ +@import '../../../../../assets/theme/color.less'; + +.site-detail-popup { + .site-detail-header { + overflow: hidden; + width: 100%; + padding-top: 16px; + background-color: @bg-13; + border-bottom: 1px solid @border-2; + z-index: 10; + } + .site-item { + height: calc(100% - 97px); + } +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.tsx new file mode 100644 index 0000000000..b2ecf651d4 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Popup/index.tsx @@ -0,0 +1,22 @@ +import BackHeader from 'components/BackHeader'; +import CustomSvg from 'components/CustomSvg'; +import { BaseHeaderProps } from 'types/UI'; +import SiteItem, { ISiteItemProps } from 'pages/WalletSecurity/components/SiteItem'; +import './index.less'; + +export default function SiteDetailPopup({ headerTitle, goBack, siteItem }: BaseHeaderProps & ISiteItemProps) { + return ( +
    +
    + } + /> +
    +
    + +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.less new file mode 100644 index 0000000000..f5f67abb1e --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.less @@ -0,0 +1,11 @@ +@import '../../../../../assets/theme/color.less'; + +.site-detail-prompt { + position: relative; + width: 100%; + flex: 1; + border-left: 1px solid @border-2; + .site-item { + height: calc(100% - 73px); + } +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.tsx new file mode 100644 index 0000000000..bef8813d21 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/Prompt/index.tsx @@ -0,0 +1,15 @@ +import SecondPageHeader from 'pages/components/SecondPageHeader'; +import SiteItem, { ISiteItemProps } from 'pages/WalletSecurity/components/SiteItem'; +import { BaseHeaderProps } from 'types/UI'; +import './index.less'; + +export default function SiteDetailPrompt({ headerTitle, goBack, siteItem }: BaseHeaderProps & ISiteItemProps) { + return ( +
    + +
    + +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/index.tsx new file mode 100644 index 0000000000..4fc9ee15a7 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/SiteDetail/index.tsx @@ -0,0 +1,30 @@ +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router'; +import { useCommonState } from 'store/Provider/hooks'; +import SiteDetailPrompt from './Prompt'; +import SiteDetailPopup from './Popup'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; + +const siteItem: DappStoreItem = { + name: 'name', + origin: 'origin', + icon: 'icon', +}; + +export default function SiteDetail() { + const { t } = useTranslation(); + const navigate = useNavigate(); + const { isNotLessThan768 } = useCommonState(); + + const title = t('Details'); + const handleBack = useCallback(() => { + navigate('/setting/wallet-security/connected-sites'); + }, [navigate]); + + return isNotLessThan768 ? ( + + ) : ( + + ); +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.less new file mode 100644 index 0000000000..bba42ea4de --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.less @@ -0,0 +1,61 @@ +@import '../../../assets/theme/color.less'; +@import '../../../assets/theme/constants.less'; + +.connected-sites { + .menu-item .menu-item-title { + height: 44px; + } + + .content { + align-items: center; + width: 100%; + .icon { + flex-shrink: 0; + width: 32px; + border-radius: 50%; + .dappdefault-icon { + width: 32px; + height: 32px; + svg { + width: 32px; + height: 32px; + } + } + } + .desc { + flex: 1; + width: 0; + word-wrap: break-word; + margin-left: 16px; + .text { + .text-overflow(1); + } + .name { + display: flex; + font-size: 16px; + line-height: 22px; + color: @font-11; + .dapp-name { + .text-overflow(1); + } + .custom-svg { + margin-left: 4px; + } + } + .origin { + margin-top: 2px; + font-size: 14px; + line-height: 20px; + color: @font-12; + } + } + } + + .no-data { + margin-top: 160px; + font-size: 14px; + line-height: 20px; + color: @font-12; + } + +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx index e91e6a8bb3..7b2e132afa 100644 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/ConnectedSites/index.tsx @@ -1,27 +1,58 @@ import { useTranslation } from 'react-i18next'; import { useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router'; -import { useAppDispatch, useDapp, useWalletInfo } from 'store/Provider/hooks'; +// import { useDapp, useWalletInfo } from 'store/Provider/hooks'; import SitesPopup from './Popup'; import SitesPrompt from './Prompt'; import { useCommonState } from 'store/Provider/hooks'; -import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; -import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { MenuItemInfo } from 'pages/components/MenuList'; +import ImageDisplay from 'pages/components/ImageDisplay'; +import './index.less'; +import CustomSvg from 'components/CustomSvg'; + +const mockDapp = [ + { + origin: 'origin', + name: 'name', + icon: 'icon', + }, + { + origin: 'origin1', + name: 'name1', + icon: 'icon1', + }, +]; export default function ConnectedSites() { const { t } = useTranslation(); const navigate = useNavigate(); - const { currentNetwork } = useWalletInfo(); - const { dappMap } = useDapp(); - const currentDapp = useMemo(() => dappMap[currentNetwork] || [], [currentNetwork, dappMap]); + // const { currentNetwork } = useWalletInfo(); + // const { dappMap } = useDapp(); + // const currentDapp = useMemo(() => dappMap[currentNetwork] || [], [currentNetwork, dappMap]); const { isNotLessThan768 } = useCommonState(); - const dispatch = useAppDispatch(); + const isSafeOrigin = useCallback((origin: string) => origin.startsWith('https://'), []); - const handleDisConnect = useCallback( - (item: DappStoreItem) => { - dispatch(removeDapp({ networkType: currentNetwork, origin: item.origin || '' })); - }, - [currentNetwork, dispatch], + const showDappList: MenuItemInfo[] = useMemo( + () => + mockDapp?.map((dapp) => ({ + key: dapp.origin, + element: ( +
    + +
    +
    + {dapp.name} + +
    +
    {dapp.origin}
    +
    +
    + ), + click: () => { + navigate(`/setting/wallet-security/connected-sites/${dapp.origin}`); + }, + })), + [isSafeOrigin, navigate], ); const title = t('Connected Sites'); @@ -30,8 +61,8 @@ export default function ConnectedSites() { }, [navigate]); return isNotLessThan768 ? ( - + ) : ( - + ); } diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less deleted file mode 100644 index d01d16dec0..0000000000 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.less +++ /dev/null @@ -1,80 +0,0 @@ -@import '../../../../assets/theme/color.less'; -@import '../../../../assets/theme/constants.less'; - -.connected-site-list { - .connected-site-item { - height: 92px; - padding: 24px; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid @bg-15; - color: black; - .content { - align-items: center; - width: 100%; - .icon { - flex-shrink: 0; - width: 32px; - border-radius: 50%; - .dappdefault-icon { - width: 32px; - height: 32px; - svg { - width: 32px; - height: 32px; - } - } - } - .desc { - flex: 1; - width: 0; - word-wrap: break-word; - margin-left: 16px; - .text { - .text-overflow(1); - } - .name { - display: flex; - font-size: 16px; - line-height: 22px; - color: @font-11; - .dapp-name { - .text-overflow(1); - } - .custom-svg { - margin-left: 4px; - } - } - .origin { - margin-top: 2px; - font-size: 14px; - line-height: 20px; - color: @font-12; - } - } - } - .btn { - width: 79px; - height: 24px; - margin-left: 16px; - .@{app-prefix}-btn-text { - width: 79px; - height: 24px; - font-size: 12px; - line-height: 16px; - color: @font-14; - background-color: @bg-11; - border-radius: 24px; - border: 1px solid @border-7; - text-shadow: none; - } - } - } -} - -.no-data { - margin-top: 160px; - font-size: 14px; - line-height: 20px; - color: @font-12; -} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx deleted file mode 100644 index 2da73f30d3..0000000000 --- a/packages/web-extension-did/app/web/pages/WalletSecurity/components/ConnectedSiteList/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; -import { Button } from 'antd'; -import { useCallback, useMemo } from 'react'; -import ImageDisplay from 'pages/components/ImageDisplay'; -import CustomSvg from 'components/CustomSvg'; -import './index.less'; - -export interface IConnectedSiteListProps { - list: DappStoreItem[]; - onDisconnect: (item: DappStoreItem) => void; -} - -export default function ConnectedSiteList({ list, onDisconnect }: IConnectedSiteListProps) { - const { t } = useTranslation(); - const isSafeOrigin = useCallback((origin: string) => origin.startsWith('https://'), []); - - const renderList = useMemo( - () => ( -
    - {list.map((item) => ( -
    -
    - -
    -
    - {item.name} - -
    -
    {item.origin}
    -
    -
    -
    - -
    -
    - ))} -
    - ), - [isSafeOrigin, list, onDisconnect, t], - ); - - return list.length === 0 ?
    {t('No Connected Sites')}
    : renderList; -} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.less b/packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.less new file mode 100644 index 0000000000..a88fe32cde --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.less @@ -0,0 +1,103 @@ +@import '../../../../assets/theme/color.less'; +@import '../../../../assets/theme/constants.less'; + +.site-item-content { + height: 100%; + font-size: 14px; + line-height: 20px; + color: @font-1; + .site-dapp { + margin: 32px auto 24px; + font-size: 16px; + line-height: 22px; + color: @font-11; + .dappdefault-icon{ + width: 64px; + height: 64px; + margin-bottom: 8px; + border-radius: 50%; + svg { + width: 100%; + height: 100%; + } + } + .origin { + margin-top: 2px; + color: @font-9; + .dappwarn-icon { + width: 12px; + height: 12px; + margin-right: 4px; + } + } + } + .content-item { + margin: 0 24px 24px; + gap: 8px; + .control { + width: 100%; + height: 48px; + padding: 14px 16px; + text-align: center; + border: 1px solid @border-1; + border-radius: 6px; + background-color: @bg-13; + color: @font-11; + } + } + .session-tip { + padding: 24px 24px 0; + display: flex; + flex-direction: column; + border-top: 1px solid @border-1; + .label { + margin-bottom: 8px; + font-size: 18px; + line-height: 22px; + color: @font-11; + } + .value { + color: @font-13; + } + } + .session-switch { + align-items: center; + padding: 24px; + .switch { + width: 43px; + height: 24px; + border: 1px solid @border-1; + background: white; + .@{app-prefix}-switch-handle::before { + background: #68737e; + } + } + .@{app-prefix}-switch-checked { + border: 1px solid #5b8ef4; + .@{app-prefix}-switch-handle::before { + background: #5b8ef4; + } + } + .@{app-prefix}-switch:focus { + box-shadow: none; + } + .status { + line-height: 22px; + color: @font-13; + font-size: 16px; + margin-left: 16px; + } + } + .btn-wrap { + border-top: 1px solid @border-1; + padding: 16px; + button { + height: 48px; + border-radius: 22px; + border: 1px solid @border-7; + color: @font-14; + font-size: 16px; + line-height: 22px; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.tsx b/packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.tsx new file mode 100644 index 0000000000..7578157de0 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/WalletSecurity/components/SiteItem/index.tsx @@ -0,0 +1,102 @@ +import { Button, Switch } from 'antd'; +import CustomSvg from 'components/CustomSvg'; +import { useTranslation } from 'react-i18next'; +// import { dateFormat } from 'utils'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { useCallback } from 'react'; +import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { useNavigate } from 'react-router'; +import './index.less'; +import CustomSelect from 'pages/components/CustomSelect'; + +export interface ISiteItemProps { + siteItem: DappStoreItem; +} + +export default function SiteItem({ siteItem }: ISiteItemProps) { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const { currentNetwork } = useWalletInfo(); + const navigate = useNavigate(); + + const handleSwitch = useCallback(() => { + // + }, []); + + const handleDisconnect = useCallback(() => { + dispatch(removeDapp({ networkType: currentNetwork, origin: siteItem.origin || '' })); + navigate('/setting/wallet-security/connected-sites'); + }, [currentNetwork, dispatch, navigate, siteItem.origin]); + + const SessionExpiredPlan = [ + { + value: '1', + children: '1 hour', + }, + { + value: '3', + children: '3 hour', + }, + { + value: '12', + children: '12 hour', + }, + { + value: '24', + children: '24 hour', + }, + { + value: 'never', + children: 'Never', + }, + ]; + + const handleSessionChange = useCallback(() => { + // + }, []); + + return ( +
    +
    +
    + +
    {siteItem.name}
    +
    + + {siteItem.origin} +
    +
    +
    +
    {t('Connected time')}
    +
    2023-07-30 23:59:59
    +
    +
    + {t('Remember me to skip authentication')} + + {t( + "Once enabled, your session key will automatically approve all requests from this DApp, on this device only. You won't see pop-up notifications asking for your approvals until the session key expires. This feature is automatically off when you disconnect from the DApp or when the session key expires. You can also manually disable it or change the expiration time.", + )} + +
    +
    + + Close +
    +
    +
    {t('Connected time')}
    + +
    +
    +
    {t('Connected time')}
    +
    2023-07-30 23:59:59
    +
    +
    +
    + +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/components/DappSession/index.less b/packages/web-extension-did/app/web/pages/components/DappSession/index.less new file mode 100644 index 0000000000..4b2387cdb7 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/DappSession/index.less @@ -0,0 +1,35 @@ + +@import '../../../assets/theme/color.less'; + +.dapp-session { + gap: 16px; + .switch-wrap { + align-items: center; + } + .switch { + font-size: 16px; + line-height: 22px; + color: @font-1; + width: 43px; + height: 24px; + border: 1px solid @border-1; + background: @bg-11; + .@{app-prefix}-switch-handle::before { + background: @bg-18; + } + } + .@{app-prefix}-switch-checked { + border: 1px solid @border-3; + .@{app-prefix}-switch-handle::before { + background: @bg-7; + } + } + .@{app-prefix}-switch:focus { + box-shadow: none; + } + .tip { + font-size: 12px; + line-height: 16px; + color: @font-13; + } +} diff --git a/packages/web-extension-did/app/web/pages/components/DappSession/index.tsx b/packages/web-extension-did/app/web/pages/components/DappSession/index.tsx new file mode 100644 index 0000000000..f76ec9c36f --- /dev/null +++ b/packages/web-extension-did/app/web/pages/components/DappSession/index.tsx @@ -0,0 +1,65 @@ +import { Switch } from 'antd'; +import CustomSelect from '../CustomSelect'; +import { useCallback, useState } from 'react'; +import './index.less'; + +export interface IDappSessionProps { + onChange: (open: boolean, exp: string) => void; +} + +const SessionExpiredPlan = [ + { + value: '1', + children: '1 hour', + }, + { + value: '3', + children: '3 hour', + }, + { + value: '12', + children: '12 hour', + }, + { + value: '24', + children: '24 hour', + }, + { + value: 'never', + children: 'Never', + }, +]; +export default function DappSession({ onChange }: IDappSessionProps) { + const [open, setOpen] = useState(false); + + const handleSwitch = useCallback( + (value: boolean) => { + setOpen(value); + const exp = value ? 'never' : ''; + onChange(value, exp); + }, + [onChange], + ); + + const handleSessionChange = useCallback( + (value: string) => { + onChange(open, value); + }, + [onChange, open], + ); + + return ( +
    +
    +
    Remember me to skip authentication
    + +
    + {open && ( +
    + +
    + )} +
    {`Once enabled, your session key will automatically approve all requests from this DApp, on this device only. You won't see pop-up notifications asking for your approvals until the session key expires. This feature is automatically off when you disconnect from the DApp or when the session key expires. You can also manually disable it or change the expiration time.`}
    +
    + ); +} diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index 808ae115dd..a3857f3eed 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -1,6 +1,6 @@ { "name": "web-extension-did", - "version": "1.3.4", + "version": "1.3.5", "description": "web-extension-did", "private": true, "dependencies": { From adf4659af96b798bbadc030c0e3d3aee816376db Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 25 Jul 2023 11:12:55 +0800 Subject: [PATCH 445/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20connected=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/dapp/slice.ts | 4 ++-- packages/store/store-ca/dapp/type.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/store/store-ca/dapp/slice.ts b/packages/store/store-ca/dapp/slice.ts index d349d6839e..e4d32329ca 100644 --- a/packages/store/store-ca/dapp/slice.ts +++ b/packages/store/store-ca/dapp/slice.ts @@ -17,7 +17,7 @@ export const dappSlice = createSlice({ let dappList = state.dappMap[networkType]; if (!dappList) dappList = []; if (dappList.some(item => item.origin === dapp.origin)) throw Error('dapp already exists'); - dappList.push(dapp); + dappList.push({ ...dapp, connectedTime: Date.now() }); state.dappMap[networkType] = dappList; }) .addCase(removeDapp, (state, action) => { @@ -31,7 +31,7 @@ export const dappSlice = createSlice({ const dappList = state.dappMap[networkType]; if (!dappList || !dappList.some(item => item.origin === origin)) throw Error('origin does not exist'); state.dappMap[networkType] = dappList.map(item => { - if (item.origin === origin) return dapp; + if (item.origin === origin) return { ...item, ...dapp }; return item; }); }) diff --git a/packages/store/store-ca/dapp/type.ts b/packages/store/store-ca/dapp/type.ts index c91b4d0f59..94f210b15f 100644 --- a/packages/store/store-ca/dapp/type.ts +++ b/packages/store/store-ca/dapp/type.ts @@ -1,4 +1,4 @@ -import { NetworkType } from '@portkey-wallet/types'; +import { NetworkType, Timestamp } from '@portkey-wallet/types'; import { SessionInfo } from '@portkey-wallet/types/session'; export type DappStoreItem = { @@ -6,6 +6,7 @@ export type DappStoreItem = { name?: string; icon?: string; sessionInfo?: SessionInfo; + connectedTime?: Timestamp; }; export interface IDappStoreState { From 43bd4a93e7f6ce27aac253697508426616867917 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 25 Jul 2023 16:37:03 +0800 Subject: [PATCH 446/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=202s=20fetch?= =?UTF-8?q?=20verifier=20loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/hooks/useVerifier.ts | 34 +++---- .../app/web/pages/RegisterStart/index.tsx | 88 +++++++++++-------- 2 files changed, 65 insertions(+), 57 deletions(-) diff --git a/packages/web-extension-did/app/web/hooks/useVerifier.ts b/packages/web-extension-did/app/web/hooks/useVerifier.ts index 26b785f129..a78901ec74 100644 --- a/packages/web-extension-did/app/web/hooks/useVerifier.ts +++ b/packages/web-extension-did/app/web/hooks/useVerifier.ts @@ -7,13 +7,14 @@ import InternalMessage from 'messages/InternalMessage'; import { PortkeyMessageTypes } from 'messages/InternalMessageTypes'; import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useOnManagerAddressAndQueryResult } from 'hooks/useOnManagerAddressAndQueryResult'; -import { useAppDispatch, useLoading, useLoginInfo } from 'store/Provider/hooks'; +import { useAppDispatch, useLoading } from 'store/Provider/hooks'; import { useCallback } from 'react'; import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; import { message } from 'antd'; import { useNavigate } from 'react-router'; import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; import { setRegisterVerifierAction } from 'store/reducers/loginCache/actions'; +import { LoginInfo } from 'store/reducers/loginCache/type'; /** * Provides two verification processes @@ -25,19 +26,19 @@ const useCheckVerifier = () => { const { setLoading } = useLoading(); const navigate = useNavigate(); const dispatch = useAppDispatch(); - const { loginAccount } = useLoginInfo(); const originChainId = useOriginChainId(); const { address: managerAddress } = useCurrentWalletInfo(); const onManagerAddressAndQueryResult = useOnManagerAddressAndQueryResult('register'); + // Send verifier verification code const sendVerifyCodeHandler = useCallback( - async (verifierItem: VerifierItem) => { + async (verifierItem: VerifierItem, loginAccount?: LoginInfo) => { try { - if (!loginAccount || !LoginType[loginAccount.loginType] || !loginAccount.guardianAccount) + if (!loginAccount || !LoginType[loginAccount?.loginType] || !loginAccount?.guardianAccount) return message.error( 'User registration information is invalid, please fill in the registration method again', ); - if (!verifierItem.id || !verifierItem.name) return message.error('Can not get verification'); + if (!verifierItem?.id || !verifierItem?.name) return message.error('Can not get verification'); setLoading(true); @@ -77,26 +78,28 @@ const useCheckVerifier = () => { message.error(_error); } }, - [dispatch, loginAccount, navigate, setLoading], + [dispatch, navigate, setLoading], ); const verifyToken = useVerifyToken(); const checkAuth = useCallback( - async (verifierItem: VerifierItem) => { + async (verifierItem: VerifierItem, loginAccount?: LoginInfo) => { try { setLoading(true); if (!loginAccount?.loginType) throw 'loginType is invalid'; + if (!verifierItem?.id || !verifierItem?.name) return message.error('Can not get verification'); + const rst = await verifyToken(loginAccount.loginType, { accessToken: loginAccount.authenticationInfo?.[loginAccount.guardianAccount || ''], id: loginAccount.guardianAccount, - verifierId: verifierItem?.id, + verifierId: verifierItem.id, chainId: originChainId, operationType: OperationTypeEnum.register, }); dispatch( setRegisterVerifierAction({ - verifierId: verifierItem?.id as string, + verifierId: verifierItem.id as string, verificationDoc: rst.verificationDoc, signature: rst.signature, }), @@ -105,7 +108,7 @@ const useCheckVerifier = () => { setLoading(false); if (managerAddress && res.data.privateKey) { onManagerAddressAndQueryResult(res.data.privateKey, { - verifierId: verifierItem?.id as string, + verifierId: verifierItem.id as string, verificationDoc: rst.verificationDoc, signature: rst.signature, }); @@ -118,16 +121,7 @@ const useCheckVerifier = () => { setLoading(false); } }, - [ - dispatch, - loginAccount, - managerAddress, - navigate, - onManagerAddressAndQueryResult, - originChainId, - setLoading, - verifyToken, - ], + [dispatch, managerAddress, navigate, onManagerAddressAndQueryResult, originChainId, setLoading, verifyToken], ); return [checkAuth, sendVerifyCodeHandler]; diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx index 9646df7de7..60560f7435 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx @@ -6,7 +6,7 @@ import ScanCard from './components/ScanCard'; import SignCard from './components/SignCard'; import { useCurrentNetworkInfo, useIsMainnet, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; import { useCallback, useMemo, useRef, useState } from 'react'; -import { useAppDispatch, useLoading, useLoginInfo } from 'store/Provider/hooks'; +import { useAppDispatch, useLoading } from 'store/Provider/hooks'; import { setOriginChainId } from '@portkey-wallet/store/store-ca/wallet/actions'; import { NetworkType } from '@portkey-wallet/types'; import CommonSelect from 'components/CommonSelect1'; @@ -47,16 +47,6 @@ export default function RegisterStart() { const isMainnet = useIsMainnet(); const [open, setOpen] = useState(); const { t } = useTranslation(); - const { loginAccount } = useLoginInfo(); - const [checkAuth, sendVerifyCodeHandler] = useCheckVerifier(); - const [openSendVerifyCode, setOpenSendVerifyCode] = useState(false); - const [verifierItem, setVerifierItem] = useState({ - id: '', - name: '', - imageUrl: '', - endPoints: [], - verifierAddresses: [], - }); const networkList = useNetworkList(); @@ -139,23 +129,42 @@ export default function RegisterStart() { const saveState = useCallback( (data: LoginInfo) => { + // update page data + loginAccountRef.current = data; + setLoginAccount(data); + // update store data dispatch(setLoginAccountAction(data)); }, [dispatch], ); + const [openSendVerifyCode, setOpenSendVerifyCode] = useState(false); + const [verifierItem, setVerifierItem] = useState({ + id: '', + name: '', + imageUrl: '', + endPoints: [], + verifierAddresses: [], + }); + const loginAccountRef = useRef(); + const [loginAccount, setLoginAccount] = useState(); + const [checkAuth, sendVerifyCodeHandler] = useCheckVerifier(); + // According to the login type, execute different verifier judgment logic - const confirmRegisterOrLogin = useCallback(async () => { - switch (loginAccount?.loginType) { - case LoginType.Apple: - case LoginType.Google: - checkAuth(verifierItem); - break; - default: - setOpenSendVerifyCode(true); - break; - } - }, [checkAuth, loginAccount?.loginType, verifierItem]); + const confirmRegisterOrLogin = useCallback( + async (data: LoginInfo, verifierItem: VerifierItem) => { + switch (data?.loginType) { + case LoginType.Apple: + case LoginType.Google: + checkAuth(verifierItem, data); + break; + default: + setOpenSendVerifyCode(true); + break; + } + }, + [checkAuth], + ); const timer = useRef(); const onSignFinish = useCallback( @@ -165,22 +174,27 @@ export default function RegisterStart() { dispatch(resetGuardians()); setLoading(true, 'Allocating verifier on-chain...'); - timer.current = setTimeout(() => { - clearTimeout(timer.current); - setLoading(false); - }, 2000); - // Get the assigned verifier data from the backend api - const verifierReq = await request.verify.getVerifierServer({ - params: { - chainId: DefaultChainId, - }, + const loadingTimeout = new Promise((resolve) => { + timer.current = setTimeout(() => { + clearTimeout(timer.current); + setLoading(false); + resolve('timeout down'); + }, 2000); }); - setVerifierItem(verifierReq); - confirmRegisterOrLogin(); + // Get the assigned verifier data from the backend api and guaranteed loading display 2s + const [verifierReq, _timeout] = await Promise.all([ + request.verify.getVerifierServer({ + params: { + chainId: DefaultChainId, + }, + }), + loadingTimeout, + ]); - setLoading(false); + setVerifierItem(verifierReq); + confirmRegisterOrLogin(data, verifierReq); }, [confirmRegisterOrLogin, dispatch, saveState, setLoading], ); @@ -324,15 +338,15 @@ export default function RegisterStart() { closable={false} open={openSendVerifyCode} width={320} - onCancel={() => setOpen(false)}> + onCancel={() => setOpenSendVerifyCode(false)}>

    {`${t('verificationCodeTip1', { verifier: verifierItem?.name })} `} {loginAccount.guardianAccount} {` ${t('verificationCodeTip2', { type: LoginType[loginAccount.loginType] })}`}

    - - +
    From 7db9a850b2263c1d22fd26b31b9cfbc03b70c485 Mon Sep 17 00:00:00 2001 From: Portkey-David <120542595+Portkey-David@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:20:53 +0800 Subject: [PATCH 447/893] Update index.tsx --- packages/mobile-app-did/js/pages/QrScanner/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index adf8aea35e..d28543b6c3 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -49,12 +49,12 @@ const QrScanner: React.FC = () => { if (typeof data !== 'string') return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); try { - const str = prefixUrlWithProtocol(data.replace(/("|'|\s)/g, '')); + const str = data.replace(/("|'|\s)/g, ''); if (checkIsUrl(str)) { jumpToWebview({ item: { - name: str, - url: str, + name: prefixUrlWithProtocol(str), + url: prefixUrlWithProtocol(str), }, }); return navigationService.goBack(); From 2db6b15d5287c58bd00b3dc80ba2625fe0a55568 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 25 Jul 2023 20:26:54 +0800 Subject: [PATCH 448/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20fix=20decimals=20?= =?UTF-8?q?params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web-extension-did/app/web/pages/SendTransactions/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx index b8efb81b2e..2614e02e64 100644 --- a/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx +++ b/packages/web-extension-did/app/web/pages/SendTransactions/index.tsx @@ -171,7 +171,7 @@ export default function SendTransactions() { const renderTransfer = useMemo(() => { const { symbol, amount } = txParams.paramsOption || {}; - const decimals = symbol === defaultToken.symbol ? defaultToken.symbol : tokenDecimals; + const decimals = symbol === defaultToken.symbol ? defaultToken.decimals : tokenDecimals; return (
    From ee121431993bc4206ead33dbdb99301860a75ff1 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 25 Jul 2023 21:06:54 +0800 Subject: [PATCH 449/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20remember=20me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/dapp.ts | 15 ++ packages/hooks/hooks-ca/dapp.ts | 11 ++ .../mobile-app-did/js/assets/theme/GStyles.ts | 3 + .../js/components/ModalBody/index.tsx | 55 +++++-- .../js/components/RemeberMe/index.tsx | 148 ++++++++++++++++++ .../TabsDrawer/TabsDrawerContent.tsx | 2 +- .../components/WalletInfoOverlay/index.tsx | 59 ++++++- .../dapp/components/ConnectOverlay/index.tsx | 59 +++++-- .../components/OverlayBottomSection/index.tsx | 56 +++++++ .../js/pages/DashBoard/Card/index.tsx | 5 +- .../DiscoverArchivedSection/index.tsx | 5 +- .../WalletSecurity/Dapp/DappDetail/index.tsx | 148 ++++++++++++++++++ .../Dapp/components/DappListItem/index.tsx | 77 +++++++++ .../js/pages/My/WalletSecurity/Dapp/index.tsx | 77 ++------- .../js/pages/My/WalletSecurity/Dapp/router.ts | 15 ++ .../js/pages/My/WalletSecurity/index.tsx | 2 +- .../js/pages/My/WalletSecurity/router.ts | 7 +- .../js/pages/QrScanner/index.tsx | 8 +- packages/utils/dapp/browser.ts | 13 ++ packages/utils/session.ts | 2 + 20 files changed, 652 insertions(+), 115 deletions(-) create mode 100644 packages/mobile-app-did/js/components/RemeberMe/index.tsx create mode 100644 packages/mobile-app-did/js/dapp/components/OverlayBottomSection/index.tsx create mode 100644 packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx create mode 100644 packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx create mode 100644 packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/router.ts diff --git a/packages/constants/constants-ca/dapp.ts b/packages/constants/constants-ca/dapp.ts index 4dbd57f49b..de465a52e3 100644 --- a/packages/constants/constants-ca/dapp.ts +++ b/packages/constants/constants-ca/dapp.ts @@ -1 +1,16 @@ +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; + export const CA_METHOD_WHITELIST = ['ManagerForwardCall', 'ManagerTransfer']; + +export const SessionKeyMap = { + [SessionExpiredPlan.hour1]: '1 hours', + [SessionExpiredPlan.hour3]: '3 hours', + [SessionExpiredPlan.hour12]: '12 hours', + [SessionExpiredPlan.hour24]: '24 hours', + [SessionExpiredPlan.always]: 'Never', +}; + +export const SessionKeyArray = Object.entries(SessionKeyMap).map(([k, v]) => ({ + value: k === SessionExpiredPlan.always ? k : Number(k), + label: v, +})); diff --git a/packages/hooks/hooks-ca/dapp.ts b/packages/hooks/hooks-ca/dapp.ts index d7743be671..de045e2dbf 100644 --- a/packages/hooks/hooks-ca/dapp.ts +++ b/packages/hooks/hooks-ca/dapp.ts @@ -18,6 +18,17 @@ export const useCurrentDappList = () => { }, [currentNetwork, dappMap]); }; +export const useIsInCurrentDappList = () => { + const list = useCurrentDappList(); + + return useCallback( + (origin: string) => { + return list?.some(ele => ele.origin === origin.trim()); + }, + [list], + ); +}; + export const useCurrentDappInfo = (origin: string) => { const list = useCurrentDappList(); return useMemo(() => list?.find(item => item.origin === origin), [list, origin]); diff --git a/packages/mobile-app-did/js/assets/theme/GStyles.ts b/packages/mobile-app-did/js/assets/theme/GStyles.ts index 0db2938e55..747cb007d3 100644 --- a/packages/mobile-app-did/js/assets/theme/GStyles.ts +++ b/packages/mobile-app-did/js/assets/theme/GStyles.ts @@ -35,6 +35,9 @@ export default { alignEnd: { alignSelf: 'flex-end', }, + flexCenter: { + justifyContent: 'center', + }, flexEnd: { justifyContent: 'flex-end', }, diff --git a/packages/mobile-app-did/js/components/ModalBody/index.tsx b/packages/mobile-app-did/js/components/ModalBody/index.tsx index a4cf1a6be1..64acab80ae 100644 --- a/packages/mobile-app-did/js/components/ModalBody/index.tsx +++ b/packages/mobile-app-did/js/components/ModalBody/index.tsx @@ -15,9 +15,12 @@ import { CommonButtonProps } from 'components/CommonButton'; export interface ModalBodyProps extends ViewProps { title?: string; + isShowLeftBackIcon?: boolean; + isShowRightCloseIcon?: boolean; modalBodyType?: 'center' | 'bottom'; style?: ViewStyle; onClose?: () => void; + onBack?: () => void; bottomButtonGroup?: { onPress?: () => void; type?: CommonButtonProps['type']; @@ -28,7 +31,16 @@ export interface ModalBodyProps extends ViewProps { } export const ModalBody: React.FC = props => { - const { modalBodyType, title = '', children, style = {}, onClose, bottomButtonGroup } = props; + const { + modalBodyType, + isShowRightCloseIcon = true, + isShowLeftBackIcon = false, + title = '', + children, + style = {}, + onClose, + bottomButtonGroup, + } = props; const gStyles = useGStyles(); @@ -36,19 +48,33 @@ export const ModalBody: React.FC = props => { return ( + {isShowLeftBackIcon && ( + { + onClose?.(); + Keyboard.dismiss(); + OverlayModal.hide(); + }}> + + + )} {title} - { - onClose?.(); - Keyboard.dismiss(); - OverlayModal.hide(); - }}> - - + {isShowRightCloseIcon && ( + { + onClose?.(); + Keyboard.dismiss(); + OverlayModal.hide(); + }}> + + + )} {children} {!!bottomButtonGroup && ( @@ -83,6 +109,12 @@ export const styles = StyleSheet.create({ paddingTop: pTd(16), paddingBottom: pTd(16), }, + leftIcon: { + ...GStyles.paddingArg(17, 20), + position: 'absolute', + left: 0, + zIndex: 10000, + }, titleStyle: { lineHeight: pTd(22), width: '100%', @@ -92,6 +124,7 @@ export const styles = StyleSheet.create({ ...GStyles.paddingArg(21, 20), position: 'absolute', right: 0, + zIndex: 10000, }, headerRow: { paddingTop: pTd(14), diff --git a/packages/mobile-app-did/js/components/RemeberMe/index.tsx b/packages/mobile-app-did/js/components/RemeberMe/index.tsx new file mode 100644 index 0000000000..9337c7ce65 --- /dev/null +++ b/packages/mobile-app-did/js/components/RemeberMe/index.tsx @@ -0,0 +1,148 @@ +import React, { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react'; +import { Keyboard, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { TextL, TextM } from 'components/CommonText'; +import OverlayModal from 'components/OverlayModal'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import { ModalBody } from 'components/ModalBody'; +import { defaultColors } from 'assets/theme'; +import { FontStyles } from 'assets/theme/styles'; +import { useLanguage } from 'i18n/hooks'; +import fonts from 'assets/theme/fonts'; +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; +import { useCheckSiteIsInBlackList } from 'hooks/discover'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { SessionKeyMap, SessionKeyArray } from '@portkey-wallet/constants/constants-ca/dapp'; +import GStyles from 'assets/theme/GStyles'; + +type DappPeriodOverlayProps = { + value: SessionExpiredPlan; + onCancel?: () => void; + onConfirm: (value: SessionExpiredPlan) => void; +}; + +export type RememberInfoType = { + isRemember: boolean; + value: SessionExpiredPlan; +}; + +type RememberMeProps = { + dappInfo: DappStoreItem; + rememberInfo: RememberInfoType; + setRememberMeInfo: Dispatch>; +}; + +function DappPeriodOverlay(props: DappPeriodOverlayProps) { + const { value, onConfirm } = props; + const { t } = useLanguage(); + + const onPressItem = useCallback( + (v: SessionExpiredPlan) => { + if (String(value) === String(v)) return; + onConfirm(v); + OverlayModal.hide(); + }, + [onConfirm, value], + ); + + return ( + + + + {t( + "Once enabled, your session key will automatically approve all requests from this DApp, on this device only. You won't see pop-up notifications asking for your approvals until the session key expires. This feature is automatically off when you disconnect from the DApp or when the session key expires. You can also manually disable it or change the expiration time.", + )} + + {t('Session key expiration')} + {SessionKeyArray.map(ele => ( + onPressItem(ele?.value)}> + {ele.label} + {value === ele.value && } + + ))} + + + ); +} + +export const showDappPeriodOverlay = (props: RememberMeProps) => { + const { rememberInfo, setRememberMeInfo } = props; + + Keyboard.dismiss(); + OverlayModal.show( + { + setRememberMeInfo(pre => ({ ...pre, value })); + }} + />, + { + position: 'bottom', + }, + ); +}; + +export const RememberMe = (props: RememberMeProps) => { + const { dappInfo, rememberInfo, setRememberMeInfo } = props; + const checkOrigin = useCheckSiteIsInBlackList(); + if (checkOrigin(dappInfo.origin)) return null; + + return ( + + setRememberMeInfo(pre => ({ ...pre, isRemember: !pre.isRemember }))}> + + + + + Remember me to skip authentication for + showDappPeriodOverlay(props)}> + {SessionKeyMap[rememberInfo.value || SessionExpiredPlan.hour1]} + + + + + ); +}; + +const styles = StyleSheet.create({ + rememberWrap: { + display: 'flex', + flexDirection: 'row', + }, + selectedIcon: { + marginRight: pTd(8), + }, + text: { + flex: 1, + }, + label: { + paddingRight: pTd(100), + }, +}); + +const Overlay = StyleSheet.create({ + wrapStyle: { + paddingHorizontal: pTd(20), + flex: 1, + }, + title: { + alignSelf: 'center', + marginVertical: 16, + }, + itemRow: { + height: 72, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: defaultColors.border6, + }, + tips: { + color: defaultColors.font3, + marginBottom: pTd(24), + }, +}); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index 08095d656f..a2c8bd70fa 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -75,7 +75,7 @@ const TabsDrawerContent: React.FC = () => { if (activeTabId) return ( - showWalletInfo()}> + showWalletInfo({ tabInfo: activeItem })}> { +type MyWalletModalType = { + tabInfo: ITabItem; +}; + +const MyWalletModal = ({ tabInfo }: MyWalletModalType) => { const { t } = useLanguage(); + const checkDapp = useIsInCurrentDappList(); + const dispatch = useAppDispatch(); const caInfo = useCurrentCaInfo(); const { walletName, currentNetwork } = useWallet(); const defaultToken = useDefaultToken(); + const [showDisconnect, setShowDisconnect] = useState(false); + const { accountToken: { accountTokenList }, } = useAppCASelector(state => state.assets); @@ -42,6 +56,20 @@ const MyWalletModal = () => { .filter(item => !!item); }, [accountTokenList, caInfo, defaultToken.symbol]); + const disconnectDapp = useCallback(() => { + try { + dispatch(removeDapp({ networkType: currentNetwork, origin: getOrigin(tabInfo.url) })); + OverlayModal.hide(); + } catch (error) { + console.log(error); + } + }, [currentNetwork, dispatch, tabInfo.url]); + + useEffect(() => { + const result = checkDapp(getOrigin(tabInfo.url)); + setShowDisconnect(!!result); + }, [checkDapp, tabInfo.url]); + return ( @@ -57,7 +85,7 @@ const MyWalletModal = () => { - + {`${formatAmountShow(divDecimals(item?.balance, item?.decimals))} ${item?.symbol || '0'}`} @@ -66,14 +94,21 @@ const MyWalletModal = () => { ))} + + {showDisconnect && ( + + + Disconnect + + + )} ); }; -export const showWalletInfo = () => { - OverlayModal.show(, { +export const showWalletInfo = (props: MyWalletModalType) => { + OverlayModal.show(, { position: 'bottom', - containerStyle: { backgroundColor: defaultColors.bg6 }, }); }; @@ -112,5 +147,13 @@ const styles = StyleSheet.create({ itemChainInfo: { marginTop: pTd(4), }, - itemBalance: {}, + btnWrap: { + height: pTd(48), + width: '100%', + }, + buttonContainer: { + width: screenWidth, + position: 'absolute', + bottom: 0, + }, }); diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 1aa543fc82..35b23e5acd 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import OverlayModal from 'components/OverlayModal'; import { StyleSheet, View } from 'react-native'; import { defaultColors } from 'assets/theme'; @@ -9,7 +9,7 @@ import { ModalBody } from 'components/ModalBody'; import { TextL, TextM, TextS } from 'components/CommonText'; import { useCurrentCaInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { CAInfo } from '@portkey-wallet/types/types-ca/wallet'; -import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; +import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr, sleep } from '@portkey-wallet/utils'; import { ChainId } from '@portkey-wallet/types'; import { useAppCASelector } from '@portkey-wallet/hooks/hooks-ca'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; @@ -20,6 +20,12 @@ import { useGStyles } from 'assets/theme/useGStyles'; import { CommonButtonProps } from 'components/CommonButton'; import DappInfoSection from '../DappInfoSection'; import { useDefaultToken } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { RememberInfoType, RememberMe } from 'components/RemeberMe'; +import { OverlayBottomSection } from '../OverlayBottomSection'; +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; +import { useUpdateSessionInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import { usePin } from 'hooks/store'; +import { getManagerAccount } from 'utils/redux'; type ConnectModalType = { dappInfo: DappStoreItem; @@ -30,10 +36,17 @@ type ConnectModalType = { const ConnectModal = (props: ConnectModalType) => { const { dappInfo, onReject, onApprove } = props; const { t } = useLanguage(); + const pin = usePin(); const defaultToken = useDefaultToken(); const caInfo = useCurrentCaInfo(); const { walletName, currentNetwork } = useWallet(); const gStyles = useGStyles(); + const upDateSessionInfo = useUpdateSessionInfo(); + + const [rememberInfo, setRememberMeInfo] = useState({ + isRemember: false, + value: SessionExpiredPlan.hour1, + }); const { accountToken: { accountTokenList }, @@ -54,7 +67,7 @@ const ConnectModal = (props: ConnectModalType) => { return list; }, [accountTokenList, caInfo, defaultToken.symbol]); - const buttonList = useMemo( + const ButtonList = useMemo( () => [ { title: t('Reject'), @@ -67,17 +80,27 @@ const ConnectModal = (props: ConnectModalType) => { { title: t('Approve'), type: 'primary' as CommonButtonProps['type'], - onPress: () => { + onPress: async () => { onApprove?.(); OverlayModal.hide(); + + await sleep(500); + if (!pin) return; + if (rememberInfo.isRemember) { + upDateSessionInfo({ + manager: getManagerAccount(pin), + origin: dappInfo.origin, + expiredPlan: SessionExpiredPlan.hour24, + }); + } }, }, ], - [onApprove, onReject, t], + [dappInfo.origin, onApprove, onReject, pin, rememberInfo.isRemember, t, upDateSessionInfo], ); return ( - + {t('Wallet')} @@ -99,6 +122,9 @@ const ConnectModal = (props: ConnectModalType) => { ))} + + + ); }; @@ -157,15 +183,22 @@ const styles = StyleSheet.create({ itemChainInfo: { marginTop: pTd(4), }, - - buttonGroup: { - width: '100%', - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - }, btn: { width: pTd(160), height: pTd(44), }, + + buttonGroup: { + backgroundColor: defaultColors.bg1, + position: 'absolute', + bottom: 0, + ...GStyles.paddingArg(10, 20, 16, 20), + }, + buttonStyle: { + height: pTd(48), + fontSize: pTd(18), + }, + buttonTitleStyle: { + fontSize: pTd(16), + }, }); diff --git a/packages/mobile-app-did/js/dapp/components/OverlayBottomSection/index.tsx b/packages/mobile-app-did/js/dapp/components/OverlayBottomSection/index.tsx new file mode 100644 index 0000000000..e4dfdeb6d7 --- /dev/null +++ b/packages/mobile-app-did/js/dapp/components/OverlayBottomSection/index.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { View, ViewProps } from 'react-native'; +import { StyleSheet } from 'react-native'; +import { pTd } from 'utils/unit'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import ButtonRow from 'components/ButtonRow'; +import { CommonButtonProps } from 'components/CommonButton'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; + +export interface OverlayBottomSectionProps extends ViewProps { + children?: React.ReactNode; + bottomButtonGroup?: { + onPress?: () => void; + type?: CommonButtonProps['type']; + title: string; + loading?: CommonButtonProps['loading']; + disabled?: boolean; + }[]; +} + +export const OverlayBottomSection: React.FC = props => { + const { children, bottomButtonGroup } = props; + + return ( + + {children} + + + ); +}; + +export const styles = StyleSheet.create({ + groupWrap: { + width: screenWidth, + backgroundColor: defaultColors.bg1, + position: 'absolute', + bottom: 0, + ...GStyles.paddingArg(10, 20, 16, 20), + }, + buttonGroup: { + backgroundColor: defaultColors.bg1, + }, + buttonStyle: { + height: pTd(48), + fontSize: pTd(18), + }, + buttonTitleStyle: { + fontSize: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx index 733a7f6bc1..835580d277 100644 --- a/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx +++ b/packages/mobile-app-did/js/pages/DashBoard/Card/index.tsx @@ -18,7 +18,7 @@ import BuyButton from 'components/BuyButton'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { useAccountBalanceUSD } from '@portkey-wallet/hooks/hooks-ca/balances'; import FaucetButton from 'components/FaucetButton'; -import { useBuyButtonShow, useRememberMeBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; const Card: React.FC = () => { const { t } = useLanguage(); @@ -28,9 +28,6 @@ const Card: React.FC = () => { const [, requestQrPermission] = useQrScanPermission(); const { isBuyButtonShow } = useBuyButtonShow(); - const list = useRememberMeBlackList(); - console.log('list!!!!', list); - const showDialog = useCallback( () => ActionSheet.alert({ diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 61a1f912f8..0dd368ba32 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -6,7 +6,6 @@ import { useBookmarkList, useDiscoverJumpWithNetWork, useRecordsList } from 'hoo import React, { useCallback, useMemo } from 'react'; import { StyleSheet, View, TouchableOpacity } from 'react-native'; import { pTd } from 'utils/unit'; -import { TabView } from '@rneui/base'; import DiscoverWebsiteImage from '../DiscoverWebsiteImage'; import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; import navigationService from 'utils/navigationService'; @@ -92,7 +91,9 @@ export function DiscoverArchivedSection() { onClickJump(item)}> - {item?.name || item?.url} + + {item?.name || item?.url} + ))} diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx new file mode 100644 index 0000000000..71a9ec6c99 --- /dev/null +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx @@ -0,0 +1,148 @@ +import React, { useCallback, useMemo } from 'react'; +import PageContainer from 'components/PageContainer'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import { pTd } from 'utils/unit'; +import DappListItem from '../components/DappListItem'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; +import { useLanguage } from 'i18n/hooks'; +import Svg from 'components/Svg'; +import ActionSheet from 'components/ActionSheet'; +import CommonSwitch from 'components/CommonSwitch'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import fonts from 'assets/theme/fonts'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; +import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { getOrigin } from '@portkey-wallet/utils/dapp/browser'; +import { showDappPeriodOverlay } from 'components/RemeberMe'; +import { useCurrentDappInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import navigationService from 'utils/navigationService'; +import { SessionKeyMap } from '@portkey-wallet/constants/constants-ca/dapp'; +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; + +interface RouterParams { + origin: string; +} + +const DappDetail: React.FC = () => { + const { t } = useLanguage(); + const { origin } = useRouterParams(); + const dappInfo = useCurrentDappInfo(origin); + const { sessionInfo } = dappInfo || {}; + + console.log('dappInfo', dappInfo); + const dispatch = useAppCommonDispatch(); + const { currentNetwork } = useWallet(); + + const showTips = useCallback(() => { + ActionSheet.alert({ + message: + 'Once enabled, you can set a session key for this dapp. The session key will automatically approve all requests without the pop-up notifications only on this device. It will be invalid upon session expiration or dapp disconnection. You can disable this feature or modify the expiration time at any time.', + buttons: [ + { + title: 'OK', + type: 'primary', + }, + ], + }); + }, []); + + const showPeriod = useCallback(() => { + // showDappPeriodOverlay(); + }, []); + + const disconnectDapp = useCallback(() => { + dispatch(removeDapp({ networkType: currentNetwork, origin: dappInfo?.origin || '' })); + + navigationService.goBack(); + }, [currentNetwork, dispatch, dappInfo]); + + return ( + + + + {t('Connected time')} + {sessionInfo?.createTime} + + + + + + {t('Remember me')} + + + {t('Skip authentication after enabled')} + + + + + + {t('Session key expiration')} + + {SessionKeyMap[sessionInfo?.expiredPlan || SessionExpiredPlan.hour1]} + + + + + + {t('Expire time')} + {sessionInfo?.expiredTime} + + + + + Disconnect + + + + ); +}; + +const pageStyles = StyleSheet.create({ + pageWrap: { + flex: 1, + backgroundColor: defaultColors.bg4, + ...GStyles.paddingArg(24, 20, 18), + }, + tipsWrap: { + lineHeight: pTd(20), + }, + deleteBtnTitle: { + color: defaultColors.font12, + }, + rightArrow: { + marginLeft: pTd(4), + }, + sectionWrap: { + paddingHorizontal: pTd(16), + borderRadius: pTd(6), + alignItems: 'center', + marginBottom: pTd(24), + }, + section1: { + height: pTd(56), + }, + section2: { + height: pTd(72), + }, + btnWrap: { + height: pTd(48), + width: '100%', + }, + buttonContainer: { + width: screenWidth, + position: 'absolute', + bottom: 0, + }, +}); + +export default DappDetail; diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx new file mode 100644 index 0000000000..7caef4e7b3 --- /dev/null +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { TextM } from 'components/CommonText'; +import { FontStyles } from 'assets/theme/styles'; +import { pTd } from 'utils/unit'; +import Touchable from 'components/Touchable'; +import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import Svg from 'components/Svg'; +import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; + +interface DappListItemProps { + item?: DappStoreItem; + isShowArrow?: boolean; + onPress?: (item?: DappStoreItem) => void; +} + +const DappListItem: React.FC = ({ item, isShowArrow, onPress }) => { + return ( + onPress?.(item)}> + + + + + {item?.origin || getHost(item?.origin || '')} + + + {isShowArrow && } + + ); +}; + +export default DappListItem; + +const itemStyles = StyleSheet.create({ + itemWrap: { + width: '100%', + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + ...GStyles.paddingArg(13, 16), + backgroundColor: defaultColors.bg1, + marginBottom: pTd(24), + borderRadius: pTd(6), + }, + itemImage: { + marginRight: pTd(16), + borderRadius: pTd(16), + }, + itemCenter: { + flex: 1, + paddingRight: pTd(16), + }, + itemDappTitle: { + marginBottom: pTd(1), + }, + itemDappUrl: { + marginTop: pTd(1), + }, + itemRight: { + width: pTd(85), + height: pTd(24), + borderWidth: pTd(1), + borderColor: defaultColors.font12, + color: defaultColors.font12, + textAlign: 'center', + lineHeight: pTd(22), + borderRadius: pTd(6), + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx index e20c8fc487..33f6bf79a4 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/index.tsx @@ -1,28 +1,15 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import PageContainer from 'components/PageContainer'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet } from 'react-native'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; -import { TextL, TextM, TextS } from 'components/CommonText'; - -import { FontStyles } from 'assets/theme/styles'; import { pTd } from 'utils/unit'; - import { useCurrentDappList } from '@portkey-wallet/hooks/hooks-ca/dapp'; -import { useAppDispatch } from 'store/hooks'; -import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; -import Touchable from 'components/Touchable'; -import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; -import fonts from 'assets/theme/fonts'; import NoData from 'components/NoData'; -import { getFaviconUrl, getHost } from '@portkey-wallet/utils/dapp/browser'; -import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; -import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import navigationService from 'utils/navigationService'; +import DappListItem from './components/DappListItem'; const DappList: React.FC = () => { - const currentNetwork = useCurrentNetworkInfo(); - - const dispatch = useAppDispatch(); const dappList = useCurrentDappList(); return ( @@ -32,19 +19,12 @@ const DappList: React.FC = () => { containerStyles={pageStyles.pageWrap} scrollViewProps={{ disabled: false }}> {dappList?.map(item => ( - - - - - - {item?.origin || getHost(item.origin)} - - - dispatch(removeDapp({ networkType: currentNetwork.networkType, origin: item.origin }))}> - Disconnect - - + navigationService.navigate('DappDetail', { origin: item.origin })} + /> ))} {(dappList ?? []).length === 0 && } @@ -65,41 +45,4 @@ const pageStyles = StyleSheet.create({ }, }); -const itemStyles = StyleSheet.create({ - itemWrap: { - width: '100%', - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - ...GStyles.paddingArg(13, 16), - backgroundColor: defaultColors.bg1, - marginBottom: pTd(24), - borderRadius: pTd(6), - }, - itemImage: { - marginRight: pTd(16), - borderRadius: pTd(16), - }, - itemCenter: { - flex: 1, - paddingRight: pTd(16), - }, - itemDappTitle: { - marginBottom: pTd(1), - }, - itemDappUrl: { - marginTop: pTd(1), - }, - itemRight: { - width: pTd(85), - height: pTd(24), - borderWidth: pTd(1), - borderColor: defaultColors.font12, - color: defaultColors.font12, - textAlign: 'center', - lineHeight: pTd(22), - borderRadius: pTd(6), - }, -}); - export default DappList; diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/router.ts b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/router.ts new file mode 100644 index 0000000000..ed61449c36 --- /dev/null +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/router.ts @@ -0,0 +1,15 @@ +import DappList from './'; +import DappDetail from './DappDetail'; + +const stackNav = [ + { + name: 'DappList', + component: DappList, + }, + { + name: 'DappDetail', + component: DappDetail, + }, +] as const; + +export default stackNav; diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx index 99a0d1c32d..c79b985823 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/index.tsx @@ -33,7 +33,7 @@ const WalletSecurity: React.FC = () => { title="Connected Sites" suffix={dappList?.length ?? 0} onPress={() => { - navigationService.navigate('ConnectedSites'); + navigationService.navigate('DappList'); }} /> diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts b/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts index 6ebeacd090..fddfff5992 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/router.ts @@ -1,16 +1,13 @@ import WalletSecurity from '.'; import DeviceNav from './Device/router'; -import Dapp from './Dapp/index'; +import DappNav from './Dapp/router'; const stackNav = [ { name: 'WalletSecurity', component: WalletSecurity, }, - { - name: 'ConnectedSites', - component: Dapp, - }, + ...DappNav, ...DeviceNav, ] as const; diff --git a/packages/mobile-app-did/js/pages/QrScanner/index.tsx b/packages/mobile-app-did/js/pages/QrScanner/index.tsx index adf8aea35e..d4d5d32c1a 100644 --- a/packages/mobile-app-did/js/pages/QrScanner/index.tsx +++ b/packages/mobile-app-did/js/pages/QrScanner/index.tsx @@ -49,12 +49,12 @@ const QrScanner: React.FC = () => { if (typeof data !== 'string') return invalidQRCode(InvalidQRCodeText.INVALID_QR_CODE); try { - const str = prefixUrlWithProtocol(data.replace(/("|'|\s)/g, '')); + const str = data.replace(/("|'|\s)/g, ''); if (checkIsUrl(str)) { jumpToWebview({ item: { - name: str, - url: str, + name: prefixUrlWithProtocol(str), + url: prefixUrlWithProtocol(str), }, }); return navigationService.goBack(); @@ -87,6 +87,8 @@ const QrScanner: React.FC = () => { if (result && result?.uri) { const scanResult = await BarCodeScanner.scanFromURLAsync(result?.uri, [BarCodeScanner.Constants.BarCodeType.qr]); + console.log('qrResult', scanResult[0]?.data, result); + if (scanResult[0]?.data) handleBarCodeScanned({ data: scanResult[0]?.data || '' }); } }; diff --git a/packages/utils/dapp/browser.ts b/packages/utils/dapp/browser.ts index 46932ec633..11ac644cdf 100644 --- a/packages/utils/dapp/browser.ts +++ b/packages/utils/dapp/browser.ts @@ -61,6 +61,19 @@ export function getProtocolAndHost(url: string) { const { protocol, hostname } = getUrlObj(url); return `${protocol}//${hostname}`; } +/** + * + * @param url + * @returns + */ +export function getOrigin(url: string) { + const { protocol, hostname, port } = getUrlObj(url); + if (port) { + return `${protocol}//${hostname}:${port}`; + } else { + return `${protocol}//${hostname}`; + } +} /** * getFaviconUrl diff --git a/packages/utils/session.ts b/packages/utils/session.ts index cb10b557dd..73d9a076a3 100644 --- a/packages/utils/session.ts +++ b/packages/utils/session.ts @@ -55,3 +55,5 @@ export function formatExpiredTime(plan: SessionExpiredPlan) { return Date.now(); } } + +export function formatTimeToStr(plan: SessionExpiredPlan) {} From 84568f3464f54061eacbc3258429479c3bc85afd Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 26 Jul 2023 10:50:52 +0800 Subject: [PATCH 450/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mobile-app-did/android/app/build.gradle | 2 +- packages/mobile-app-did/ios/Portkey/Info.plist | 2 +- packages/utils/fetch.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/android/app/build.gradle b/packages/mobile-app-did/android/app/build.gradle index 933c53ff0a..1f37aae52d 100644 --- a/packages/mobile-app-did/android/app/build.gradle +++ b/packages/mobile-app-did/android/app/build.gradle @@ -168,7 +168,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 231 - versionName "1.3.4" + versionName "1.3.5" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { diff --git a/packages/mobile-app-did/ios/Portkey/Info.plist b/packages/mobile-app-did/ios/Portkey/Info.plist index fb64de9954..a9ce16809b 100644 --- a/packages/mobile-app-did/ios/Portkey/Info.plist +++ b/packages/mobile-app-did/ios/Portkey/Info.plist @@ -32,7 +32,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.3.4 + 1.3.5 CFBundleSignature ???? CFBundleVersion diff --git a/packages/utils/fetch.ts b/packages/utils/fetch.ts index 84640cf2de..ffb5e0e78b 100644 --- a/packages/utils/fetch.ts +++ b/packages/utils/fetch.ts @@ -15,7 +15,7 @@ const defaultHeaders = { Accept: 'text/plain;v=1.0', 'Content-Type': 'application/json', // FIXME: delete - version: 'v1.3.4', + version: 'v1.3.5', }; function formatResponse(response: string) { From 722d4d1f61be7e1a8fa15f77e66f36ac09c50381 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 26 Jul 2023 11:04:50 +0800 Subject: [PATCH 451/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20useCheckSi?= =?UTF-8?q?teIsInBlackList=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index a9c34e73cb..bd6710011c 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -9,6 +9,7 @@ import { getRememberMeBlackListAsync, } from '@portkey-wallet/store/store-ca/cms/actions'; import { BuyButtonType } from '@portkey-wallet/store/store-ca/cms/types'; +import { getHost } from '@portkey-wallet/utils/dapp/browser'; export const useCMS = () => useAppCASelector(state => state.cms); @@ -159,3 +160,18 @@ export const useRememberMeBlackList = (isInit = false) => { return rememberMeBlackList || []; }; + +export const useCheckSiteIsInBlackList = () => { + const list = useRememberMeBlackList(); + + return useCallback( + (url: string) => { + try { + return list.indexOf(getHost(url)) >= 0; + } catch (err) { + console.log(err); + } + }, + [list], + ); +}; From 523542eb6b1c5a818daa3329b8b65ac5a7b7dc83 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Wed, 26 Jul 2023 16:17:06 +0800 Subject: [PATCH 452/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20REMEMBER=5FME=5F?= =?UTF-8?q?ACTION=5FWHITELIST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/dapp.ts | 4 ++++ packages/constants/package.json | 3 ++- packages/mobile-app-did/js/dapp/dappMobileOperator.ts | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/constants/constants-ca/dapp.ts b/packages/constants/constants-ca/dapp.ts index 4dbd57f49b..80db337527 100644 --- a/packages/constants/constants-ca/dapp.ts +++ b/packages/constants/constants-ca/dapp.ts @@ -1 +1,5 @@ +import { MethodsBase } from '@portkey/provider-types'; + export const CA_METHOD_WHITELIST = ['ManagerForwardCall', 'ManagerTransfer']; + +export const REMEMBER_ME_ACTION_WHITELIST: string[] = [MethodsBase.SEND_TRANSACTION]; diff --git a/packages/constants/package.json b/packages/constants/package.json index f35f306fb7..ece9271a80 100644 --- a/packages/constants/package.json +++ b/packages/constants/package.json @@ -12,6 +12,7 @@ "handle-network": "cd ./scripts && node handleNetwork.js" }, "dependencies": { - "@portkey-wallet/types": "^1.3.0" + "@portkey-wallet/types": "^1.3.0", + "@portkey/provider-types": "^1.0.1-alpha.0" } } diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index a342454503..65696b2e58 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -21,7 +21,7 @@ import { getContractBasic } from '@portkey-wallet/contracts/utils'; import { getCurrentCaHash, getManagerAccount, getPin } from 'utils/redux'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { isEqDapp } from '@portkey-wallet/utils/dapp/browser'; -import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; +import { CA_METHOD_WHITELIST, REMEMBER_ME_ACTION_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; import { hasSessionInfoExpired, verifySession } from '@portkey-wallet/utils/session'; const SEND_METHOD: { [key: string]: true } = { [MethodsBase.SEND_TRANSACTION]: true, @@ -245,8 +245,8 @@ export default class DappMobileOperator extends Operator { }) { const validSession = await this.verifySessionInfo(); - // valid session - if (validSession) return callBack(eventName, params); + // valid session && is remember me actions + if (validSession && REMEMBER_ME_ACTION_WHITELIST.includes(method)) return callBack(eventName, params); // user confirm const response = await this.userConfirmation({ eventName, method, params }); From 4946673943748caf6ce253838caafb84f2a48607 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 26 Jul 2023 16:23:59 +0800 Subject: [PATCH 453/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dapp=20rememberm?= =?UTF-8?q?e=20adjust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/dapp.ts | 8 +- packages/hooks/hooks-ca/cms.ts | 4 +- .../js/components/RemeberMe/index.tsx | 64 ++++++-- .../js/components/SendButton/index.tsx | 1 + .../dapp/components/ConnectOverlay/index.tsx | 8 +- .../js/dapp/components/SignOverlay/index.tsx | 8 +- .../components/TransactionOverlay/index.tsx | 40 +++-- packages/mobile-app-did/js/hooks/discover.ts | 18 +-- .../WalletSecurity/Dapp/DappDetail/index.tsx | 140 ++++++++++++++---- .../Dapp/components/DappListItem/index.tsx | 34 ++++- packages/utils/package.json | 3 +- packages/utils/session.ts | 5 +- yarn.lock | 5 + 13 files changed, 248 insertions(+), 90 deletions(-) diff --git a/packages/constants/constants-ca/dapp.ts b/packages/constants/constants-ca/dapp.ts index de465a52e3..d8579d5f9b 100644 --- a/packages/constants/constants-ca/dapp.ts +++ b/packages/constants/constants-ca/dapp.ts @@ -3,10 +3,10 @@ import { SessionExpiredPlan } from '@portkey-wallet/types/session'; export const CA_METHOD_WHITELIST = ['ManagerForwardCall', 'ManagerTransfer']; export const SessionKeyMap = { - [SessionExpiredPlan.hour1]: '1 hours', - [SessionExpiredPlan.hour3]: '3 hours', - [SessionExpiredPlan.hour12]: '12 hours', - [SessionExpiredPlan.hour24]: '24 hours', + [SessionExpiredPlan.hour1]: 'In 1 hour', + [SessionExpiredPlan.hour3]: 'In 3 hours', + [SessionExpiredPlan.hour12]: 'In 12 hours', + [SessionExpiredPlan.hour24]: 'In 24 hours', [SessionExpiredPlan.always]: 'Never', }; diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index bd6710011c..15101075c8 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -9,7 +9,7 @@ import { getRememberMeBlackListAsync, } from '@portkey-wallet/store/store-ca/cms/actions'; import { BuyButtonType } from '@portkey-wallet/store/store-ca/cms/types'; -import { getHost } from '@portkey-wallet/utils/dapp/browser'; +import { getOrigin } from '@portkey-wallet/utils/dapp/browser'; export const useCMS = () => useAppCASelector(state => state.cms); @@ -167,7 +167,7 @@ export const useCheckSiteIsInBlackList = () => { return useCallback( (url: string) => { try { - return list.indexOf(getHost(url)) >= 0; + return list.indexOf(getOrigin(url)) >= 0; } catch (err) { console.log(err); } diff --git a/packages/mobile-app-did/js/components/RemeberMe/index.tsx b/packages/mobile-app-did/js/components/RemeberMe/index.tsx index 9337c7ce65..00318915de 100644 --- a/packages/mobile-app-did/js/components/RemeberMe/index.tsx +++ b/packages/mobile-app-did/js/components/RemeberMe/index.tsx @@ -10,16 +10,10 @@ import { FontStyles } from 'assets/theme/styles'; import { useLanguage } from 'i18n/hooks'; import fonts from 'assets/theme/fonts'; import { SessionExpiredPlan } from '@portkey-wallet/types/session'; -import { useCheckSiteIsInBlackList } from 'hooks/discover'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { SessionKeyMap, SessionKeyArray } from '@portkey-wallet/constants/constants-ca/dapp'; import GStyles from 'assets/theme/GStyles'; - -type DappPeriodOverlayProps = { - value: SessionExpiredPlan; - onCancel?: () => void; - onConfirm: (value: SessionExpiredPlan) => void; -}; +import { useCheckSiteIsInBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; export type RememberInfoType = { isRemember: boolean; @@ -32,7 +26,13 @@ type RememberMeProps = { setRememberMeInfo: Dispatch>; }; -function DappPeriodOverlay(props: DappPeriodOverlayProps) { +type RememberMeOverlayProps = { + value: SessionExpiredPlan; + onCancel?: () => void; + onConfirm: (value: SessionExpiredPlan) => void; +}; + +function RememberMeOverlay(props: RememberMeOverlayProps) { const { value, onConfirm } = props; const { t } = useLanguage(); @@ -65,12 +65,45 @@ function DappPeriodOverlay(props: DappPeriodOverlayProps) { ); } -export const showDappPeriodOverlay = (props: RememberMeProps) => { +function PeriodOverlay(props: RememberMeOverlayProps) { + const { value, onConfirm } = props; + + const onPressItem = useCallback( + (v: SessionExpiredPlan) => { + if (String(value) === String(v)) return; + onConfirm(v); + OverlayModal.hide(); + }, + [onConfirm, value], + ); + + return ( + + + {SessionKeyArray.map(ele => ( + onPressItem(ele?.value)}> + {ele.label} + {value === ele.value && } + + ))} + + + ); +} + +export const showPeriodOverlay = (props: RememberMeOverlayProps) => { + Keyboard.dismiss(); + OverlayModal.show(, { + position: 'bottom', + }); +}; + +const showRememberMeOverlay = (props: RememberMeProps) => { const { rememberInfo, setRememberMeInfo } = props; Keyboard.dismiss(); OverlayModal.show( - { setRememberMeInfo(pre => ({ ...pre, value })); @@ -84,8 +117,8 @@ export const showDappPeriodOverlay = (props: RememberMeProps) => { export const RememberMe = (props: RememberMeProps) => { const { dappInfo, rememberInfo, setRememberMeInfo } = props; - const checkOrigin = useCheckSiteIsInBlackList(); - if (checkOrigin(dappInfo.origin)) return null; + const checkOriginInBlackList = useCheckSiteIsInBlackList(); + if (checkOriginInBlackList(dappInfo.origin)) return null; return ( @@ -99,7 +132,7 @@ export const RememberMe = (props: RememberMeProps) => { Remember me to skip authentication for - showDappPeriodOverlay(props)}> + showRememberMeOverlay(props)}> {SessionKeyMap[rememberInfo.value || SessionExpiredPlan.hour1]} @@ -108,6 +141,11 @@ export const RememberMe = (props: RememberMeProps) => { ); }; +export default { + RememberMe, + showPeriodOverlay, +}; + const styles = StyleSheet.create({ rememberWrap: { display: 'flex', diff --git a/packages/mobile-app-did/js/components/SendButton/index.tsx b/packages/mobile-app-did/js/components/SendButton/index.tsx index f207c7931f..b57c62873b 100644 --- a/packages/mobile-app-did/js/components/SendButton/index.tsx +++ b/packages/mobile-app-did/js/components/SendButton/index.tsx @@ -11,6 +11,7 @@ import { useLanguage } from 'i18n/hooks'; import { pTd } from 'utils/unit'; import AssetsOverlay from 'pages/DashBoard/AssetsOverlay'; import GStyles from 'assets/theme/GStyles'; + interface SendButtonType { themeType?: 'dashBoard' | 'innerPage'; sentToken?: TokenItemShowType; diff --git a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx index 35b23e5acd..41ac5b470c 100644 --- a/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/ConnectOverlay/index.tsx @@ -41,7 +41,7 @@ const ConnectModal = (props: ConnectModalType) => { const caInfo = useCurrentCaInfo(); const { walletName, currentNetwork } = useWallet(); const gStyles = useGStyles(); - const upDateSessionInfo = useUpdateSessionInfo(); + const updateSessionInfo = useUpdateSessionInfo(); const [rememberInfo, setRememberMeInfo] = useState({ isRemember: false, @@ -87,16 +87,16 @@ const ConnectModal = (props: ConnectModalType) => { await sleep(500); if (!pin) return; if (rememberInfo.isRemember) { - upDateSessionInfo({ + updateSessionInfo({ manager: getManagerAccount(pin), origin: dappInfo.origin, - expiredPlan: SessionExpiredPlan.hour24, + expiredPlan: rememberInfo?.value || SessionExpiredPlan.hour1, }); } }, }, ], - [dappInfo.origin, onApprove, onReject, pin, rememberInfo.isRemember, t, upDateSessionInfo], + [dappInfo.origin, onApprove, onReject, pin, rememberInfo.isRemember, rememberInfo?.value, t, updateSessionInfo], ); return ( diff --git a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx index 6b432714a9..84d369f106 100644 --- a/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/SignOverlay/index.tsx @@ -13,6 +13,7 @@ import DappInfoSection from '../DappInfoSection'; import { GetSignatureParams } from '@portkey/provider-types'; import TransactionDataSection from '../TransactionDataSection'; import { TextXXXL } from 'components/CommonText'; +import { OverlayBottomSection } from '../OverlayBottomSection'; type SignModalPropsType = { dappInfo: DappStoreItem; @@ -24,7 +25,7 @@ const SignModal = (props: SignModalPropsType) => { const { dappInfo, signInfo, onReject, onSign } = props; const { t } = useLanguage(); - const buttonList = useMemo( + const ButtonList = useMemo( () => [ { title: t('Reject'), @@ -37,7 +38,7 @@ const SignModal = (props: SignModalPropsType) => { { title: t('Approve'), type: 'primary' as CommonButtonProps['type'], - onPress: () => { + onPress: async () => { onSign?.(); OverlayModal.hide(); }, @@ -47,12 +48,13 @@ const SignModal = (props: SignModalPropsType) => { ); return ( - + Sign Message + ); }; diff --git a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx index 20b7a99ccb..5987b0f1db 100644 --- a/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx +++ b/packages/mobile-app-did/js/dapp/components/TransactionOverlay/index.tsx @@ -6,7 +6,7 @@ import { useLanguage } from 'i18n/hooks'; import { ModalBody } from 'components/ModalBody'; import { TextM, TextS } from 'components/CommonText'; import { useCurrentWalletInfo, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr } from '@portkey-wallet/utils'; +import { addressFormat, formatChainInfoToShow, formatStr2EllipsisStr, sleep } from '@portkey-wallet/utils'; import { divDecimals, formatAmountShow } from '@portkey-wallet/utils/converter'; import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; @@ -28,6 +28,10 @@ import { styles, transferGroupStyle } from './styles/index'; import Lottie from 'lottie-react-native'; import { useCheckManagerSyncState } from 'hooks/wallet'; import { request } from '@portkey-wallet/api/api-did'; +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; +import { RememberInfoType, RememberMe } from 'components/RemeberMe'; +import { useUpdateSessionInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import { OverlayBottomSection } from '../OverlayBottomSection'; enum ErrorText { ESTIMATE_ERROR = 'Failed to estimate transaction fee', @@ -46,12 +50,16 @@ const ConnectModal = (props: TransactionModalPropsType) => { const isMainnet = useIsMainnet(); const defaultToken = useDefaultToken(); const pin = usePin(); - const { walletName } = useWallet(); const wallet = useCurrentWalletInfo(); const checkManagerSyncState = useCheckManagerSyncState(); - const amountInUsdShow = useAmountInUsdShow(); + const updateSessionInfo = useUpdateSessionInfo(); + + const [rememberInfo, setRememberMeInfo] = useState({ + isRemember: false, + value: SessionExpiredPlan.hour1, + }); const chainInfo = useCurrentChain(transactionInfo.chainId); const [, getTokenPrice, getTokensPrice] = useGetCurrentAccountTokenPrice(); @@ -70,8 +78,8 @@ const ConnectModal = (props: TransactionModalPropsType) => { const isTransfer = useMemo(() => transactionInfo.method.toLowerCase() === 'transfer', [transactionInfo.method]); - const buttonList = useMemo(() => { - return [ + const ButtonList = useMemo( + () => [ { title: t('Reject'), type: 'outline' as CommonButtonProps['type'], @@ -83,13 +91,24 @@ const ConnectModal = (props: TransactionModalPropsType) => { { title: t('Approve'), type: 'primary' as CommonButtonProps['type'], - onPress: () => { + onPress: async () => { onSign?.(); OverlayModal.hide(); + + await sleep(500); + if (!pin) return; + if (rememberInfo.isRemember) { + updateSessionInfo({ + manager: getManagerAccount(pin), + origin: dappInfo.origin, + expiredPlan: rememberInfo?.value || SessionExpiredPlan.hour1, + }); + } }, }, - ]; - }, [onReject, onSign, t]); + ], + [dappInfo.origin, onReject, onSign, pin, rememberInfo.isRemember, rememberInfo?.value, t, updateSessionInfo], + ); const formatAmountInUsdShow = useCallback( (amount: string | number, decimals: string | number, symbol: string) => { @@ -414,7 +433,7 @@ const ConnectModal = (props: TransactionModalPropsType) => { }, [defaultToken.symbol, getTokenPrice, getTokensPrice, transactionInfo?.params?.paramsOption?.symbol]); return ( - + {transactionInfo?.method} @@ -429,6 +448,9 @@ const ConnectModal = (props: TransactionModalPropsType) => { + + + ); }; diff --git a/packages/mobile-app-did/js/hooks/discover.ts b/packages/mobile-app-did/js/hooks/discover.ts index caf5f4d1cb..784d0c2eb0 100644 --- a/packages/mobile-app-did/js/hooks/discover.ts +++ b/packages/mobile-app-did/js/hooks/discover.ts @@ -1,6 +1,5 @@ import { request } from '@portkey-wallet/api/api-did'; import { useAppCASelector, useAppCommonDispatch } from '@portkey-wallet/hooks'; -import { useRememberMeBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { addUrlToWhiteList, @@ -17,7 +16,7 @@ import { } from '@portkey-wallet/store/store-ca/discover/slice'; import { IBookmarkItem, ITabItem } from '@portkey-wallet/store/store-ca/discover/type'; import { isUrl } from '@portkey-wallet/utils'; -import { getProtocolAndHost, prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; +import { prefixUrlWithProtocol } from '@portkey-wallet/utils/dapp/browser'; import { DISCOVER_BOOKMARK_MAX_COUNT } from 'constants/common'; import { useCallback, useEffect, useMemo } from 'react'; @@ -170,18 +169,3 @@ export const useCheckAndUpDateTabItemName = () => { [discoverMap, dispatch, networkType], ); }; - -export const useCheckSiteIsInBlackList = () => { - const list = useRememberMeBlackList(); - - return useCallback( - (url: string) => { - try { - return list.indexOf(getProtocolAndHost(url)) >= 0; - } catch (err) { - console.log(err); - } - }, - [list], - ); -}; diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx index 71a9ec6c99..3dce4c2a1d 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx @@ -9,7 +9,6 @@ import { TextL, TextM, TextS } from 'components/CommonText'; import { BGStyles, FontStyles } from 'assets/theme/styles'; import { pTd } from 'utils/unit'; import DappListItem from '../components/DappListItem'; -import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; import { useLanguage } from 'i18n/hooks'; import Svg from 'components/Svg'; import ActionSheet from 'components/ActionSheet'; @@ -19,11 +18,17 @@ import fonts from 'assets/theme/fonts'; import { useAppCommonDispatch } from '@portkey-wallet/hooks'; import { removeDapp } from '@portkey-wallet/store/store-ca/dapp/actions'; import { getOrigin } from '@portkey-wallet/utils/dapp/browser'; -import { showDappPeriodOverlay } from 'components/RemeberMe'; -import { useCurrentDappInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import { showPeriodOverlay } from 'components/RemeberMe'; +import { useCurrentDappInfo, useUpdateSessionInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; import navigationService from 'utils/navigationService'; import { SessionKeyMap } from '@portkey-wallet/constants/constants-ca/dapp'; import { SessionExpiredPlan } from '@portkey-wallet/types/session'; +import { usePin } from 'hooks/store'; +import { getManagerAccount } from 'utils/redux'; +import { formatTimeToStr } from '@portkey-wallet/utils/session'; +import CommonToast from 'components/CommonToast'; +import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import { useCheckSiteIsInBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; interface RouterParams { origin: string; @@ -31,18 +36,33 @@ interface RouterParams { const DappDetail: React.FC = () => { const { t } = useLanguage(); + const pin = usePin(); + + const checkOriginInBlackList = useCheckSiteIsInBlackList(); + const { origin } = useRouterParams(); const dappInfo = useCurrentDappInfo(origin); const { sessionInfo } = dappInfo || {}; - console.log('dappInfo', dappInfo); + console.log('dappInfo', dappInfo?.sessionInfo?.expiredPlan); const dispatch = useAppCommonDispatch(); const { currentNetwork } = useWallet(); + const updateSessionInfo = useUpdateSessionInfo(); + const discoverJump = useDiscoverJumpWithNetWork(); + + const isRememberMe = useMemo(() => { + return !!sessionInfo?.expiredPlan; + }, [sessionInfo?.expiredPlan]); + + const isInBlackList = useMemo( + () => checkOriginInBlackList(dappInfo?.origin || ''), + [checkOriginInBlackList, dappInfo?.origin], + ); const showTips = useCallback(() => { ActionSheet.alert({ message: - 'Once enabled, you can set a session key for this dapp. The session key will automatically approve all requests without the pop-up notifications only on this device. It will be invalid upon session expiration or dapp disconnection. You can disable this feature or modify the expiration time at any time.', + "Once enabled, your session key will automatically approve all requests from this DApp, on this device only. You won't see pop-up notifications asking for your approvals until the session key expires. This feature is automatically off when you disconnect from the DApp or when the session key expires. You can also manually disable it or change the expiration time.", buttons: [ { title: 'OK', @@ -52,51 +72,109 @@ const DappDetail: React.FC = () => { }); }, []); - const showPeriod = useCallback(() => { - // showDappPeriodOverlay(); - }, []); + const showOverlay = useCallback(() => { + showPeriodOverlay({ + value: dappInfo?.sessionInfo?.expiredPlan || SessionExpiredPlan.hour1, + onConfirm: value => { + if (!pin) return; + updateSessionInfo({ + manager: getManagerAccount(pin), + origin: getOrigin(dappInfo?.origin || ''), + expiredPlan: value, + }); + CommonToast.success('Session Key updated'); + }, + }); + }, [dappInfo?.origin, dappInfo?.sessionInfo?.expiredPlan, pin, updateSessionInfo]); + + const switchRememberMe = useCallback( + (v: boolean) => { + if (v) { + // select RememberMe + if (!pin) return; + updateSessionInfo({ + manager: getManagerAccount(pin), + origin: getOrigin(dappInfo?.origin || ''), + expiredPlan: SessionExpiredPlan.hour1, + }); + CommonToast.success('Session Key enabled'); + } else { + updateSessionInfo({ origin: getOrigin(dappInfo?.origin || '') }); + CommonToast.success('Session Key disabled'); + } + }, + [dappInfo?.origin, pin, updateSessionInfo], + ); const disconnectDapp = useCallback(() => { dispatch(removeDapp({ networkType: currentNetwork, origin: dappInfo?.origin || '' })); - navigationService.goBack(); }, [currentNetwork, dispatch, dappInfo]); + const onJumpToDapp = useCallback( + (name: string, url: string) => { + discoverJump({ + item: { + name, + url, + }, + }); + }, + [discoverJump], + ); + return ( - + onJumpToDapp(dappInfo?.name || '', dappInfo?.origin || '')} + /> {t('Connected time')} - {sessionInfo?.createTime} + {formatTimeToStr(dappInfo?.connectedTime || 0) || '--'} - - - - {t('Remember me')} - - - {t('Skip authentication after enabled')} + {!isInBlackList && ( + + + + {t('Remember me')} + + + {t('Skip authentication after enabled')} + + switchRememberMe(!isRememberMe)} /> - - + )} - - {t('Session key expiration')} - - {SessionKeyMap[sessionInfo?.expiredPlan || SessionExpiredPlan.hour1]} - - - + {!isInBlackList && isRememberMe && ( + + {t('Session key expiration')} + + {SessionKeyMap[sessionInfo?.expiredPlan || SessionExpiredPlan.hour1]} + + + + )} - - {t('Expire time')} - {sessionInfo?.expiredTime} - + {!isInBlackList && isRememberMe && ( + + {t('Expiration time')} + + {sessionInfo?.expiredPlan === SessionExpiredPlan.always + ? '--' + : formatTimeToStr(sessionInfo?.expiredTime || 0)} + + + )} diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx index 7caef4e7b3..5e8ceaffb0 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/components/DappListItem/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { TextM } from 'components/CommonText'; @@ -13,12 +13,36 @@ import Svg from 'components/Svg'; import { DappStoreItem } from '@portkey-wallet/store/store-ca/dapp/type'; interface DappListItemProps { + type?: 'home' | 'detail'; item?: DappStoreItem; - isShowArrow?: boolean; onPress?: (item?: DappStoreItem) => void; } -const DappListItem: React.FC = ({ item, isShowArrow, onPress }) => { +const DappListItem: React.FC = ({ item, type = 'home', onPress }) => { + if (type === 'detail') { + return ( + + + + + onPress?.(item)}> + + {item?.origin || getHost(item?.origin || '')} + + + + + ); + } + return ( onPress?.(item)}> @@ -28,11 +52,11 @@ const DappListItem: React.FC = ({ item, isShowArrow, onPress title={item?.name || getHost(item?.origin || '')} url={item?.origin || ''} /> - + {item?.origin || getHost(item?.origin || '')} - {isShowArrow && } + ); }; diff --git a/packages/utils/package.json b/packages/utils/package.json index 5d3ca845d4..7f6d1f6b59 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -14,7 +14,8 @@ "@portkey-wallet/constants": "^1.3.0", "@portkey-wallet/types": "^1.3.0", "is-url": "^1.2.4", - "query-string": "^7.1.1" + "query-string": "^7.1.1", + "dayjs":"^1.11.9" }, "devDependencies": { "@portkey/provider-types": "1.0.1-alpha.0", diff --git a/packages/utils/session.ts b/packages/utils/session.ts index 73d9a076a3..017edecbc9 100644 --- a/packages/utils/session.ts +++ b/packages/utils/session.ts @@ -2,6 +2,7 @@ import type { ec } from 'elliptic'; import { SessionExpiredPlan, SessionInfo } from '../types/session'; import { Timestamp } from '../types'; import AElf from 'aelf-sdk'; +import dayjs from 'dayjs'; const HOUR = 60 * 60 * 1000; @@ -56,4 +57,6 @@ export function formatExpiredTime(plan: SessionExpiredPlan) { } } -export function formatTimeToStr(plan: SessionExpiredPlan) {} +export function formatTimeToStr(time: number) { + return dayjs(time).format('YYYY-MM-DD HH:mm:ss'); +} diff --git a/yarn.lock b/yarn.lock index c4c4e61e11..1a4636aa67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12666,6 +12666,11 @@ dayjs@^1.11.7: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea" integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ== +dayjs@^1.11.9: + version "1.11.9" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" + integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== + debounce@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" From 4b593c1622990bc115e33b6ce700de68925b7d3f Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Wed, 26 Jul 2023 17:05:12 +0800 Subject: [PATCH 454/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20update=20dapp=20?= =?UTF-8?q?auto=20tx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/session.ts | 23 ++++ .../app/web/Prompt/routes/index.tsx | 2 +- .../approval/ApprovalController.ts | 17 +++ .../methodController/AELFMethodController.ts | 100 +++++++++++++-- .../app/web/messages/InternalMessageTypes.ts | 1 + .../app/web/pages/ConnectWallet/index.tsx | 31 +++-- .../app/web/pages/DappAutoTx/index.tsx | 103 ++++++++++++++- .../app/web/pages/GetSignature/index.less | 3 + .../app/web/pages/GetSignature/index.tsx | 35 ++++-- .../app/web/pages/Home/index.tsx | 4 +- .../app/web/pages/SendTransactions/index.less | 3 + .../app/web/pages/SendTransactions/index.tsx | 46 ++++++- .../SiteDetail/Popup/index.less | 1 + .../SiteDetail/Prompt/index.less | 1 + .../ConnectedSites/SiteDetail/index.tsx | 9 +- .../WalletSecurity/ConnectedSites/index.tsx | 27 +--- .../components/SiteItem/index.tsx | 117 +++++++++++------- .../pages/components/DappSession/index.tsx | 40 ++---- .../NotificationService/getPromptRoute.ts | 1 + .../web/service/NotificationService/index.ts | 1 + .../app/web/utils/getManager.ts | 15 +++ .../web-extension-did/app/web/utils/index.ts | 4 + .../app/web/utils/lib/SWGetReduxStore.ts | 9 ++ .../app/web/utils/lib/getManager.ts | 11 ++ 24 files changed, 473 insertions(+), 131 deletions(-) create mode 100644 packages/web-extension-did/app/web/utils/getManager.ts create mode 100644 packages/web-extension-did/app/web/utils/lib/getManager.ts diff --git a/packages/types/session.ts b/packages/types/session.ts index d3c5db9971..135b14f658 100644 --- a/packages/types/session.ts +++ b/packages/types/session.ts @@ -14,3 +14,26 @@ export type SessionInfo = { expiredTime: Timestamp; signature: string; }; + +export const SessionExpiredPlanShow = [ + { + value: SessionExpiredPlan.hour1, + children: '1 hour', + }, + { + value: SessionExpiredPlan.hour3, + children: '3 hours', + }, + { + value: SessionExpiredPlan.hour12, + children: '12 hours', + }, + { + value: SessionExpiredPlan.hour24, + children: '24 hours', + }, + { + value: SessionExpiredPlan.always, + children: 'Never', + }, +]; diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index 5d56636428..9d737bddb5 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -179,7 +179,7 @@ export const PageRouter = () => { element: , }, { - path: '/auto-tx', + path: '/auto-execute-tx', element: , }, { diff --git a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts index 84a2fd0679..f1baa6faa6 100644 --- a/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts +++ b/packages/web-extension-did/app/web/controllers/approval/ApprovalController.ts @@ -126,4 +126,21 @@ export default class ApprovalController { search: JSON.stringify(params), }); } + + /** + * Obtain authorization to auto execute + * + */ + async authorizedToAutoExecute(params: any): Promise { + return this.notificationService.openPrompt( + { + method: PromptRouteTypes.AUTO_EXECUTE_TX, + search: JSON.stringify(params), + }, + 'windows', + { + state: 'minimized', + }, + ); + } } diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 81744c7867..50b80b1687 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -8,12 +8,14 @@ import { IPageState, RequestCommonHandler, RequestMessageData } from 'types/SW'; import errorHandler from 'utils/errorHandler'; import { MethodsBase, ResponseCode, MethodsWallet } from '@portkey/provider-types'; import { ExtensionDappManager } from './ExtensionDappManager'; -import { getSWReduxState } from 'utils/lib/SWGetReduxStore'; +import { getCurrentCaHash, getSWReduxState, getWalletState } from 'utils/lib/SWGetReduxStore'; import ApprovalController from 'controllers/approval/ApprovalController'; import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; import { randomId } from '@portkey-wallet/utils'; import { removeLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; import SWEventController from 'controllers/SWEventController'; +import { hasSessionInfoExpired, verifySession } from '@portkey-wallet/utils/session'; +import getManager from 'utils/lib/getManager'; const storeInSW = { getState: getSWReduxState, @@ -58,6 +60,22 @@ export default class AELFMethodController { store: storeInSW, }); } + + handleRequest = async ({ params, method, callBack }: { params: any; method: any; callBack: any }) => { + const validSession = await this.verifySessionInfo(params.origin); + + let result; + if (validSession) { + result = await this.approvalController.authorizedToAutoExecute({ + ...params, + method, + }); + } else { + result = await callBack(params); + } + return result; + }; + dispenseMessage = (message: RequestMessageData, sendResponse: SendResponseFun) => { switch (message.type) { case MethodsBase.CHAIN_ID: @@ -102,6 +120,34 @@ export default class AELFMethodController { } }; + verifySessionInfo = async (origin: string) => { + try { + const sessionInfo = await this.dappManager.getSessionInfo(origin); + const wallet = await getWalletState(); + if (!wallet.walletInfo) return false; + const pin = this.getPassword(); + if (!pin) return false; + const manager = await getManager(pin); + const caHash = await getCurrentCaHash(); + if (!manager?.keyPair || !caHash || !sessionInfo) return false; + const valid = verifySession({ + keyPair: manager.keyPair, + origin, + managerAddress: manager.address, + caHash, + expiredPlan: sessionInfo.expiredPlan, + expiredTime: sessionInfo.expiredTime, + signature: sessionInfo.signature, + }); + if (!valid) return valid; + const res = hasSessionInfoExpired(sessionInfo); + return res; + } catch (error) { + console.log('verifySessionInfo error'); + return false; + } + }; + isUnlocked = () => { return Boolean(this.getPassword()); }; @@ -292,11 +338,35 @@ export default class AELFMethodController { const key = randomId(); setLocalStorage({ txPayload: { [key]: JSON.stringify(payload.params) } }); delete message.payload?.params; - const result = await this.approvalController.authorizedToSendTransactions({ - origin, - transactionInfoId: key, - payload: message.payload, - }); + + // const result = await this.handleRequest({ + // params: { + // origin, + // transactionInfoId: key, + // payload: message.payload, + // }, + // method: MethodsBase.SEND_TRANSACTION, + // callBack: (params: any) => this.approvalController.authorizedToSendTransactions(params), + // }); + + const validSession = await this.verifySessionInfo(origin); + + let result; + if (validSession) { + result = await this.approvalController.authorizedToAutoExecute({ + origin, + transactionInfoId: key, + payload: message.payload, + method: MethodsBase.SEND_TRANSACTION, + }); + } else { + result = await this.approvalController.authorizedToSendTransactions({ + origin, + transactionInfoId: key, + payload: message.payload, + }); + } + // TODO Only support open a window removeLocalStorage('txPayload'); if (result.error === 200003) @@ -306,13 +376,16 @@ export default class AELFMethodController { code: ResponseCode.USER_DENIED, }, }); - if (result.error) + if (result.error) { + console.log('error', result); + return sendResponse({ ...errorHandler(700002), data: { code: ResponseCode.CONTRACT_ERROR, }, }); + } sendResponse(result); } catch (error) { console.log('sendTransaction===', error); @@ -341,13 +414,18 @@ export default class AELFMethodController { }, }); - const result = await this.approvalController.authorizedToGetSignature({ - origin, - payload: { - data: message.payload.data, + const result = await this.handleRequest({ + params: { origin: message.origin, + payload: { + data: message.payload.data, + origin: message.origin, + }, }, + method: MethodsWallet.GET_WALLET_SIGNATURE, + callBack: (params: any) => this.approvalController.authorizedToGetSignature(params), }); + if (result.error === 200003) return sendResponse({ ...errorHandler(200003), diff --git a/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts b/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts index e3b8961e2b..1587c0ec70 100644 --- a/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts +++ b/packages/web-extension-did/app/web/messages/InternalMessageTypes.ts @@ -11,6 +11,7 @@ export const PromptRouteTypes = { CONNECT_WALLET: 'CONNECT_WALLET', SEND_TRANSACTION: 'SEND_TRANSACTION', EXPAND_FULL_SCREEN: 'EXPAND_FULL_SCREEN', + AUTO_EXECUTE_TX: 'AUTO_EXECUTE_TX', // my SETTING: 'SETTING', diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index e3a037b31f..35994f2c4f 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -9,6 +9,9 @@ import { useAppDispatch, useWalletInfo } from 'store/Provider/hooks'; import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; import DappSession from 'pages/components/DappSession'; +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; +import { useUpdateSessionInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; +import getManager from 'utils/getManager'; import './index.less'; const allowItem = ['view wallet balance and activities', 'send you transaction requests']; @@ -19,8 +22,9 @@ export default function ConnectWallet() { const dispatch = useAppDispatch(); const { currentNetwork } = useWalletInfo(); const disabled = useMemo(() => !detail.appHref, [detail]); - const [open, setOpen] = useState(false); - const [exp, setExp] = useState(''); + const [open, setOpen] = useState(false); + const [exp, setExp] = useState(SessionExpiredPlan.hour1); + const updateSessionInfo = useUpdateSessionInfo(); const renderSite = useMemo( () => @@ -50,14 +54,10 @@ export default function ConnectWallet() { [t], ); - const handleSessionChange = useCallback( - (flag: boolean, time: string) => { - setOpen(flag); - setExp(time); - console.log(open, exp); - }, - [exp, open], - ); + const handleSessionChange = useCallback((flag: boolean, extTime: SessionExpiredPlan) => { + setOpen(flag); + setExp(extTime); + }, []); const handleSign = useCallback(async () => { try { @@ -71,6 +71,15 @@ export default function ConnectWallet() { }, }), ); + if (open) { + const manager = await getManager(); + updateSessionInfo({ + networkType: currentNetwork, + origin: detail.appHref, + expiredPlan: exp, + manager, + }); + } closePrompt({ ...errorHandler(0), data: { origin: detail.appHref }, @@ -78,7 +87,7 @@ export default function ConnectWallet() { } catch (error) { console.log('add dapp error', error); } - }, [currentNetwork, detail, dispatch]); + }, [currentNetwork, detail.appHref, detail.appLogo, detail.appName, dispatch, exp, open, updateSessionInfo]); return (
    diff --git a/packages/web-extension-did/app/web/pages/DappAutoTx/index.tsx b/packages/web-extension-did/app/web/pages/DappAutoTx/index.tsx index 98d437764d..36f8080c57 100644 --- a/packages/web-extension-did/app/web/pages/DappAutoTx/index.tsx +++ b/packages/web-extension-did/app/web/pages/DappAutoTx/index.tsx @@ -1,14 +1,115 @@ import CustomSvg from 'components/CustomSvg'; +import usePromptSearch from 'hooks/usePromptSearch'; +import { useCallback, useEffect, useMemo } from 'react'; +import { closePrompt } from 'utils/lib/serviceWorkerAction'; +import errorHandler from 'utils/errorHandler'; +import { MethodsBase, ResponseCode } from '@portkey/provider-types'; +import { useCurrentChain } from '@portkey-wallet/hooks/hooks-ca/chainList'; +import { useCurrentWalletInfo } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import { getLocalStorage } from 'utils/storage/chromeStorage'; +import aes from '@portkey-wallet/utils/aes'; +import { useUserInfo } from 'store/Provider/hooks'; +import { callSendMethod } from 'utils/sandboxUtil/sendTransactions'; +import { Loading } from '@portkey/did-ui-react'; import './index.less'; export default function DappAutoTx() { + const txParams = usePromptSearch(); + const { payload } = txParams; + const chainInfo = useCurrentChain(payload?.chainId); + const wallet = useCurrentWalletInfo(); + const isCAContract = useMemo(() => chainInfo?.caContractAddress === payload?.contractAddress, [chainInfo, payload]); + const { passwordSeed } = useUserInfo(); + const privateKey = useMemo( + () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), + [passwordSeed, wallet.AESEncryptPrivateKey], + ); + const handleTransaction = useCallback(async () => { + try { + if (!chainInfo?.endPoint || !wallet?.caHash) { + closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid chain id' } }); + return; + } + + if (chainInfo?.endPoint !== payload?.rpcUrl) { + closePrompt({ ...errorHandler(400001), data: { code: ResponseCode.ERROR_IN_PARAMS, msg: 'invalid rpcUrl' } }); + return; + } + + const txPayload = await getLocalStorage<{ [x: string]: any }>('txPayload'); + const { transactionInfoId } = txParams; + if (!txPayload[transactionInfoId]) { + closePrompt({ + ...errorHandler(400001), + data: { code: ResponseCode.ERROR_IN_PARAMS }, + }); + return; + } + + const transactionInfo = JSON.parse(txPayload[transactionInfoId]); + let paramsOption = transactionInfo.paramsOption; + + const functionName = isCAContract ? payload?.method : 'ManagerForwardCall'; + + paramsOption = isCAContract + ? paramsOption + : { + caHash: wallet.caHash, + methodName: payload?.method, + contractAddress: payload?.contractAddress, + args: paramsOption, + }; + if (!privateKey) throw 'Invalid user information, please check'; + + const result = await callSendMethod({ + rpcUrl: chainInfo.endPoint, + chainType: 'aelf', + methodName: functionName, + paramsOption, + privateKey, + address: chainInfo.caContractAddress, + sendOptions: { onMethod: 'transactionHash' }, + }); + closePrompt({ + ...errorHandler(0), + data: result.result, + }); + } catch (error) { + console.error(error, 'error===detail'); + closePrompt({ + ...errorHandler(400001), + data: { code: ResponseCode.ERROR_IN_PARAMS }, + }); + } + }, [chainInfo, wallet, payload, txParams, isCAContract, privateKey]); + + const executeFn = useCallback(() => { + switch (txParams.method) { + case MethodsBase.SEND_TRANSACTION: + return handleTransaction(); + default: + return () => { + closePrompt({ + ...errorHandler(0), + data: 'default', + }); + }; + } + }, [handleTransaction, txParams.method]); + + useEffect(() => { + executeFn(); + }, [executeFn]); + return (
    PORTKEY
    -
    +
    + +
    The transaction is being automatically processed, please DO NOT diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.less b/packages/web-extension-did/app/web/pages/GetSignature/index.less index 3425518b2b..07c66a04c5 100644 --- a/packages/web-extension-did/app/web/pages/GetSignature/index.less +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.less @@ -51,6 +51,9 @@ border-radius: 6px; } } + .dapp-session { + margin-top: 32px; + } .btn { width: 100%; margin-top: 40px; diff --git a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx index 1fb68e3fa7..96c569d3c0 100644 --- a/packages/web-extension-did/app/web/pages/GetSignature/index.tsx +++ b/packages/web-extension-did/app/web/pages/GetSignature/index.tsx @@ -4,13 +4,16 @@ import aes from '@portkey-wallet/utils/aes'; import { Button, message } from 'antd'; import { useTranslation } from 'react-i18next'; import usePromptSearch from 'hooks/usePromptSearch'; -import { useCallback, useMemo } from 'react'; -import { useDapp, useUserInfo, useWalletInfo } from 'store/Provider/hooks'; +import { useCallback, useMemo, useState } from 'react'; +import { useUserInfo, useWalletInfo } from 'store/Provider/hooks'; import errorHandler from 'utils/errorHandler'; import { closePrompt } from 'utils/lib/serviceWorkerAction'; import { ResponseCode } from '@portkey/provider-types'; import { getWallet } from '@portkey-wallet/utils/aelf'; import ImageDisplay from 'pages/components/ImageDisplay'; +import DappSession from 'pages/components/DappSession'; +import { SessionExpiredPlan } from '@portkey-wallet/types/session'; +import { useCurrentDappInfo, useUpdateSessionInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; import './index.less'; export default function GetSignature() { @@ -24,15 +27,14 @@ export default function GetSignature() { const { t } = useTranslation(); const { passwordSeed } = useUserInfo(); const { currentNetwork } = useWalletInfo(); - const { dappMap } = useDapp(); const privateKey = useMemo( () => aes.decrypt(wallet.AESEncryptPrivateKey, passwordSeed), [passwordSeed, wallet.AESEncryptPrivateKey], ); - const curDapp = useMemo( - () => dappMap[currentNetwork]?.find((item) => item.origin === payload?.origin), - [currentNetwork, dappMap, payload?.origin], - ); + const curDapp = useCurrentDappInfo(payload?.origin); + const [open, setOpen] = useState(false); + const [exp, setExp] = useState(SessionExpiredPlan.hour1); + const updateSessionInfo = useUpdateSessionInfo(); const renderSite = useMemo( () => @@ -45,6 +47,11 @@ export default function GetSignature() { [curDapp], ); + const handleSessionChange = useCallback((flag: boolean, extTime: SessionExpiredPlan) => { + setOpen(flag); + setExp(extTime); + }, []); + const sendHandler = useCallback(async () => { try { if (!privateKey) throw 'Invalid user information, please check'; @@ -56,6 +63,17 @@ export default function GetSignature() { } const data = manager.keyPair.sign(payload?.data); + + if (open) { + updateSessionInfo({ + networkType: currentNetwork, + origin: payload?.origin, + expiredPlan: exp, + manager, + }); + } else { + updateSessionInfo({ origin: payload?.origin }); + } closePrompt({ ...errorHandler(0), data, @@ -64,7 +82,7 @@ export default function GetSignature() { console.error(error, 'error===detail'); message.error(handleErrorMessage(error)); } - }, [payload, privateKey]); + }, [currentNetwork, exp, open, payload?.data, payload?.origin, privateKey, updateSessionInfo]); return (
    @@ -74,6 +92,7 @@ export default function GetSignature() {
    Message
    {payload?.data}
    +
    {open && (
    - +
    )}
    {`Once enabled, your session key will automatically approve all requests from this DApp, on this device only. You won't see pop-up notifications asking for your approvals until the session key expires. This feature is automatically off when you disconnect from the DApp or when the session key expires. You can also manually disable it or change the expiration time.`}
    diff --git a/packages/web-extension-did/app/web/service/NotificationService/getPromptRoute.ts b/packages/web-extension-did/app/web/service/NotificationService/getPromptRoute.ts index 2852315074..8cc8259fe2 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/getPromptRoute.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/getPromptRoute.ts @@ -22,6 +22,7 @@ const routMap: { [x in keyof typeof PromptRouteTypes]?: string } = { [PromptRouteTypes.BLANK_PAGE]: '#/query-page', [PromptRouteTypes.CONNECT_WALLET]: '#/connect-wallet', [PromptRouteTypes.SEND_TRANSACTION]: '#/send-transactions', + [PromptRouteTypes.AUTO_EXECUTE_TX]: '#/auto-execute-tx', [PromptRouteTypes.GET_SIGNATURE]: '#/get-signature', [PromptRouteTypes.EXPAND_FULL_SCREEN]: '#/', [PromptRouteTypes.SETTING]: '#/setting', diff --git a/packages/web-extension-did/app/web/service/NotificationService/index.ts b/packages/web-extension-did/app/web/service/NotificationService/index.ts index fc362c3fd5..1123a38a14 100644 --- a/packages/web-extension-did/app/web/service/NotificationService/index.ts +++ b/packages/web-extension-did/app/web/service/NotificationService/index.ts @@ -99,6 +99,7 @@ export default class NotificationService { // create new notification popup const popupWindow = await this.platform.openWindow(config); + console.log('popupWindow', popupWindow, promptConfig); // Firefox currently ignores left/top for create, but it works for update if (popupWindow.left !== left && popupWindow.state !== 'fullscreen' && popupWindow.state !== 'minimized') { diff --git a/packages/web-extension-did/app/web/utils/getManager.ts b/packages/web-extension-did/app/web/utils/getManager.ts new file mode 100644 index 0000000000..5052dddd32 --- /dev/null +++ b/packages/web-extension-did/app/web/utils/getManager.ts @@ -0,0 +1,15 @@ +import InternalMessage from 'messages/InternalMessage'; +import InternalMessageTypes from 'messages/InternalMessageTypes'; +import { getWallet } from '@portkey-wallet/utils/aelf'; +import aes from '@portkey-wallet/utils/aes'; +import { getWalletInfo } from 'store/utils/getStore'; + +export default async function getManager() { + const getSeedResult = await InternalMessage.payload(InternalMessageTypes.GET_SEED).send(); + const pin = getSeedResult.data.privateKey; + const walletInfo = getWalletInfo(); + if (!walletInfo?.AESEncryptPrivateKey) return; + const privateKey = aes.decrypt(walletInfo.AESEncryptPrivateKey, pin); + if (!privateKey) return; + return getWallet(privateKey); +} diff --git a/packages/web-extension-did/app/web/utils/index.ts b/packages/web-extension-did/app/web/utils/index.ts index 049da4cfb4..7284276375 100644 --- a/packages/web-extension-did/app/web/utils/index.ts +++ b/packages/web-extension-did/app/web/utils/index.ts @@ -23,6 +23,10 @@ export const dateFormat = (ipt?: moment.MomentInput) => { return moment(ipt).format('MMM D , h:mm a').replace(',', 'at'); }; +export const dateFullFormat = (ipt?: moment.MomentInput) => { + return moment(ipt).format('YYYY-MM-DD hh:mm:ss '); +}; + export const dateFormatTransTo13 = (ipt?: moment.MomentInput) => { let time = String(ipt); while (time.length < 13) { diff --git a/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts b/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts index c48f45d45d..3f716784a1 100644 --- a/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts +++ b/packages/web-extension-did/app/web/utils/lib/SWGetReduxStore.ts @@ -3,6 +3,7 @@ import { WalletState } from '@portkey-wallet/store/store-ca/wallet/type'; import { getStoreState as getDefaultState } from 'store/utils/getStore'; import { getStoredState } from 'redux-persist'; import { walletPersistConfig, dappPersistConfig } from 'store/Provider/config'; +import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network'; export async function getSWReduxState() { return { @@ -28,3 +29,11 @@ export const getCurrentNetworkWallet = async () => { const currentNetwork = wallet.currentNetwork; return wallet.walletInfo?.caInfo?.[currentNetwork]; }; + +export const getCurrentCaHash = async () => { + const wallet = await getWalletState(); + const { walletInfo, currentNetwork } = wallet || {}; + const caInfo = walletInfo?.caInfo?.[currentNetwork]; + const originChainId = wallet.originChainId || caInfo?.originChainId; + return caInfo?.[originChainId || DefaultChainId]?.caHash; +}; diff --git a/packages/web-extension-did/app/web/utils/lib/getManager.ts b/packages/web-extension-did/app/web/utils/lib/getManager.ts new file mode 100644 index 0000000000..b8bab06120 --- /dev/null +++ b/packages/web-extension-did/app/web/utils/lib/getManager.ts @@ -0,0 +1,11 @@ +import { getWallet } from '@portkey-wallet/utils/aelf'; +import aes from '@portkey-wallet/utils/aes'; +import { getWalletState } from './SWGetReduxStore'; + +export default async function getManager(pin: string) { + const { walletInfo } = await getWalletState(); + if (!walletInfo?.AESEncryptPrivateKey) return; + const privateKey = aes.decrypt(walletInfo.AESEncryptPrivateKey, pin); + if (!privateKey) return; + return getWallet(privateKey); +} From 74e698acda63d60556c809306bcddb0126a3ddd9 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 26 Jul 2023 18:43:46 +0800 Subject: [PATCH 455/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dapp=20expire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/dapp.ts | 2 ++ .../TabsDrawer/TabsDrawerContent.tsx | 17 +++++++++-------- .../js/dapp/dappMobileOperator.ts | 2 +- .../DiscoverArchivedSection/index.tsx | 2 +- .../WalletSecurity/Dapp/DappDetail/index.tsx | 18 +++++++++++------- packages/types/session.ts | 2 ++ packages/utils/session.ts | 6 +++--- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/constants/constants-ca/dapp.ts b/packages/constants/constants-ca/dapp.ts index 0359e90cf6..290952f14c 100644 --- a/packages/constants/constants-ca/dapp.ts +++ b/packages/constants/constants-ca/dapp.ts @@ -2,6 +2,8 @@ import { SessionExpiredPlan } from '@portkey-wallet/types/session'; import { MethodsBase } from '@portkey/provider-types'; export const SessionKeyMap = { + [SessionExpiredPlan.min6]: 'In 6 mins', + [SessionExpiredPlan.min12]: 'In 12 mins', [SessionExpiredPlan.hour1]: 'In 1 hour', [SessionExpiredPlan.hour3]: 'In 3 hours', [SessionExpiredPlan.hour12]: 'In 12 hours', diff --git a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx index a2c8bd70fa..bedd5da9c3 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/TabsDrawerContent.tsx @@ -216,19 +216,20 @@ const TabsDrawerContent: React.FC = () => { - - {t('Close All')} - + + + {t('Close All')} + + + dispatch(changeDrawerOpenStatus(false))}> - - {t('Done')} - + + {t('Done')} + )} diff --git a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts index 65696b2e58..43cef98e4d 100644 --- a/packages/mobile-app-did/js/dapp/dappMobileOperator.ts +++ b/packages/mobile-app-did/js/dapp/dappMobileOperator.ts @@ -367,7 +367,7 @@ export default class DappMobileOperator extends Operator { signature: sessionInfo.signature, }); if (!valid) return valid; - return hasSessionInfoExpired(sessionInfo); + return !hasSessionInfoExpired(sessionInfo); } catch (error) { return false; } diff --git a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx index 0dd368ba32..8f93d1662c 100644 --- a/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Discover/components/DiscoverArchivedSection/index.tsx @@ -84,7 +84,7 @@ export function DiscoverArchivedSection() { {index === ArchivedTabEnum.Bookmarks && ( <> {bookmarkList?.length === 0 ? ( - + ) : ( {bookmarkList.map((item, idx) => ( diff --git a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx index 3dce4c2a1d..597df97354 100644 --- a/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletSecurity/Dapp/DappDetail/index.tsx @@ -25,7 +25,7 @@ import { SessionKeyMap } from '@portkey-wallet/constants/constants-ca/dapp'; import { SessionExpiredPlan } from '@portkey-wallet/types/session'; import { usePin } from 'hooks/store'; import { getManagerAccount } from 'utils/redux'; -import { formatTimeToStr } from '@portkey-wallet/utils/session'; +import { formatTimeToStr, hasSessionInfoExpired } from '@portkey-wallet/utils/session'; import CommonToast from 'components/CommonToast'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import { useCheckSiteIsInBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; @@ -43,16 +43,20 @@ const DappDetail: React.FC = () => { const { origin } = useRouterParams(); const dappInfo = useCurrentDappInfo(origin); const { sessionInfo } = dappInfo || {}; - - console.log('dappInfo', dappInfo?.sessionInfo?.expiredPlan); const dispatch = useAppCommonDispatch(); const { currentNetwork } = useWallet(); const updateSessionInfo = useUpdateSessionInfo(); const discoverJump = useDiscoverJumpWithNetWork(); + const isExpired = useMemo(() => { + if (!sessionInfo) return false; + return hasSessionInfoExpired(sessionInfo); + }, [sessionInfo]); + const isRememberMe = useMemo(() => { - return !!sessionInfo?.expiredPlan; - }, [sessionInfo?.expiredPlan]); + if (!!sessionInfo?.expiredPlan && !isExpired) return true; + return false; + }, [isExpired, sessionInfo]); const isInBlackList = useMemo( () => checkOriginInBlackList(dappInfo?.origin || ''), @@ -153,7 +157,7 @@ const DappDetail: React.FC = () => { )} - {!isInBlackList && isRememberMe && ( + {!isExpired && !isInBlackList && isRememberMe && ( {t('Session key expiration')} @@ -164,7 +168,7 @@ const DappDetail: React.FC = () => { )} - {!isInBlackList && isRememberMe && ( + {!isExpired && !isInBlackList && isRememberMe && ( {t('Expiration time')} diff --git a/packages/types/session.ts b/packages/types/session.ts index d3c5db9971..5d0073db62 100644 --- a/packages/types/session.ts +++ b/packages/types/session.ts @@ -2,6 +2,8 @@ import { Timestamp } from './index'; export enum SessionExpiredPlan { // hours + min6 = 0.1, + min12 = 0.2, hour1 = 1, hour3 = 3, hour12 = 12, diff --git a/packages/utils/session.ts b/packages/utils/session.ts index 017edecbc9..2b50764d3b 100644 --- a/packages/utils/session.ts +++ b/packages/utils/session.ts @@ -44,9 +44,9 @@ export function verifySession(params: IVerifySessionParams) { export function hasSessionInfoExpired(sessionInfo: SessionInfo) { // always - if (sessionInfo.expiredPlan === SessionExpiredPlan.always) return true; - if (Date.now() < sessionInfo.expiredTime) return true; - return false; + if (sessionInfo.expiredPlan === SessionExpiredPlan.always) return false; + if (Date.now() < sessionInfo.expiredTime) return false; + return true; } export function formatExpiredTime(plan: SessionExpiredPlan) { From 1d8f8f438ab023b855f3afc7ea3e3c46b8fb0495 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 26 Jul 2023 19:32:00 +0800 Subject: [PATCH 456/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20auto-lo?= =?UTF-8?q?ck=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/components/SelectOverlay/index.tsx | 51 ++++++------------- packages/store/store-ca/cms/actions.ts | 2 - 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/packages/mobile-app-did/js/components/SelectOverlay/index.tsx b/packages/mobile-app-did/js/components/SelectOverlay/index.tsx index ac184ea589..da1d140c9c 100644 --- a/packages/mobile-app-did/js/components/SelectOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/SelectOverlay/index.tsx @@ -1,10 +1,9 @@ import React from 'react'; import OverlayModal from 'components/OverlayModal'; -import { StyleSheet, TouchableOpacity, Text, View } from 'react-native'; +import { StyleSheet, TouchableOpacity, Text, View, ScrollView } from 'react-native'; import { TextL, TextM } from 'components/CommonText'; import { ModalBody } from 'components/ModalBody'; import { defaultColors } from 'assets/theme'; -import fonts from 'assets/theme/fonts'; import { pTd } from 'utils/unit'; import Svg from 'components/Svg'; import { useLanguage } from 'i18n/hooks'; @@ -20,31 +19,28 @@ type SelectModalProps = { const SelectModal = ({ title = '', value = 1, dataList = [], onChangeValue }: SelectModalProps) => { const { t } = useLanguage(); return ( - - {title} - - - + + {dataList.map(ele => ( { onChangeValue?.(ele); OverlayModal.hide(); }}> - {t(ele.label)} - {ele.value === value ? : } + {ele.label} + {value === ele.value && } ))} - + ); }; export const showSelectModal = (props: SelectModalProps) => { OverlayModal.show(, { - position: 'center', + position: 'bottom', }); }; @@ -53,34 +49,17 @@ export default { }; export const styles = StyleSheet.create({ - modalStyle: { - width: pTd(287), - }, - title: { - textAlign: 'center', - height: pTd(22), - lineHeight: pTd(22), - marginVertical: pTd(13), - ...fonts.mediumFont, - }, - divider: { - width: '100%', - height: StyleSheet.hairlineWidth, - backgroundColor: defaultColors.border6, - }, - listWrap: { - paddingTop: pTd(8), - paddingBottom: pTd(8), + wrapStyle: { + paddingHorizontal: pTd(20), + flex: 1, }, item: { - width: '100%', - paddingLeft: pTd(24), - paddingRight: pTd(24), - height: pTd(44), - display: 'flex', + height: 72, + alignItems: 'center', flexDirection: 'row', justifyContent: 'space-between', - alignItems: 'center', + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: defaultColors.border6, }, label: { fontSize: pTd(14), diff --git a/packages/store/store-ca/cms/actions.ts b/packages/store/store-ca/cms/actions.ts index 2310778611..c85871fc20 100644 --- a/packages/store/store-ca/cms/actions.ts +++ b/packages/store/store-ca/cms/actions.ts @@ -120,8 +120,6 @@ export const getRememberMeBlackListAsync = createAsyncThunk< }, }); - console.log('getRememberMeBlackListSites result', result); - if (result.data.rememberMeBlackListSites) { return { rememberMeBlackListMap: { From 66a2142527f25d78e40b04a2d5ae1c3b2610f843 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 27 Jul 2023 10:19:26 +0800 Subject: [PATCH 457/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20verifie?= =?UTF-8?q?r=20timeout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/pages/RegisterStart/index.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx index 60560f7435..779b1a5e94 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx @@ -175,26 +175,28 @@ export default function RegisterStart() { setLoading(true, 'Allocating verifier on-chain...'); - const loadingTimeout = new Promise((resolve) => { + await new Promise((resolve) => { timer.current = setTimeout(() => { clearTimeout(timer.current); - setLoading(false); resolve('timeout down'); }, 2000); }); // Get the assigned verifier data from the backend api and guaranteed loading display 2s - const [verifierReq, _timeout] = await Promise.all([ - request.verify.getVerifierServer({ + try { + const verifierReq = await request.verify.getVerifierServer({ params: { chainId: DefaultChainId, }, - }), - loadingTimeout, - ]); + }); + setLoading(false); - setVerifierItem(verifierReq); - confirmRegisterOrLogin(data, verifierReq); + setVerifierItem(verifierReq); + confirmRegisterOrLogin(data, verifierReq); + } catch (error) { + message.error(handleErrorMessage(error, 'Get verifier failed')); + throw handleErrorMessage(error, 'Get verifier failed'); + } }, [confirmRegisterOrLogin, dispatch, saveState, setLoading], ); From 5a9a9b5188ef6271306d505027d1019af682ede0 Mon Sep 17 00:00:00 2001 From: ykx Date: Thu, 27 Jul 2023 10:41:27 +0800 Subject: [PATCH 458/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20verifie?= =?UTF-8?q?r=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/web-extension-did/app/web/hooks/useVerifier.ts | 4 ++-- .../web-extension-did/app/web/pages/RegisterStart/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web-extension-did/app/web/hooks/useVerifier.ts b/packages/web-extension-did/app/web/hooks/useVerifier.ts index a78901ec74..10a7cd31f6 100644 --- a/packages/web-extension-did/app/web/hooks/useVerifier.ts +++ b/packages/web-extension-did/app/web/hooks/useVerifier.ts @@ -19,8 +19,8 @@ import { LoginInfo } from 'store/reducers/loginCache/type'; /** * Provides two verification processes * @returns [checkAuth, sendVerifyCodeHandler] - * @desc checkAuth: for social login - * @desc sendVerifyCodeHandler: for Ordinary email address and mobile phone number + * checkAuth: for social login + * sendVerifyCodeHandler: for Ordinary email address and mobile phone number */ const useCheckVerifier = () => { const { setLoading } = useLoading(); diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx index 779b1a5e94..c7f7e6af62 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx @@ -173,7 +173,7 @@ export default function RegisterStart() { saveState(data); dispatch(resetGuardians()); - setLoading(true, 'Allocating verifier on-chain...'); + setLoading(true, 'Assigning a verifier on-chain…'); await new Promise((resolve) => { timer.current = setTimeout(() => { From a77c71abb927aa98031f44a2c6785a5cf7a52be9 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 27 Jul 2023 11:18:26 +0800 Subject: [PATCH 459/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20verifie?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/payment/index.ts | 4 +- packages/api/api-did/verification/index.ts | 1 + .../js/components/Loading/index.tsx | 18 +- packages/mobile-app-did/js/hooks/guardian.ts | 5 +- .../js/hooks/{login.ts => login.tsx} | 205 +++++++++++++-- .../pages/Guardian/SelectVerifier/index.tsx | 236 ------------------ .../components/VerifierOverlay/index.tsx | 55 ---- .../components/VerifierOverlay/styles.ts | 30 --- .../mobile-app-did/js/pages/Guardian/index.ts | 2 - .../js/pages/Login/components/Email.tsx | 11 +- .../js/pages/Login/components/Phone.tsx | 11 +- .../js/pages/Login/components/Referral.tsx | 19 +- .../My/Guardian/GuardianDetail/index.tsx | 7 +- .../pages/My/Guardian/GuardianEdit/index.tsx | 7 +- .../js/pages/Pin/SetPin/index.tsx | 17 +- packages/types/verifier.ts | 2 - 16 files changed, 252 insertions(+), 378 deletions(-) rename packages/mobile-app-did/js/hooks/{login.ts => login.tsx} (66%) delete mode 100644 packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/styles.ts diff --git a/packages/api/api-did/payment/index.ts b/packages/api/api-did/payment/index.ts index 091116d949..82b707d167 100644 --- a/packages/api/api-did/payment/index.ts +++ b/packages/api/api-did/payment/index.ts @@ -15,6 +15,6 @@ export default { config: { method: 'GET' }, }, updateAchOrder: '/api/app/thirdPart/order/alchemy', - updateAlchemyOrderTxHash: 'api/app/thirdPart/alchemy/txHash', - sendSellTransaction: 'api/app/thirdPart/alchemy/transaction', + updateAlchemyOrderTxHash: '/api/app/thirdPart/alchemy/txHash', + sendSellTransaction: '/api/app/thirdPart/alchemy/transaction', } as const; diff --git a/packages/api/api-did/verification/index.ts b/packages/api/api-did/verification/index.ts index 2888f07092..ab1cd36b9c 100644 --- a/packages/api/api-did/verification/index.ts +++ b/packages/api/api-did/verification/index.ts @@ -10,4 +10,5 @@ export default { config: { method: 'GET' }, }, checkGoogleRecaptcha: '/api/app/account/isGoogleRecaptchaOpen', + getVerifierServer: '/api/app/account/getVerifierServer', } as const; diff --git a/packages/mobile-app-did/js/components/Loading/index.tsx b/packages/mobile-app-did/js/components/Loading/index.tsx index 53adbe1429..f5c657b22b 100644 --- a/packages/mobile-app-did/js/components/Loading/index.tsx +++ b/packages/mobile-app-did/js/components/Loading/index.tsx @@ -31,7 +31,7 @@ function LoadingBody({ text }: { text?: string; position?: LoadingPositionType; } export default class Loading extends React.Component { - static show(options?: ShowOptionsType) { + static show(options?: ShowOptionsType): number { const { text = 'Loading...', iconType = 'loading', isMaskTransparent = true, overlayProps = {} } = options || {}; Keyboard.dismiss(); Loading.hide(); @@ -45,7 +45,9 @@ export default class Loading extends React.Component { ); - elements.push(Overlay.show(overlayView)); + const key = Overlay.show(overlayView); + elements.push(key); + return key; // timer && clearBackgroundTimeout(timer); // timer = setBackgroundTimeout(() => { // Loading.hide(); @@ -57,12 +59,18 @@ export default class Loading extends React.Component { Loading.show(options); } - static hide() { + static hide(key?: number) { timer && clearTimeout(timer); timer = null; elements = elements.filter(item => item); // Discard invalid data - const topItem = elements.pop(); - Overlay.hide(topItem); + let keyItem: number | undefined; + if (key !== undefined) { + keyItem = elements.find(item => item === key); + elements = elements.filter(item => item !== key); + } else { + keyItem = elements.pop(); + } + keyItem !== undefined && Overlay.hide(keyItem); } static destroy() { diff --git a/packages/mobile-app-did/js/hooks/guardian.ts b/packages/mobile-app-did/js/hooks/guardian.ts index a179d9f114..fd72e265fb 100644 --- a/packages/mobile-app-did/js/hooks/guardian.ts +++ b/packages/mobile-app-did/js/hooks/guardian.ts @@ -76,10 +76,7 @@ export const useGetVerifierServers = () => { const caContract = await getCurrentCAViewContract(chainInfo); const res = await caContract?.callViewMethod('GetVerifierServers', ''); if (res && !res.error) { - const verifierList: VerifierItem[] = res.data.verifierServers.map((item: VerifierItem) => ({ - ...item, - url: item.endPoints[0], - })); + const verifierList: VerifierItem[] = res.data.verifierServers; dispatch(setVerifierListAction(verifierList)); return verifierList; } else { diff --git a/packages/mobile-app-did/js/hooks/login.ts b/packages/mobile-app-did/js/hooks/login.tsx similarity index 66% rename from packages/mobile-app-did/js/hooks/login.ts rename to packages/mobile-app-did/js/hooks/login.tsx index 9ed2f6b340..fddbb94ac4 100644 --- a/packages/mobile-app-did/js/hooks/login.ts +++ b/packages/mobile-app-did/js/hooks/login.tsx @@ -1,5 +1,6 @@ import { CurrentWalletType, + useCurrentWalletInfo, useOriginChainId, useOtherNetworkLogged, useWallet, @@ -13,12 +14,18 @@ import { setOriginChainId, } from '@portkey-wallet/store/store-ca/wallet/actions'; import { CAInfo, LoginType, ManagerInfo } from '@portkey-wallet/types/types-ca/wallet'; -import { AuthenticationInfo, VerificationType, VerifierInfo } from '@portkey-wallet/types/verifier'; +import { + AuthenticationInfo, + OperationTypeEnum, + VerificationType, + VerifierInfo, + VerifierItem, +} from '@portkey-wallet/types/verifier'; import { handleErrorCode, sleep } from '@portkey-wallet/utils'; import Loading from 'components/Loading'; import AElf from 'aelf-sdk'; import { request } from 'api'; -import { useCallback, useRef } from 'react'; +import React, { useCallback, useRef } from 'react'; import { useAppDispatch } from 'store/hooks'; import useBiometricsReady from './useBiometrics'; import navigationService from 'utils/navigationService'; @@ -42,6 +49,12 @@ import { useResetStore } from '@portkey-wallet/hooks/hooks-ca'; import { ChainId } from '@portkey-wallet/types'; import ActionSheet from 'components/ActionSheet'; import { resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; +import { request as globalRequest } from '@portkey-wallet/api/api-did'; +import { useVerifyToken } from './authentication'; +import { verification } from 'utils/api'; +import { Text } from 'react-native'; +import { TextL } from 'components/CommonText'; +import fonts from 'assets/theme/fonts'; export function useOnResultFail() { const dispatch = useAppDispatch(); @@ -241,29 +254,167 @@ export function useGoGuardianApproval(isLogin?: boolean) { ); } +type LoginConfirmParams = { + showLoginAccount: string; + loginAccount: string; + loginType: LoginType; + authenticationInfo?: AuthenticationInfo; +}; + +type LoginAuthParams = LoginConfirmParams & { + selectedVerifier: VerifierItem; + chainId: ChainId; +}; + +const ALLOCATE_SLEEP_TIME = 2 * 1000; export function useGoSelectVerifier(isLogin?: boolean) { const dispatch = useAppDispatch(); - return useCallback( - ({ - showLoginAccount, - loginAccount, - loginType, - authenticationInfo, - }: { - showLoginAccount: string; - loginAccount: string; - loginType: LoginType; - authenticationInfo?: AuthenticationInfo; - }) => { - const onConfirm = () => { - dispatch(setOriginChainId(DefaultChainId)); - navigationService.navigate('SelectVerifier', { - showLoginAccount, - loginAccount, - loginType, - authenticationInfo, + const pin = usePin(); + const { address } = useCurrentWalletInfo(); + const verifyToken = useVerifyToken(); + const onRequestOrSetPin = useOnRequestOrSetPin(); + + const onConfirmAuth = useCallback( + async ({ loginAccount, loginType, authenticationInfo, selectedVerifier, chainId }: LoginAuthParams) => { + const isRequestResult = !!(pin && address); + + const loadingKey = Loading.show(isRequestResult ? { text: 'Creating address on the chain...' } : undefined); + + try { + const rst = await verifyToken(loginType, { + accessToken: authenticationInfo?.[loginAccount || ''], + id: loginAccount, + verifierId: selectedVerifier?.id, + chainId, + operationType: OperationTypeEnum.register, }); - }; + onRequestOrSetPin({ + showLoading: !isRequestResult, + managerInfo: { + verificationType: VerificationType.register, + loginAccount: loginAccount, + type: loginType, + }, + verifierInfo: { ...rst, verifierId: selectedVerifier?.id }, + }); + } catch (error) { + Loading.hide(loadingKey); + CommonToast.failError(error); + } + !isRequestResult && Loading.hide(loadingKey); + }, + [address, onRequestOrSetPin, pin, verifyToken], + ); + + const onDefaultConfirm = useCallback( + async ({ loginAccount, loginType, selectedVerifier, chainId }: LoginAuthParams) => { + const loadingKey = Loading.show(); + try { + const requestCodeResult = await verification.sendVerificationCode({ + params: { + type: LoginType[loginType], + guardianIdentifier: loginAccount, + verifierId: selectedVerifier?.id, + chainId, + operationType: OperationTypeEnum.register, + }, + }); + if (requestCodeResult.verifierSessionId) { + navigationService.navigate('VerifierDetails', { + requestCodeResult, + verificationType: VerificationType.register, + guardianItem: { + isLoginAccount: true, + verifier: selectedVerifier, + guardianAccount: loginAccount, + guardianType: loginType, + }, + }); + } else { + throw new Error('send fail'); + } + } catch (error) { + CommonToast.failError(error); + } + Loading.hide(loadingKey); + }, + [], + ); + + const onConfirm = useCallback( + async (confirmParams: LoginConfirmParams) => { + const { loginType } = confirmParams; + dispatch(setOriginChainId(DefaultChainId)); + + const loadingKey = Loading.show({ + text: 'Assigning a verifier on-chain...', + }); + try { + await sleep(ALLOCATE_SLEEP_TIME); + const result = await globalRequest.verify.getVerifierServer({ + params: { + chainId: DefaultChainId, + }, + }); + Loading.hide(loadingKey); + + const allotVerifier: VerifierItem = result; + if (!allotVerifier || allotVerifier.id === undefined) { + throw new Error('No verifier found'); + } + switch (loginType) { + case LoginType.Apple: + case LoginType.Google: + onConfirmAuth({ + ...confirmParams, + selectedVerifier: allotVerifier, + chainId: DefaultChainId, + }); + break; + default: { + ActionSheet.alert({ + title2: ( + + {`${allotVerifier?.name} will send a verification code to `} + {confirmParams.showLoginAccount || ''} + {` to verify your ${ + loginType === LoginType.Phone ? 'phone number' : 'email address' + }.`} + + ), + buttons: [ + { + title: 'Cancel', + // type: 'solid', + type: 'outline', + }, + { + title: 'Confirm', + onPress: () => { + onDefaultConfirm({ + ...confirmParams, + selectedVerifier: allotVerifier, + chainId: DefaultChainId, + }); + }, + }, + ], + }); + break; + } + } + } catch (error) { + Loading.hide(loadingKey); + CommonToast.failError(error); + } + }, + [dispatch, onConfirmAuth, onDefaultConfirm], + ); + const onConfirmRef = useRef(onConfirm); + onConfirmRef.current = onConfirm; + + return useCallback( + async (params: LoginConfirmParams) => { if (isLogin) { ActionSheet.alert({ title: 'Continue with this account?', @@ -272,15 +423,15 @@ export function useGoSelectVerifier(isLogin?: boolean) { { title: 'Cancel', type: 'outline' }, { title: 'Confirm', - onPress: () => onConfirm(), + onPress: () => onConfirmRef.current(params), }, ], }); } else { - onConfirm(); + await onConfirmRef.current(params); } }, - [dispatch, isLogin], + [isLogin], ); } @@ -315,7 +466,7 @@ export function useOnLogin(isLogin?: boolean) { authenticationInfo, }); } else { - goSelectVerifier({ + await goSelectVerifier({ showLoginAccount: showLoginAccount || loginAccount, loginAccount, loginType, @@ -324,7 +475,7 @@ export function useOnLogin(isLogin?: boolean) { } } catch (error) { if (handleErrorCode(error) === '3002') { - goSelectVerifier({ + await goSelectVerifier({ showLoginAccount: showLoginAccount || loginAccount, loginAccount, loginType, diff --git a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx b/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx deleted file mode 100644 index a3409b4045..0000000000 --- a/packages/mobile-app-did/js/pages/Guardian/SelectVerifier/index.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { StyleSheet, Text } from 'react-native'; -import PageContainer from 'components/PageContainer'; -import { TextL, TextM, TextS, TextXXXL } from 'components/CommonText'; -import GStyles from 'assets/theme/GStyles'; -import Svg from 'components/Svg'; -import Touchable from 'components/Touchable'; -import { View } from 'react-native'; -import CommonButton from 'components/CommonButton'; -import { useLanguage } from 'i18n/hooks'; -import ActionSheet from 'components/ActionSheet'; -import { BorderStyles, FontStyles } from 'assets/theme/styles'; -import ListItem from 'components/ListItem'; -import { pTd } from 'utils/unit'; -import fonts from 'assets/theme/fonts'; -import navigationService from 'utils/navigationService'; -import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; -import CommonToast from 'components/CommonToast'; -import Loading from 'components/Loading'; -import { useVerifierList } from '@portkey-wallet/hooks/hooks-ca/network'; -import VerifierOverlay from '../components/VerifierOverlay'; -import { VerifierImage } from '../components/VerifierImage'; -import { LoginType } from '@portkey-wallet/types/types-ca/wallet'; -import myEvents from 'utils/deviceEvent'; -import { verification } from 'utils/api'; -import { AuthenticationInfo, VerificationType, OperationTypeEnum, VerifierItem } from '@portkey-wallet/types/verifier'; -import { useVerifyToken } from 'hooks/authentication'; -import { useCurrentWalletInfo, useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { useOnRequestOrSetPin } from 'hooks/login'; -import { usePin } from 'hooks/store'; - -export type RouterParams = { - loginAccount: string; - loginType: LoginType; - authenticationInfo?: AuthenticationInfo; - showLoginAccount?: string; -}; - -const ScrollViewProps = { disabled: true }; -export default function SelectVerifier() { - const { t } = useLanguage(); - const verifierList = useVerifierList(); - - const [selectedVerifier, setSelectedVerifier] = useState(verifierList[0]); - - const { loginAccount, loginType, authenticationInfo, showLoginAccount } = useRouterParams(); - const verifyToken = useVerifyToken(); - const originChainId = useOriginChainId(); - const onRequestOrSetPin = useOnRequestOrSetPin(); - const pin = usePin(); - const { address } = useCurrentWalletInfo(); - const onConfirmAuth = useCallback(async () => { - const isRequestResult = !!(pin && address); - Loading.show(isRequestResult ? { text: 'Creating address on the chain...' } : undefined); - try { - const rst = await verifyToken(loginType, { - accessToken: authenticationInfo?.[loginAccount || ''], - id: loginAccount, - verifierId: selectedVerifier?.id, - chainId: originChainId, - operationType: OperationTypeEnum.register, - }); - onRequestOrSetPin({ - showLoading: !isRequestResult, - managerInfo: { - verificationType: VerificationType.register, - loginAccount: loginAccount, - type: loginType, - }, - verifierInfo: { ...rst, verifierId: selectedVerifier?.id }, - }); - } catch (error) { - Loading.hide(); - CommonToast.failError(error); - } - !isRequestResult && Loading.hide(); - }, [ - address, - authenticationInfo, - loginAccount, - loginType, - onRequestOrSetPin, - originChainId, - pin, - selectedVerifier?.id, - verifyToken, - ]); - const onDefaultConfirm = useCallback(() => { - const confirm = async () => { - try { - Loading.show(); - const requestCodeResult = await verification.sendVerificationCode({ - params: { - type: LoginType[loginType], - guardianIdentifier: loginAccount, - verifierId: selectedVerifier?.id, - chainId: originChainId, - operationType: OperationTypeEnum.register, - }, - }); - if (requestCodeResult.verifierSessionId) { - navigationService.navigate('VerifierDetails', { - requestCodeResult, - verificationType: VerificationType.register, - guardianItem: { - isLoginAccount: true, - verifier: selectedVerifier, - guardianAccount: loginAccount, - guardianType: loginType, - }, - }); - } else { - throw new Error('send fail'); - } - } catch (error) { - CommonToast.failError(error); - } - Loading.hide(); - }; - - ActionSheet.alert({ - title2: ( - - {`${selectedVerifier?.name} will send a verification code to `} - {showLoginAccount || loginAccount} - {` to verify your ${loginType === LoginType.Phone ? 'phone number' : 'email address'}.`} - - ), - buttons: [ - { - title: t('Cancel'), - // type: 'solid', - type: 'outline', - }, - { - title: t('Confirm'), - onPress: confirm, - }, - ], - }); - }, [loginAccount, loginType, originChainId, selectedVerifier, showLoginAccount, t]); - const onConfirm = useCallback(async () => { - switch (loginType) { - case LoginType.Apple: - case LoginType.Google: - onConfirmAuth(); - break; - default: { - onDefaultConfirm(); - break; - } - } - }, [loginType, onConfirmAuth, onDefaultConfirm]); - return ( - { - myEvents.clearSignupInput.emit(); - navigationService.goBack(); - }}> - - Select verifier - - {t( - 'Verifiers protect your account and help you recover your assets when they are subject to risks. Please note: The more diversified your verifiers are, the higher security your assets enjoy.', - )} - - - selectedVerifier && - VerifierOverlay.showVerifierList({ - verifierList, - selectedVerifier, - callBack: setSelectedVerifier, - }) - } - titleLeftElement={ - - } - titleStyle={[GStyles.flexRowWrap, GStyles.itemCenter]} - titleTextStyle={styles.titleTextStyle} - style={[styles.selectedItem, BorderStyles.border1]} - title={selectedVerifier?.name || ''} - rightElement={} - /> - Popular - - {verifierList.slice(0, 3).map(item => { - return ( - setSelectedVerifier(item)}> - - {item.name} - - ); - })} - - - - {t('Confirm')} - - - ); -} - -const styles = StyleSheet.create({ - containerStyles: { - justifyContent: 'space-between', - paddingBottom: 16, - }, - selectedItem: { - borderWidth: StyleSheet.hairlineWidth, - paddingVertical: 13, - marginTop: 24, - marginBottom: 48, - }, - titleTextStyle: { - marginLeft: pTd(8), - fontSize: pTd(14), - }, - verifierRow: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-around', - marginTop: 20, - }, - verifierTitle: { - marginTop: 8, - textAlign: 'center', - }, -}); diff --git a/packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/index.tsx b/packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/index.tsx deleted file mode 100644 index 8f06528bd8..0000000000 --- a/packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import OverlayModal from 'components/OverlayModal'; -import { Keyboard, ScrollView } from 'react-native'; -import Touchable from 'components/Touchable'; -import styles from './styles'; -import GStyles from 'assets/theme/GStyles'; -import Svg from 'components/Svg'; -import { TextXL } from 'components/CommonText'; -import { pTd } from 'utils/unit'; -import OverlayBody from 'components/OverlayModal/OverlayBody'; -import { VerifierItem } from '@portkey-wallet/types/verifier'; -import { VerifierImage } from '../VerifierImage'; - -type VerifierListProps = { - selectedVerifier: VerifierItem; - verifierList: VerifierItem[]; - callBack: (item: VerifierItem) => void; -}; - -const VerifierList = ({ verifierList, callBack, selectedVerifier }: VerifierListProps) => { - return ( - - - {verifierList.map(item => { - return ( - { - OverlayModal.hide(); - callBack(item); - }}> - - {item.name} - {selectedVerifier.name === item.name && ( - - )} - - ); - })} - - - ); -}; - -const showVerifierList = (params: VerifierListProps) => { - Keyboard.dismiss(); - OverlayModal.show(, { - position: 'bottom', - }); -}; - -export default { - showVerifierList, -}; diff --git a/packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/styles.ts b/packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/styles.ts deleted file mode 100644 index b2f2e67c1b..0000000000 --- a/packages/mobile-app-did/js/pages/Guardian/components/VerifierOverlay/styles.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { defaultColors } from 'assets/theme'; -import { StyleSheet } from 'react-native'; -import { screenWidth, windowHeight } from '@portkey-wallet/utils/mobile/device'; -import { pTd } from 'utils/unit'; - -const styles = StyleSheet.create({ - centerBox: { - maxHeight: windowHeight * 0.7, - maxWidth: 500, - width: screenWidth - 48, - backgroundColor: defaultColors.bg1, - borderRadius: pTd(8), - padding: pTd(24), - }, - itemRow: { - paddingVertical: 20, - paddingHorizontal: pTd(24), - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: defaultColors.border4, - }, - itemName: { - marginLeft: 12, - }, - itemIcon: { - position: 'absolute', - right: 26, - }, -}); - -export default styles; diff --git a/packages/mobile-app-did/js/pages/Guardian/index.ts b/packages/mobile-app-did/js/pages/Guardian/index.ts index 6ea8d455bf..da9e033919 100644 --- a/packages/mobile-app-did/js/pages/Guardian/index.ts +++ b/packages/mobile-app-did/js/pages/Guardian/index.ts @@ -1,9 +1,7 @@ -import SelectVerifier from './SelectVerifier'; import GuardianApproval from './GuardianApproval'; import VerifierDetails from './VerifierDetails'; const stackNav = [ - { name: 'SelectVerifier', component: SelectVerifier }, { name: 'GuardianApproval', component: GuardianApproval }, { name: 'VerifierDetails', component: VerifierDetails }, ] as const; diff --git a/packages/mobile-app-did/js/pages/Login/components/Email.tsx b/packages/mobile-app-did/js/pages/Login/components/Email.tsx index 21398e0ef5..6f0d6659a0 100644 --- a/packages/mobile-app-did/js/pages/Login/components/Email.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/Email.tsx @@ -41,17 +41,22 @@ export default function Email({ const [loginAccount, setLoginAccount] = useState(); const [errorMessage, setErrorMessage] = useState(); const onLogin = useOnLogin(type === PageType.login); + + const isLoginRef = useRef(false); const onPageLogin = useCallback(async () => { - const message = checkEmail(loginAccount); + if (isLoginRef.current) return; + isLoginRef.current = true; + const message = checkEmail(loginAccount) || undefined; setErrorMessage(message); if (message) return; - Loading.show(); + const loadingKey = Loading.show(); try { await onLogin({ loginAccount: loginAccount as string }); } catch (error) { setErrorMessage(handleErrorMessage(error)); } - Loading.hide(); + Loading.hide(loadingKey); + isLoginRef.current = false; }, [loginAccount, onLogin]); useEffectOnce(() => { diff --git a/packages/mobile-app-did/js/pages/Login/components/Phone.tsx b/packages/mobile-app-did/js/pages/Login/components/Phone.tsx index e35d85a8fe..ae1aad8a6a 100644 --- a/packages/mobile-app-did/js/pages/Login/components/Phone.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/Phone.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import { View } from 'react-native'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { BGStyles } from 'assets/theme/styles'; @@ -39,8 +39,12 @@ export default function Phone({ const [errorMessage, setErrorMessage] = useState(); const { localPhoneCountryCode: country } = usePhoneCountryCode(); const onLogin = useOnLogin(type === PageType.login); + + const isLoginRef = useRef(false); const onPageLogin = useCallback(async () => { - Loading.show(); + if (isLoginRef.current) return; + isLoginRef.current = true; + const loadingKey = Loading.show(); try { await onLogin({ showLoginAccount: `+${country.code} ${loginAccount}`, @@ -50,7 +54,8 @@ export default function Phone({ } catch (error) { setErrorMessage(handleErrorMessage(error)); } - Loading.hide(); + Loading.hide(loadingKey); + isLoginRef.current = false; }, [country.code, loginAccount, onLogin]); useEffectOnce(() => { diff --git a/packages/mobile-app-did/js/pages/Login/components/Referral.tsx b/packages/mobile-app-did/js/pages/Login/components/Referral.tsx index 692295062a..d9894418a3 100644 --- a/packages/mobile-app-did/js/pages/Login/components/Referral.tsx +++ b/packages/mobile-app-did/js/pages/Login/components/Referral.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useRef } from 'react'; import { View, Text, Image, StyleSheet } from 'react-native'; import { BGStyles, FontStyles } from 'assets/theme/styles'; import navigationService from 'utils/navigationService'; @@ -44,9 +44,13 @@ export default function Referral({ const { googleSign } = useGoogleAuthentication(); const onLogin = useOnLogin(type === PageType.login); + + const isLoginRef = useRef(false); const onAppleSign = useCallback(async () => { + if (isLoginRef.current) return; + isLoginRef.current = true; + const loadingKey = Loading.show(); try { - Loading.show(); const userInfo = await appleSign(); await onLogin({ loginAccount: userInfo.user.id, @@ -56,11 +60,15 @@ export default function Referral({ } catch (error) { CommonToast.failError(error); } - Loading.hide(); + Loading.hide(loadingKey); + isLoginRef.current = false; }, [appleSign, onLogin]); const onGoogleSign = useCallback(async () => { + if (isLoginRef.current) return; + isLoginRef.current = true; + + const loadingKey = Loading.show(); try { - Loading.show(); const userInfo = await googleSign(); await onLogin({ loginAccount: userInfo.user.id, @@ -70,7 +78,8 @@ export default function Referral({ } catch (error) { CommonToast.failError(error); } - Loading.hide(); + Loading.hide(loadingKey); + isLoginRef.current = false; }, [googleSign, onLogin]); return ( diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx index 1fed662985..93d56d55fa 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianDetail/index.tsx @@ -232,7 +232,12 @@ export default function GuardianDetail() { - + {guardian?.verifier?.name || ''} diff --git a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx index 96b25933a5..09d5a1c7f1 100644 --- a/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Guardian/GuardianEdit/index.tsx @@ -605,7 +605,12 @@ const GuardianEdit: React.FC = () => { }} titleLeftElement={ selectedVerifier && ( - + ) } titleStyle={[GStyles.flexRowWrap, GStyles.itemCenter]} diff --git a/packages/mobile-app-did/js/pages/Pin/SetPin/index.tsx b/packages/mobile-app-did/js/pages/Pin/SetPin/index.tsx index 17e34936d6..1e05a2ab45 100644 --- a/packages/mobile-app-did/js/pages/Pin/SetPin/index.tsx +++ b/packages/mobile-app-did/js/pages/Pin/SetPin/index.tsx @@ -12,6 +12,7 @@ import { AElfWallet } from '@portkey-wallet/types/aelf'; import PinContainer from 'components/PinContainer'; import { GuardiansApproved } from 'pages/Guardian/types'; import { StyleSheet } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; type RouterParams = { oldPin?: string; @@ -32,13 +33,15 @@ const MessageMap: any = { [VerificationType.addManager]: 'After returning, you need to scan the code again to authorize login', }; const RouterMap: any = { - [VerificationType.register]: 'SelectVerifier', + [VerificationType.register]: 'LoginPortkey', [VerificationType.communityRecovery]: 'GuardianApproval', [VerificationType.addManager]: 'LoginPortkey', }; export default function SetPin() { const { oldPin, managerInfo, caInfo, walletInfo, verifierInfo, guardiansApproved } = useRouterParams(); const digitInput = useRef(); + const navigation = useNavigation(); + useEffectOnce(() => { const listener = myEvents.clearSetPin.addListener(() => digitInput.current?.reset()); return () => listener.remove(); @@ -55,6 +58,16 @@ export default function SetPin() { title: 'Yes', onPress: () => { if (managerInfo.verificationType === VerificationType.addManager) myEvents.clearQRWallet.emit(); + if (managerInfo.verificationType === VerificationType.register) { + const routesArr = navigation.getState().routes; + const isSignUpPageExist = routesArr.some(item => item.name === 'SignupPortkey'); + if (isSignUpPageExist) { + navigationService.navigate('SignupPortkey'); + } else { + navigationService.navigate('LoginPortkey'); + } + return; + } navigationService.navigate(RouterMap[managerInfo.verificationType]); }, }, @@ -65,7 +78,7 @@ export default function SetPin() { return navigationService.navigate(RouterMap[managerInfo.verificationType]); navigationService.goBack(); - }, [managerInfo, oldPin]); + }, [managerInfo, navigation, oldPin]); return ( Date: Thu, 27 Jul 2023 12:04:27 +0800 Subject: [PATCH 460/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20verifie?= =?UTF-8?q?r=20item=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/store/store-ca/guardians/index.test.ts | 14 -------------- .../app/web/pages/RegisterStart/index.tsx | 12 ++---------- .../web/pages/components/VerifierPage/index.tsx | 2 +- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/packages/store/store-ca/guardians/index.test.ts b/packages/store/store-ca/guardians/index.test.ts index 1c8a6269c3..2b4cc38fea 100644 --- a/packages/store/store-ca/guardians/index.test.ts +++ b/packages/store/store-ca/guardians/index.test.ts @@ -23,11 +23,9 @@ const mockUserGuardiansListItem1 = { identifierHash: 'identifierHash1@q.com', salt: 'salt', verifier: { - endPoints: ['http://192.168.0.250:5555'], id: 'Gauss', imageUrl: 'https://x/Gauss.png', name: 'Gauss', - verifierAddresses: ['5M5sG4'], }, guardianIdentifier: '1@q.com', isLoginGuardian: true, @@ -43,11 +41,9 @@ const mockUserGuardiansListItem2 = { identifierHash: 'identifierHash2@q.com', salt: 'salt', verifier: { - endPoints: ['http://192.168.0.250:5555'], id: 'Gauss', imageUrl: 'https://x/Gauss.png', name: 'Gauss', - verifierAddresses: ['5M5sG4'], }, guardianIdentifier: '2@q.com', isLoginGuardian: true, @@ -63,11 +59,9 @@ const mockUserGuardiansListItem3 = { identifierHash: 'identifierHash3@q.com', salt: 'salt', verifier: { - endPoints: ['http://192.168.0.250:5555'], id: 'Gauss', imageUrl: 'https://x/Gauss.png', name: 'Gauss', - verifierAddresses: ['5M5sG4'], }, guardianIdentifier: '3@q.com', isLoginGuardian: true, @@ -76,11 +70,9 @@ const mockUserGuardiansListItem3 = { status: 'verified' as VerifyStatus, }; const mockVerifierItem = { - endPoints: ['http://192.168.0.250:5555'], id: 'Gauss', imageUrl: 'https://x/Gauss.png', name: 'Gauss', - verifierAddresses: ['5M5sG4'], }; const mockUserGuardianStatus = { '1@q.com&Gauss': { @@ -91,11 +83,9 @@ const mockUserGuardianStatus = { identifierHash: 'identifierHash', salt: 'salt', verifier: { - endPoints: ['http://192.168.0.250:5555'], id: 'Gauss', imageUrl: 'https://x/Gauss.png', name: 'Gauss', - verifierAddresses: ['5M5sG4'], }, }, '2@q.com&Minerva': { @@ -106,11 +96,9 @@ const mockUserGuardianStatus = { identifierHash: 'identifierHash', salt: 'salt', verifier: { - endPoints: ['http://192.168.0.250:5577'], id: 'Minerva', imageUrl: 'https://x/Minerva.png', name: 'Minerva', - verifierAddresses: ['3sWGDJ'], }, }, }; @@ -217,11 +205,9 @@ describe('setGuardiansAction', () => { identifierHash: '1@q.com', salt: 'salt', verifier: { - endPoints: ['http://192.168.0.250:5555'], id: 'Gauss', imageUrl: 'https://x/Gauss.png', name: 'Gauss', - verifierAddresses: ['5M5sG4'], }, guardianIdentifier: '', isLoginGuardian: true, diff --git a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx index c7f7e6af62..d893a00a40 100644 --- a/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx +++ b/packages/web-extension-did/app/web/pages/RegisterStart/index.tsx @@ -16,7 +16,7 @@ import { LoginInfo } from 'store/reducers/loginCache/type'; import { setLoginAccountAction } from 'store/reducers/loginCache/actions'; import { resetGuardians } from '@portkey-wallet/store/store-ca/guardians/actions'; import useGuardianList from 'hooks/useGuardianList'; -import { handleErrorCode, handleErrorMessage } from '@portkey-wallet/utils'; +import { handleErrorCode, handleErrorMessage, sleep } from '@portkey-wallet/utils'; import { Button, message } from 'antd'; import { getHolderInfo } from 'utils/sandboxUtil/getHolderInfo'; import { SocialLoginFinishHandler } from 'types/wallet'; @@ -143,8 +143,6 @@ export default function RegisterStart() { id: '', name: '', imageUrl: '', - endPoints: [], - verifierAddresses: [], }); const loginAccountRef = useRef(); const [loginAccount, setLoginAccount] = useState(); @@ -166,7 +164,6 @@ export default function RegisterStart() { [checkAuth], ); - const timer = useRef(); const onSignFinish = useCallback( async (data: LoginInfo) => { dispatch(setOriginChainId(DefaultChainId)); @@ -175,12 +172,7 @@ export default function RegisterStart() { setLoading(true, 'Assigning a verifier on-chain…'); - await new Promise((resolve) => { - timer.current = setTimeout(() => { - clearTimeout(timer.current); - resolve('timeout down'); - }, 2000); - }); + await sleep(2000); // Get the assigned verifier data from the backend api and guaranteed loading display 2s try { diff --git a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx index e6c9c2947e..74b7448d33 100644 --- a/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/VerifierPage/index.tsx @@ -11,7 +11,7 @@ import { verification } from 'utils/api'; import { useOriginChainId } from '@portkey-wallet/hooks/hooks-ca/wallet'; import { useCommonState } from 'store/Provider/hooks'; import { useLocation } from 'react-router'; -import { OperationTypeEnum } from '@portkey-wallet/types/verifier'; +import { OperationTypeEnum, VerifierItem } from '@portkey-wallet/types/verifier'; import { CodeVerifyUI } from '@portkey/did-ui-react'; import { AccountType } from '@portkey/services'; From 8099dcf1d660d08815d6cae60d2bf0df47338f48 Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Thu, 27 Jul 2023 03:54:49 +0800 Subject: [PATCH 461/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20adjust=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/dapp.ts | 1 + packages/hooks/hooks-ca/cms.ts | 1 + packages/types/session.ts | 23 ---- packages/utils/check.ts | 2 +- .../methodController/AELFMethodController.ts | 41 ++----- .../app/web/pages/ConnectWallet/index.tsx | 4 +- .../app/web/pages/DappAutoTx/index.tsx | 27 +++-- .../app/web/pages/GetSignature/index.less | 3 - .../app/web/pages/GetSignature/index.tsx | 35 ++---- .../app/web/pages/SendTransactions/index.tsx | 4 +- .../ConnectedSites/SiteDetail/index.tsx | 13 +- .../components/SiteItem/index.less | 13 +- .../components/SiteItem/index.tsx | 57 +++++---- .../pages/components/CustomSelect/index.less | 7 ++ .../components/CustomShowSelect/index.less | 112 ++++++++++++++++++ .../components/CustomShowSelect/index.tsx | 41 +++++++ .../pages/components/DappSession/index.less | 4 +- .../pages/components/DappSession/index.tsx | 10 +- .../app/web/store/Provider/Updater.tsx | 3 +- .../web-extension-did/app/web/utils/index.ts | 4 - 20 files changed, 267 insertions(+), 138 deletions(-) create mode 100644 packages/web-extension-did/app/web/pages/components/CustomShowSelect/index.less create mode 100644 packages/web-extension-did/app/web/pages/components/CustomShowSelect/index.tsx diff --git a/packages/constants/constants-ca/dapp.ts b/packages/constants/constants-ca/dapp.ts index 290952f14c..b329de38a3 100644 --- a/packages/constants/constants-ca/dapp.ts +++ b/packages/constants/constants-ca/dapp.ts @@ -14,6 +14,7 @@ export const SessionKeyMap = { export const SessionKeyArray = Object.entries(SessionKeyMap).map(([k, v]) => ({ value: k === SessionExpiredPlan.always ? k : Number(k), label: v, + children: v, })); export const CA_METHOD_WHITELIST = ['ManagerForwardCall', 'ManagerTransfer']; diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index 15101075c8..0d40d28dbf 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -170,6 +170,7 @@ export const useCheckSiteIsInBlackList = () => { return list.indexOf(getOrigin(url)) >= 0; } catch (err) { console.log(err); + return false; } }, [list], diff --git a/packages/types/session.ts b/packages/types/session.ts index 1e92da471c..5d0073db62 100644 --- a/packages/types/session.ts +++ b/packages/types/session.ts @@ -16,26 +16,3 @@ export type SessionInfo = { expiredTime: Timestamp; signature: string; }; - -export const SessionExpiredPlanShow = [ - { - value: SessionExpiredPlan.hour1, - children: '1 hour', - }, - { - value: SessionExpiredPlan.hour3, - children: '3 hours', - }, - { - value: SessionExpiredPlan.hour12, - children: '12 hours', - }, - { - value: SessionExpiredPlan.hour24, - children: '24 hours', - }, - { - value: SessionExpiredPlan.always, - children: 'Never', - }, -]; diff --git a/packages/utils/check.ts b/packages/utils/check.ts index b2bc5f6f28..91787c13de 100644 --- a/packages/utils/check.ts +++ b/packages/utils/check.ts @@ -6,7 +6,7 @@ export enum EmailError { noAccount = 'Failed to log in with this email. Please use your login account.', } -export function checkEmail(email?: string): undefined | string { +export function checkEmail(email?: string): void | string { if (!email) return EmailError.noEmail; if (!isValidEmail(email)) return EmailError.invalidEmail; } diff --git a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts index 50b80b1687..e4db75ff0d 100644 --- a/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts +++ b/packages/web-extension-did/app/web/controllers/methodController/AELFMethodController.ts @@ -10,7 +10,7 @@ import { MethodsBase, ResponseCode, MethodsWallet } from '@portkey/provider-type import { ExtensionDappManager } from './ExtensionDappManager'; import { getCurrentCaHash, getSWReduxState, getWalletState } from 'utils/lib/SWGetReduxStore'; import ApprovalController from 'controllers/approval/ApprovalController'; -import { CA_METHOD_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; +import { CA_METHOD_WHITELIST, REMEMBER_ME_ACTION_WHITELIST } from '@portkey-wallet/constants/constants-ca/dapp'; import { randomId } from '@portkey-wallet/utils'; import { removeLocalStorage, setLocalStorage } from 'utils/storage/chromeStorage'; import SWEventController from 'controllers/SWEventController'; @@ -62,8 +62,11 @@ export default class AELFMethodController { } handleRequest = async ({ params, method, callBack }: { params: any; method: any; callBack: any }) => { - const validSession = await this.verifySessionInfo(params.origin); + if (!REMEMBER_ME_ACTION_WHITELIST.includes(method)) { + return await callBack(params); + } + const validSession = await this.verifySessionInfo(params.origin); let result; if (validSession) { result = await this.approvalController.authorizedToAutoExecute({ @@ -140,8 +143,8 @@ export default class AELFMethodController { signature: sessionInfo.signature, }); if (!valid) return valid; - const res = hasSessionInfoExpired(sessionInfo); - return res; + const isExpired = hasSessionInfoExpired(sessionInfo); + return !isExpired; } catch (error) { console.log('verifySessionInfo error'); return false; @@ -339,33 +342,15 @@ export default class AELFMethodController { setLocalStorage({ txPayload: { [key]: JSON.stringify(payload.params) } }); delete message.payload?.params; - // const result = await this.handleRequest({ - // params: { - // origin, - // transactionInfoId: key, - // payload: message.payload, - // }, - // method: MethodsBase.SEND_TRANSACTION, - // callBack: (params: any) => this.approvalController.authorizedToSendTransactions(params), - // }); - - const validSession = await this.verifySessionInfo(origin); - - let result; - if (validSession) { - result = await this.approvalController.authorizedToAutoExecute({ - origin, - transactionInfoId: key, - payload: message.payload, - method: MethodsBase.SEND_TRANSACTION, - }); - } else { - result = await this.approvalController.authorizedToSendTransactions({ + const result = await this.handleRequest({ + params: { origin, transactionInfoId: key, payload: message.payload, - }); - } + }, + method: MethodsBase.SEND_TRANSACTION, + callBack: (params: any) => this.approvalController.authorizedToSendTransactions(params), + }); // TODO Only support open a window removeLocalStorage('txPayload'); diff --git a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx index 35994f2c4f..1ccf8e637f 100644 --- a/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx +++ b/packages/web-extension-did/app/web/pages/ConnectWallet/index.tsx @@ -12,6 +12,7 @@ import DappSession from 'pages/components/DappSession'; import { SessionExpiredPlan } from '@portkey-wallet/types/session'; import { useUpdateSessionInfo } from '@portkey-wallet/hooks/hooks-ca/dapp'; import getManager from 'utils/getManager'; +import { useCheckSiteIsInBlackList } from '@portkey-wallet/hooks/hooks-ca/cms'; import './index.less'; const allowItem = ['view wallet balance and activities', 'send you transaction requests']; @@ -25,6 +26,7 @@ export default function ConnectWallet() { const [open, setOpen] = useState(false); const [exp, setExp] = useState(SessionExpiredPlan.hour1); const updateSessionInfo = useUpdateSessionInfo(); + const checkOriginInBlackList = useCheckSiteIsInBlackList(); const renderSite = useMemo( () => @@ -94,7 +96,7 @@ export default function ConnectWallet() { {renderSite}
    {t('Connect with Portkey')}
    {renderAllow} - + {!checkOriginInBlackList(detail.appHref) && }
    {payload?.method.toLowerCase() === 'transfer' ? renderTransfer : renderMessage} {errMsg &&
    {errMsg}
    } - + {!checkOriginInBlackList(origin) && }
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/Prompt/index.tsx index a9a920e183..e4688ee546 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/Prompt/index.tsx @@ -33,7 +33,16 @@ export default function ContactsPrompt({ />
    - +
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.tsx index 692d4cd6a2..e046ce3907 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.tsx @@ -4,34 +4,108 @@ import NoContacts from '../NoContacts'; import { useNavigate } from 'react-router'; import { ContactIndexType, ContactItemType } from '@portkey-wallet/types/types-ca/contact'; import './index.less'; +import { Tabs } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { useCallback, useMemo, useState } from 'react'; +import { ContactsTab } from '@portkey-wallet/constants/constants-ca/assets'; export interface IContactsBodyProps { isSearch: boolean; list: ContactIndexType[]; contactCount: number; initData: Partial; + portkeyChatCount: number; + portkeyChatInitData: Partial; + portkeyChatList: ContactIndexType[]; + isSearchPortkeyChat: boolean; } -export default function ContactsBody({ isSearch, list, contactCount, initData }: IContactsBodyProps) { +export default function ContactsBody({ + isSearch, + list, + contactCount, + initData, + portkeyChatCount, + portkeyChatInitData, + portkeyChatList, + isSearchPortkeyChat, +}: IContactsBodyProps) { const navigate = useNavigate(); + const { t } = useTranslation(); + const [activeKey, setActiveKey] = useState(ContactsTab.ALL); + + const onChange = useCallback(async (key: string) => { + setActiveKey(key); + }, []); + + const findMoreHandler = useCallback(() => { + navigate('/setting/contacts/find-more-people'); + }, [navigate]); + + const allContactListUI = useMemo(() => { + return ( + <> + {contactCount === 0 ? ( + isSearch ? ( +
    There is no search result.
    + ) : ( + + ) + ) : ( + { + navigate('/setting/contacts/view', { state: { ...item, index: index } }); + }} + /> + )} + + ); + }, [contactCount, initData, isSearch, list, navigate]); + + const portkeyChatListUI = useMemo(() => { + return ( + <> +
    Find more people
    + {portkeyChatCount === 0 ? ( + isSearchPortkeyChat ? ( +
    There is no search result.
    + ) : ( + + ) + ) : ( + { + navigate('/setting/contacts/view', { state: { ...item, index: index } }); + }} + /> + )} + + ); + }, [findMoreHandler, isSearchPortkeyChat, navigate, portkeyChatCount, portkeyChatInitData, portkeyChatList]); + + const renderTabsData = useMemo( + () => [ + { + label: t('All'), + key: ContactsTab.ALL, + children: allContactListUI, + }, + { + label: t('Portkey Chat'), + key: ContactsTab.PORTKEY_CHAT, + children: portkeyChatListUI, + }, + ], + [allContactListUI, portkeyChatListUI, t], + ); return (
    - {contactCount === 0 ? ( - isSearch ? ( -
    There is no search result.
    - ) : ( - - ) - ) : ( - { - navigate('/setting/contacts/view', { state: { ...item, index: index } }); - }} - /> - )} +
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.tsx index f6a2e251d7..9b4ddae5ec 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.tsx @@ -7,15 +7,17 @@ import './index.less'; export default function ContactsSearchInput({ handleChange, className, + placeholder = 'Name or Address', }: { handleChange: ChangeEventHandler; className?: string; + placeholder?: string; }) { return ( } - placeholder="Name or Address" + placeholder={placeholder} onChange={handleChange} /> ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx index f1ac58d2a4..f73970eac1 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx @@ -15,9 +15,11 @@ export interface IEditContactFormProps extends FormProps { isEdit: boolean; isDisable: boolean; validName: ValidData; + validRemark: ValidData; state: any; addressArr: CustomAddressItem[]; handleInputValueChange: (v: string) => void; + handleInputRemarkChange: (v: string) => void; handleDelete: (name: any, i: any, remove: (index: number | number[]) => void) => void; handleSelectNetwork: (i: number) => void; handleAddressChange: (i: number, value: string) => void; @@ -31,8 +33,10 @@ export default function EditContactForm({ state, addressArr, validName, + validRemark, onFinish, handleInputValueChange, + handleInputRemarkChange, handleSelectNetwork, handleAddressChange, handleDelete, @@ -68,6 +72,18 @@ export default function EditContactForm({ maxLength={16} /> + + handleInputRemarkChange(e.target.value)} + maxLength={16} + /> + + {(fields, { add, remove }) => (
    From 5f63d5a314af3e2c91d9cd6b59144b022467459a Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 9 Aug 2023 15:48:29 +0800 Subject: [PATCH 501/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20edit=20con?= =?UTF-8?q?tact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contacts/EditContact/Popup/index.tsx | 4 -- .../Contacts/EditContact/Prompt/index.tsx | 4 -- .../web/pages/Contacts/EditContact/index.tsx | 38 ------------------- .../components/EditContactForm/index.tsx | 21 +--------- 4 files changed, 2 insertions(+), 65 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx index 2851872578..416fd81119 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx @@ -21,8 +21,6 @@ export default function EditContactPopup({ handleInputRemarkChange, handleSelectNetwork, handleAddressChange, - handleDelete, - handleAdd, closeDrawer, handleNetworkChange, }: IEditContactProps) { @@ -44,10 +42,8 @@ export default function EditContactPopup({ state={state} addressArr={addressArr} onFinish={onFinish} - handleDelete={handleDelete} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} - handleAdd={handleAdd} handleInputValueChange={handleInputValueChange} handleInputRemarkChange={handleInputRemarkChange} /> diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx index d49486cd46..0f1c36cd01 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx @@ -20,8 +20,6 @@ export default function EditContactPrompt({ handleInputRemarkChange, handleSelectNetwork, handleAddressChange, - handleDelete, - handleAdd, closeDrawer, handleNetworkChange, }: IEditContactProps) { @@ -37,10 +35,8 @@ export default function EditContactPrompt({ state={state} addressArr={addressArr} onFinish={onFinish} - handleDelete={handleDelete} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} - handleAdd={handleAdd} handleInputValueChange={handleInputValueChange} handleInputRemarkChange={handleInputRemarkChange} /> diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx index 60c51b704b..0c6726bc8d 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx @@ -40,13 +40,6 @@ export interface IEditContactProps extends IEditContactFormProps, BaseHeaderProp handleNetworkChange: (v: any) => void; } -const initAddress: CustomAddressItem = { - chainId: 'AELF', - address: '', - networkName: 'MainChain AELF Testnet', - validData: { validateStatus: '', errorMsg: '' }, -}; - export default function EditContact() { const [form] = Form.useForm(); const { t } = useTranslation(); @@ -105,13 +98,6 @@ export default function EditContact() { [addressArr, form, index], ); - const handleRemoveAds = useCallback( - (i: number) => { - setAddressArr(addressArr.filter((_, j) => j !== i)); - }, - [addressArr], - ); - const handleFormValueChange = useCallback(() => { const { name, addresses } = form.getFieldsValue(); const flag = addresses.some((ads: Record) => !ads?.address); @@ -260,26 +246,6 @@ export default function EditContact() { } }, [isEdit, navigate, state]); - // delete address method - const deleteAddress = useCallback( - (name: any, i: any, remove: (index: number | number[]) => void) => { - remove(name); - handleFormValueChange(); - handleRemoveAds(i); - }, - [handleFormValueChange, handleRemoveAds], - ); - - // add address method - const handleAdd = useCallback( - (add: (defaultValue?: any, insertIndex?: number) => void) => { - add(initAddress); - setDisabled(true); - setAddressArr([...addressArr, initAddress]); - }, - [addressArr], - ); - const handleCloseDrawer = () => { setNetOpen(false); }; @@ -299,10 +265,8 @@ export default function EditContact() { state={state} addressArr={addressArr} onFinish={onFinish} - handleDelete={deleteAddress} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} - handleAdd={handleAdd} handleInputValueChange={handleInputValueChange} handleInputRemarkChange={handleInputRemarkChange} isShowDrawer={netOpen} @@ -321,10 +285,8 @@ export default function EditContact() { state={state} addressArr={addressArr} onFinish={onFinish} - handleDelete={deleteAddress} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} - handleAdd={handleAdd} handleInputValueChange={handleInputValueChange} handleInputRemarkChange={handleInputRemarkChange} isShowDrawer={netOpen} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx index f73970eac1..fd6f152c6c 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx @@ -20,10 +20,8 @@ export interface IEditContactFormProps extends FormProps { addressArr: CustomAddressItem[]; handleInputValueChange: (v: string) => void; handleInputRemarkChange: (v: string) => void; - handleDelete: (name: any, i: any, remove: (index: number | number[]) => void) => void; handleSelectNetwork: (i: number) => void; handleAddressChange: (i: number, value: string) => void; - handleAdd: (add: (defaultValue?: any, insertIndex?: number) => void) => void; } export default function EditContactForm({ @@ -39,8 +37,6 @@ export default function EditContactForm({ handleInputRemarkChange, handleSelectNetwork, handleAddressChange, - handleDelete, - handleAdd, }: IEditContactFormProps) { const { t } = useTranslation(); const navigate = useNavigate(); @@ -85,18 +81,11 @@ export default function EditContactForm({ - {(fields, { add, remove }) => ( + {(fields) => (
    {fields.map(({ key, name, ...restField }, i) => (
    -
    - {`Address${i + 1}`} - handleDelete(name, i, remove)} - /> -
    +
    {`Address${i + 1}`}
    ))} - {fields.length < 5 && ( -
    handleAdd(add)}> - - {t('Add Address')} -
    - )}
    )}
    From 1b242d9876120e215f0e3d1ed6a77881edea168b Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 9 Aug 2023 20:44:56 +0800 Subject: [PATCH 502/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20contact=20?= =?UTF-8?q?profile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/types/types-ca/contact.ts | 1 + .../web/pages/Contacts/EditContact/index.tsx | 42 +++++++++++++-- .../Contacts/ViewContact/Popup/index.tsx | 43 ++++++++++++++- .../Contacts/ViewContact/Prompt/index.less | 6 ++- .../Contacts/ViewContact/Prompt/index.tsx | 43 ++++++++++++++- .../web/pages/Contacts/ViewContact/index.tsx | 54 +++++++++++++++++-- 6 files changed, 176 insertions(+), 13 deletions(-) diff --git a/packages/types/types-ca/contact.ts b/packages/types/types-ca/contact.ts index c686ba0b09..3b19b2553a 100644 --- a/packages/types/types-ca/contact.ts +++ b/packages/types/types-ca/contact.ts @@ -14,6 +14,7 @@ export interface ContactItemType { id: string; index: string; name: string; + remark?: string; addresses: AddressItem[]; modificationTime: number; isDeleted: boolean; diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx index 0c6726bc8d..518098705e 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo, useEffect, useState } from 'react'; -import { Form, message } from 'antd'; +import { Form, Modal, message } from 'antd'; import { useNavigate, useLocation, useParams } from 'react-router'; import { useTranslation } from 'react-i18next'; import { ContactItemType, AddressItem } from '@portkey-wallet/types/types-ca/contact'; @@ -178,6 +178,23 @@ export default function EditContact() { [checkExistName, form], ); + const checkExistRemark = useCallback( + async (v: string) => { + if (isEdit && state.remark === v) { + return false; + } + const { existed } = await checkExistNameApi(v); // TODO remark + return existed; + }, + [checkExistNameApi, isEdit, state.remark], + ); + + const handleCheckRemark = useCallback(async (v: string) => { + const existed = await checkExistRemark(v); + + return existed; + }, []); + const handleCheckAddress = useCallback( (addresses: AddressItem[]) => { let flag = 0; @@ -199,20 +216,36 @@ export default function EditContact() { const onFinish = useCallback( async (values: ContactItemType) => { - const { name, addresses } = values; + const { name, remark, addresses } = values; try { setLoading(true); const checkName = await handleCheckName(name.trim()); + const checkRemark = await handleCheckRemark(remark?.trim() || ''); const checkAddress = handleCheckAddress(addresses); - if (checkName && checkAddress) { + if (checkName && checkRemark && checkAddress) { if (isEdit) { + // TODO remark await editContactApi({ name: name.trim(), addresses, id: state.id, index: state.index }); } else { + // TODO remark await addContactApi({ name: name.trim(), addresses }); } appDispatch(fetchContactListAsync()); - navigate('/setting/contacts'); + // navigate('/setting/contacts'); + // TODO if can chat + Modal.confirm({ + width: 320, + content: t('This contact is identified as a new portkey web3 chat friend.'), + className: 'cross-modal delete-modal', + autoFocusButton: null, + icon: null, + centered: true, + okText: t('Ok'), + cancelText: t('Cancel'), + onOk: () => navigate('/chat'), + onCancel: () => navigate('/setting/contacts/view', { state: {} }), + }); message.success(isEdit ? 'Edit Contact Successful' : 'Add Contact Successful'); } } catch (e: any) { @@ -228,6 +261,7 @@ export default function EditContact() { editContactApi, handleCheckAddress, handleCheckName, + handleCheckRemark, isEdit, navigate, setLoading, diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx index 21bcbb8cc7..63eda99176 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx @@ -5,7 +5,18 @@ import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import './index.less'; import { IViewContactProps } from '..'; -export default function ViewContactPopup({ headerTitle, goBack, data, editText, handleEdit }: IViewContactProps) { +export default function ViewContactPopup({ + headerTitle, + goBack, + data, + editText, + chatText, + addContactText, + handleEdit, + handleChat, + handleAdd, + handleCopy, +}: IViewContactProps) { return (
    @@ -19,11 +30,39 @@ export default function ViewContactPopup({ headerTitle, goBack, data, editText,
    {data.index}
    {data.name}
    + +
    + Remark + {data.remark} +
    + +
    + Portkey ID +
    + {data.portkeyId} + handleCopy(data.portkeyId)} type="Copy" className="address-copy-icon" /> +
    +
    + +
    + {`ID (relation one)`} +
    + {data.relationOneId} + handleCopy(data.relationOneId)} type="Copy" className="address-copy-icon" /> +
    +
    +
    + - + +
    ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less index bb2eda39b1..0fe1a466f0 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less @@ -25,13 +25,15 @@ flex: 1; width: 0; word-wrap: break-word; - .text-overflow(1) + .text-overflow(1); } } .contact-body { padding: 0 24px; - .edit-btn { + .edit-btn, + .chat-btn, + .add-contact-btn { height: 48px; color: @font-9; font-size: 14px; diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx index 2de39dcf4d..c05f983600 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx @@ -3,8 +3,20 @@ import './index.less'; import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import SecondPageHeader from 'pages/components/SecondPageHeader'; import { IViewContactProps } from '..'; +import CustomSvg from 'components/CustomSvg'; -export default function ViewContactPrompt({ headerTitle, goBack, data, editText, handleEdit }: IViewContactProps) { +export default function ViewContactPrompt({ + headerTitle, + goBack, + data, + editText, + chatText, + addContactText, + handleEdit, + handleChat, + handleAdd, + handleCopy, +}: IViewContactProps) { return (
    @@ -14,11 +26,38 @@ export default function ViewContactPrompt({ headerTitle, goBack, data, editText,
    {data.name}
    +
    + Remark + {data.remark} +
    + +
    + Portkey ID +
    + {data.portkeyId} + handleCopy(data.portkeyId)} type="Copy" className="address-copy-icon" /> +
    +
    + +
    + {`ID (relation one)`} +
    + {data.relationOneId} + handleCopy(data.relationOneId)} type="Copy" className="address-copy-icon" /> +
    +
    +
    + - + +
    ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index dce1a45cc7..6302882e38 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -8,9 +8,21 @@ import { BaseHeaderProps } from 'types/UI'; import { useCommonState } from 'store/Provider/hooks'; export interface IViewContactProps extends BaseHeaderProps { - data: { name: string; index: string; addresses: AddressItem[] }; + data: { + name: string; + remark: string; + portkeyId: string; + relationOneId: string; + index: string; + addresses: AddressItem[]; + }; editText: string; + chatText: string; + addContactText: string; handleEdit: () => void; + handleChat: () => void; + handleAdd: () => void; + handleCopy: (v: string) => void; } export default function ViewContact() { @@ -21,6 +33,8 @@ export default function ViewContact() { const title = t('Contacts'); const editText = t('Edit'); + const chatText = t('Chat'); + const addContactText = t('Add Contact'); const goBack = useCallback(() => { navigate('/setting/contacts'); @@ -30,9 +44,43 @@ export default function ViewContact() { navigate('/setting/contacts/edit', { state: state }); }, [navigate, state]); + const handleChat = useCallback(() => { + navigate('/chat-list', { state: state }); + }, [navigate, state]); + + const handleAdd = useCallback(() => { + navigate('/setting/contacts/add', { state: state }); + }, [navigate, state]); + + const handleCopy = useCallback((v: string) => { + console.log('🌈 🌈 🌈 🌈 🌈 🌈 v', v); + }, []); + return isNotLessThan768 ? ( - + ) : ( - + ); } From 59101d1530d22f248cc5a340a9fe38158c4c8cab Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Thu, 10 Aug 2023 16:03:26 +0800 Subject: [PATCH 503/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20IM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/chat.ts | 51 ++ packages/constants/constants-ca/im.ts | 6 + packages/hooks/hooks-ca/contact.ts | 16 +- packages/hooks/hooks-ca/im/channelContext.tsx | 97 ++++ packages/hooks/hooks-ca/im/index.ts | 529 ++++++++++++++++++ packages/im/README.md | 11 + packages/im/index.ts | 199 +++++++ packages/im/package.json | 17 + packages/im/types/index.ts | 74 +++ packages/im/utils/index.ts | 2 + packages/im/utils/parser.ts | 6 + packages/im/utils/sign.ts | 51 ++ .../js/hooks/useAsyncStorageState.ts | 76 +++ .../mobile-app-did/js/hooks/useInitData.ts | 34 +- .../js/hooks/useKeyboardHeight.ts | 69 ++- packages/mobile-app-did/js/navigation/Tab.tsx | 4 +- .../js/pages/Chat/ChatCamera/index.tsx | 123 ++-- .../js/pages/Chat/ChatDetails/index.tsx | 28 +- .../js/pages/Chat/ChatHome/index.tsx | 73 ++- .../js/pages/Chat/FindMorePeople/index.tsx | 74 +++ .../js/pages/Chat/NewChatHome/index.tsx | 41 +- .../js/pages/Chat/Profile/index.tsx | 104 +++- .../js/pages/Chat/SearchPeople/index.tsx | 53 +- .../Chat/components/BookmarkOverlay/index.tsx | 65 +++ .../components/ChatHomeListItem/index.tsx | 92 --- .../ChatHomeListItemSwiper/index.tsx | 154 +++++ .../pages/Chat/components/ChatList/index.tsx | 62 ++ .../js/pages/Chat/components/Chats/index.tsx | 139 +++-- .../Chat/components/CustomBubble/index.tsx | 47 ++ .../InputToolbar/AccessoryBar/index.tsx | 64 +++ .../InputToolbar/BottomBarContainer/index.tsx | 156 ++++++ .../InputToolbar/Emoticons/config.ts | 7 + .../InputToolbar/Emoticons/emoticonData.json | 167 ++++++ .../InputToolbar/Emoticons/index.tsx | 47 ++ .../InputToolbar/SendMessageButton/index.tsx | 9 + .../components/InputToolbar/ToolBar/index.tsx | 80 +++ .../Chat/components/InputToolbar/index.ts | 5 + .../Chat/components/InputToolbar/index.tsx | 118 ---- .../components/Message/MessageText/index.tsx | 97 ++++ .../components/MessageContainer/index.tsx | 2 +- .../components/RecommendSection/index.tsx | 47 ++ .../Chat/components/context/chatsContext.tsx | 3 + .../js/pages/Chat/components/context/hooks.ts | 18 + .../js/pages/Chat/components/hooks/index.ts | 43 ++ .../js/pages/Chat/components/messages.js | 6 +- .../js/pages/Chat/hooks/index.ts | 30 + .../mobile-app-did/js/pages/Chat/routes.ts | 4 + .../pages/DashBoard/AssetsOverlay/index.tsx | 3 +- .../mobile-app-did/js/pages/Home/index.tsx | 119 +++- .../pages/My/Contacts/ContactsHome/index.tsx | 39 +- .../SearchContactListSection/index.tsx | 29 + .../pages/My/WalletHome/WalletName/index.tsx | 47 +- .../js/pages/My/WalletHome/index.tsx | 2 +- packages/mobile-app-did/js/pages/My/index.tsx | 5 + .../mobile-app-did/js/store/chat/slice.ts | 27 + packages/mobile-app-did/js/store/config.ts | 2 + .../mobile-app-did/js/store/rootReducer.ts | 4 + packages/mobile-app-did/package.json | 1 + packages/store/store-ca/assets/type.ts | 6 +- packages/store/store-ca/im/actions.ts | 32 ++ packages/store/store-ca/im/slice.ts | 108 ++++ packages/store/store-ca/im/type.ts | 16 + packages/store/store-ca/im/util.ts | 19 + packages/types/package.json | 3 + packages/types/types-ca/store.ts | 3 + packages/utils/chat.ts | 23 +- packages/utils/mobile/device.ts | 12 +- yarn.lock | 7 + 68 files changed, 3277 insertions(+), 430 deletions(-) create mode 100644 packages/constants/constants-ca/chat.ts create mode 100644 packages/constants/constants-ca/im.ts create mode 100644 packages/hooks/hooks-ca/im/channelContext.tsx create mode 100644 packages/hooks/hooks-ca/im/index.ts create mode 100644 packages/im/README.md create mode 100644 packages/im/index.ts create mode 100644 packages/im/package.json create mode 100644 packages/im/types/index.ts create mode 100644 packages/im/utils/index.ts create mode 100644 packages/im/utils/parser.ts create mode 100644 packages/im/utils/sign.ts create mode 100644 packages/mobile-app-did/js/hooks/useAsyncStorageState.ts create mode 100644 packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItem/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/config.ts create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/emoticonData.json create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendMessageButton/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/index.ts delete mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/RecommendSection/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/context/chatsContext.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts create mode 100644 packages/mobile-app-did/js/pages/Chat/components/hooks/index.ts create mode 100644 packages/mobile-app-did/js/pages/Chat/hooks/index.ts create mode 100644 packages/mobile-app-did/js/pages/My/Contacts/SearchContactListSection/index.tsx create mode 100644 packages/mobile-app-did/js/store/chat/slice.ts create mode 100644 packages/store/store-ca/im/actions.ts create mode 100644 packages/store/store-ca/im/slice.ts create mode 100644 packages/store/store-ca/im/type.ts create mode 100644 packages/store/store-ca/im/util.ts diff --git a/packages/constants/constants-ca/chat.ts b/packages/constants/constants-ca/chat.ts new file mode 100644 index 0000000000..27675fd632 --- /dev/null +++ b/packages/constants/constants-ca/chat.ts @@ -0,0 +1,51 @@ +export enum ChatOperationsEnum { + PROFILE = 'profile', + MUTE = 'mute', + UNMUTE = 'unmute', + PIN = 'pin', + UNPIN = 'unPin', + DELETE_CHAT = 'deleteChat', + ADD_CONTACT = 'addContact', +} + +export const ChatHomeOperationList = [ + { + label: 'Mute/UnMute', + operation: ChatOperationsEnum.MUTE, + toggleOperation: ChatOperationsEnum.UNMUTE, + }, + { + label: 'Pin/Unpin', + operation: ChatOperationsEnum.PIN, + toggleOperation: ChatOperationsEnum.UNPIN, + }, + { + label: 'Delete', + operation: ChatOperationsEnum.DELETE_CHAT, + }, +] as const; + +export const ChatDetailsOperationList = [ + { + label: 'Profile', + operation: ChatOperationsEnum.PROFILE, + }, + { + label: 'Mute/UnMute', + operation: ChatOperationsEnum.MUTE, + toggleOperation: ChatOperationsEnum.UNMUTE, + }, + { + label: 'Pin/Unpin', + operation: ChatOperationsEnum.PIN, + toggleOperation: ChatOperationsEnum.UNPIN, + }, + { + label: 'Delete Chat', + operation: ChatOperationsEnum.DELETE_CHAT, + }, + { + label: 'Add Contact', + operation: ChatOperationsEnum.ADD_CONTACT, + }, +] as const; diff --git a/packages/constants/constants-ca/im.ts b/packages/constants/constants-ca/im.ts new file mode 100644 index 0000000000..a8d7ecc263 --- /dev/null +++ b/packages/constants/constants-ca/im.ts @@ -0,0 +1,6 @@ +export enum IMErrorEnum { + NO_IM_INSTANCE = 'noImInstance', +} + +export const CHANNEL_LIST_LIMIT = 20; +export const MESSAGE_LIST_LIMIT = 50; diff --git a/packages/hooks/hooks-ca/contact.ts b/packages/hooks/hooks-ca/contact.ts index 49ec61230d..58c4c0b154 100644 --- a/packages/hooks/hooks-ca/contact.ts +++ b/packages/hooks/hooks-ca/contact.ts @@ -2,7 +2,7 @@ import { request } from '@portkey-wallet/api/api-did'; import { CheckContactNameResponseType } from '@portkey-wallet/api/api-did/contact/type'; import { useCurrentNetworkInfo } from '@portkey-wallet/hooks/hooks-ca/network'; import { AddContactItemApiType, ContactItemType, EditContactItemApiType } from '@portkey-wallet/types/types-ca/contact'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { addContactAction, deleteContactAction, @@ -93,3 +93,17 @@ export const useContact = (isFetch = true, isInit = false) => { }, [dispatch, isFetch, isInit]); return useAppCommonSelector(state => state.contact); }; + +export const useContactList = () => { + const contact = useAppCommonSelector(state => state.contact); + + return useMemo(() => { + let result: ContactItemType[] = []; + contact.contactIndexList + .filter(ele => ele.contacts.length !== 0) + .map(ele => { + result = [...result, ...ele.contacts]; + }); + return result; + }, [contact.contactIndexList]); +}; diff --git a/packages/hooks/hooks-ca/im/channelContext.tsx b/packages/hooks/hooks-ca/im/channelContext.tsx new file mode 100644 index 0000000000..a2925b09c3 --- /dev/null +++ b/packages/hooks/hooks-ca/im/channelContext.tsx @@ -0,0 +1,97 @@ +import { Message } from '@portkey-wallet/im'; +import React, { ReactNode, createContext, useContext, useMemo, useReducer } from 'react'; + +export function basicActions(type: T, payload?: any) { + return { + type, + payload, + }; +} +export type BasicActions = { + dispatch: (actions: { type: T; payload: any }) => void; +}; + +export interface ChannelState { + list: Message[]; + hasNext: boolean; +} + +export const INITIAL_STATE = { + list: [], + hasNext: false, +}; + +export const ChannelContext = createContext<[ChannelState, React.Dispatch] | []>([]); + +export function useChannelContext() { + return useContext(ChannelContext) as [ChannelState, React.Dispatch]; +} + +export enum ChannelActions { + CLEAR_CHANNEL = 'clearChannel', + ADD_MESSAGE = 'addMessage', + NEXT_LIST = 'nextList', + SET_LIST = 'setList', + SET_HAS_NEXT = 'setHasNext', + DELETE_MESSAGE = 'deleteMessage', +} + +// reducer +export function reducer(state: ChannelState, { type, payload }: { type: ChannelActions; payload: any }) { + switch (type) { + case ChannelActions.ADD_MESSAGE: + return Object.assign({}, state, { + list: [...state.list, payload], + }); + + case ChannelActions.NEXT_LIST: + return Object.assign({}, state, { + list: [...payload, ...state.list], + }); + + case ChannelActions.SET_LIST: + return Object.assign({}, state, { + list: payload, + }); + + case ChannelActions.SET_HAS_NEXT: + return Object.assign({}, state, { + hasNext: payload, + }); + case ChannelActions.DELETE_MESSAGE: + return Object.assign({}, state, { + list: state.list.filter(item => item.sendUuid !== payload), + }); + + default: { + const { destroy } = payload; + if (destroy) return Object.assign({}, payload); + return Object.assign({}, state, payload); + } + } +} + +// actions +export const basicChannelActions = { + clearChannel: () => + basicActions(ChannelActions.CLEAR_CHANNEL, { + ...INITIAL_STATE, + }), + addMessage: (message: Message) => basicActions(ChannelActions.ADD_MESSAGE, message), + nextList: (list: Message[]) => basicActions(ChannelActions.NEXT_LIST, list), + setList: (list: Message[]) => basicActions(ChannelActions.NEXT_LIST, list), + setHasNext: (value: boolean) => basicActions(ChannelActions.SET_HAS_NEXT, value), + deleteMessage: (sendUuid: string) => basicActions(ChannelActions.DELETE_MESSAGE, sendUuid), +}; + +export const { clearChannel, addMessage, nextList, setList, setHasNext, deleteMessage } = basicChannelActions; + +// provider +export function ChannelProvider({ children }: { children?: ReactNode | undefined }) { + const [state, dispatch] = useReducer(reducer, INITIAL_STATE); + + const value = useMemo(() => [state, dispatch], [state]); + return ( + ]}>{children} + ); +} diff --git a/packages/hooks/hooks-ca/im/index.ts b/packages/hooks/hooks-ca/im/index.ts new file mode 100644 index 0000000000..da2eb04824 --- /dev/null +++ b/packages/hooks/hooks-ca/im/index.ts @@ -0,0 +1,529 @@ +import im, { utils, MessageType, ChannelInfo, MessageCount, Message } from '@portkey-wallet/im'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useAppCASelector } from '../.'; +import { + addMessage, + clearChannel, + nextList, + setHasNext as setHasNextMessage, + setList, + useChannelContext, + deleteMessage as deleteMessageAction, +} from './channelContext'; +import { randomId } from '@portkey-wallet/utils'; +import { CHANNEL_LIST_LIMIT, IMErrorEnum, MESSAGE_LIST_LIMIT } from '@portkey-wallet/constants/constants-ca/im'; + +import { useCurrentNetworkInfo } from '../network'; +import { useAppCommonDispatch } from '../../index'; +import { + removeChannel, + nextChannelList, + setChannelList, + setHasNext as setHasNextChannel, + updateChannelAttribute, +} from '@portkey-wallet/store/store-ca/im/actions'; + +export const useImState = () => useAppCASelector(state => state.im); + +export const useChannel = (channelId: string) => { + const { networkType } = useCurrentNetworkInfo(); + const appCommonDispatch = useAppCommonDispatch(); + + const muteChannel = useMuteChannel(); + const pinChannel = usePinChannel(); + const hideChannel = useHideChannel(); + + const [{ list, hasNext }, dispatch] = useChannelContext(); + const [info, setInfo] = useState(); + const listRef = useRef(list); + listRef.current = list; + + const [loading, setLoading] = useState(false); + const isNextLoading = useRef(false); + + const next = useCallback( + async (isInit = false) => { + const imInstance = im.getInstance(); + if (!imInstance) { + throw { + code: IMErrorEnum.NO_IM_INSTANCE, + message: 'No im instance', + }; + } + + if (isNextLoading.current) return; + isNextLoading.current = true; + + let maxCreateAt = Date.now(); + if (!isInit) { + const list = listRef.current; + const lastMsg = list[0]; + maxCreateAt = lastMsg?.createAt ? Number(lastMsg?.createAt) : Date.now(); + } + + setLoading(true); + try { + const result = await imInstance.messageList({ + channelUuid: channelId, + maxCreateAt, + limit: MESSAGE_LIST_LIMIT, + }); + const length = result.data?.length || 0; + const hasNextValue = length >= MESSAGE_LIST_LIMIT; + dispatch(setHasNextMessage(hasNextValue)); + + const list = result.data.map((item: any) => utils.messageParser(item)); + if (isInit) { + dispatch(setList(list)); + imInstance.messageRead({ channelUuid: channelId, total: 9999 }).then(() => { + refreshMessageCount(); + appCommonDispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + unreadMessageCount: 0, + }, + }), + ); + }); + } else { + dispatch(nextList(list)); + } + } catch (error) { + console.log('next: error', error); + throw error; + } finally { + setLoading(false); + isNextLoading.current = false; + } + }, + [appCommonDispatch, channelId, dispatch, networkType], + ); + + const init = useCallback(() => { + dispatch(clearChannel()); + return next(true); + }, [dispatch, next]); + + const errorHandler = useCallback( + async (e: any) => { + console.log('errorHandler', e); + try { + await init(); + } catch (error) { + console.log('errorHandler:init error', error); + } + }, + [init], + ); + const errorHandlerRef = useRef(errorHandler); + errorHandlerRef.current = errorHandler; + + const read = useCallback(() => { + const imInstance = im.getInstance(); + if (!imInstance) { + console.log('read: No im instance'); + return; + } + + imInstance.messageRead({ channelUuid: channelId, total: 9999 }).then(() => { + // TODO: async portkey backend + }); + }, [channelId]); + + const updateList = useCallback( + (e: any) => { + const rawMsg = e['im-message']; + if (rawMsg.channelUuid !== channelId) return; + const parseredMsg = utils.messageParser(rawMsg); + dispatch(addMessage(parseredMsg)); + read(); + appCommonDispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + lastMessageType: parseredMsg.type, + lastMessageContent: parseredMsg.content, + lastPostAt: parseredMsg.createAt, + }, + }), + ); + console.log('result', parseredMsg); + }, + [appCommonDispatch, channelId, dispatch, networkType, read], + ); + const updateListRef = useRef(updateList); + updateListRef.current = updateList; + + useEffect(() => { + const { remove: removeMsgObserver } = im.registerChannelMsgObserver(channelId, e => { + updateListRef.current(e); + }); + const { remove: removeErrorObserver } = im.registerErrorObserver(e => { + errorHandlerRef.current(e); + }); + + const imInstance = im.getInstance(); + if (imInstance) { + imInstance.channelInfo(channelId).then(result => { + console.log('channelInfo', result.data); + setInfo(result.data); + }); + } else { + console.log('use Channel init,No im instance'); + } + + return () => { + removeMsgObserver(); + removeErrorObserver(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const sendMessage = useCallback( + async (msg: string) => { + const imInstance = im.getInstance(); + if (!imInstance) { + throw { + code: IMErrorEnum.NO_IM_INSTANCE, + message: 'No im instance', + }; + } + // TODO: need check + const userInfo = im.userInfo; + if (!userInfo) { + throw new Error('No user info'); + } + const uuid = randomId(); + const msgParams = { + channelUuid: channelId, + type: 'TEXT' as MessageType, + content: msg, + sendUuid: `${userInfo.relationId}-${channelId}-${Date.now()}-${uuid}`, + }; + + const msgObj: Message = { + ...msgParams, + from: userInfo.relationId, + fromAvatar: userInfo.avatar, + fromName: userInfo.name, + createAt: `${Date.now()}`, + parsedContent: msgParams.content, + }; + dispatch(addMessage(msgObj)); + + try { + await imInstance.sendMessage(msgParams); + appCommonDispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + lastMessageType: msgObj.type, + lastMessageContent: msgObj.content, + lastPostAt: msgObj.createAt, + }, + }), + ); + } catch (error) { + console.log('error', error); + throw error; + } + }, + [channelId, dispatch], + ); + + const deleteMessage = useCallback( + async (sendUuid: string) => { + const imInstance = im.getInstance(); + if (!imInstance) { + throw { + code: IMErrorEnum.NO_IM_INSTANCE, + message: 'No im instance', + }; + } + + // TODO: add delete request + + const list = listRef.current || []; + if (list.length <= 0) return; + + const latestMsg = list[list.length - 1]; + if (latestMsg.sendUuid === sendUuid) { + if (list.length <= 1) { + appCommonDispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + lastMessageType: 'TEXT', + lastMessageContent: '', + }, + }), + ); + } else { + const nextMsg = list[list.length - 2]; + appCommonDispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + lastMessageType: nextMsg.type, + lastMessageContent: nextMsg.content, + }, + }), + ); + } + } + dispatch(deleteMessageAction(sendUuid)); + }, + [appCommonDispatch, channelId, dispatch, networkType], + ); + + const mute = useCallback(async (value: boolean) => muteChannel(channelId, value, false), [channelId, muteChannel]); + + const pin = useCallback(async (value: boolean) => pinChannel(channelId, value), [channelId, pinChannel]); + + const exit = useCallback(async () => hideChannel(channelId), [channelId, hideChannel]); + + return { + info, + list, + next, + hasNext, + sendMessage, + init, + loading, + mute, + pin, + exit, + deleteMessage, + }; +}; + +export const useNextChannelList = () => { + const { channelListNetMap, hasNextNetMap } = useImState(); + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const channelList = useMemo(() => channelListNetMap?.[networkType], [channelListNetMap, networkType]); + const hasNext = useMemo( + () => (hasNextNetMap?.[networkType] !== undefined ? hasNextNetMap[networkType] : true), + [hasNextNetMap, networkType], + ); + + const isLoadingRef = useRef(false); + const next = useCallback( + async (isInit = false) => { + const imInstance = im.getInstance(); + if (!imInstance) { + throw { + code: IMErrorEnum.NO_IM_INSTANCE, + message: 'No im instance', + }; + } + + if (isLoadingRef.current) return; + isLoadingRef.current = true; + + const lastCursor = isInit ? '' : channelList?.cursor || ''; + try { + const result = await imInstance.userChannelsList({ + cursor: lastCursor, + limit: CHANNEL_LIST_LIMIT, + }); + + const list = result.data?.list || []; + const cursor = result.data?.cursor || lastCursor; + + const hasNextValue = list.length >= CHANNEL_LIST_LIMIT; + dispatch( + setHasNextChannel({ + network: networkType, + hasNext: hasNextValue, + }), + ); + + if (isInit) { + dispatch( + setChannelList({ + network: networkType, + channelList: { + list, + cursor, + }, + }), + ); + } else { + dispatch( + nextChannelList({ + network: networkType, + channelList: { + list, + cursor, + }, + }), + ); + } + } catch (error) { + console.log('next: error', error); + throw error; + } finally { + isLoadingRef.current = false; + } + }, + [channelList?.cursor, dispatch, networkType], + ); + + return { + next, + hasNext, + }; +}; + +export const useChannelList = () => { + const { channelListNetMap } = useImState(); + const { networkType } = useCurrentNetworkInfo(); + const { next, hasNext } = useNextChannelList(); + + const list = useMemo(() => channelListNetMap?.[networkType]?.list || [], [channelListNetMap, networkType]); + + const init = useCallback(() => { + return next(true); + }, [next]); + + return { + list, + init, + next, + hasNext, + }; +}; + +export const useCreateP2pChannel = () => { + const createChannel = useCallback((relationId: string) => { + const imInstance = im.getInstance(); + if (!imInstance) { + throw { + code: IMErrorEnum.NO_IM_INSTANCE, + message: 'No im instance', + }; + } + return imInstance.channelCreate({ + name: 'test', + type: 'P', + members: [relationId], + }); + }, []); + return createChannel; +}; + +export const refreshMessageCount = async () => { + const imInstance = im.getInstance(); + if (!imInstance) { + throw { + code: IMErrorEnum.NO_IM_INSTANCE, + message: 'No im instance', + }; + } + + const messageCount: MessageCount = { + unreadCount: 0, + mentionsCount: 0, + }; + + im.updateMessageCount(messageCount); + return messageCount; +}; + +export const useUnreadCount = () => { + const [unreadCount, setUnreadCount] = useState(0); + + useEffect(() => { + const { unreadCount } = im.getMessageCount(); + setUnreadCount(unreadCount); + + const { remove: removeMessageCountObserver } = im.registerMessageCountObserver(e => { + setUnreadCount(e.unreadCount); + }); + + return removeMessageCountObserver; + }, []); + + return unreadCount; +}; + +export const useMuteChannel = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const mute = useCallback( + async (channelId: string, value: boolean, isRefreshTotal = true) => { + // TODO: add mute request + + dispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + mute: value, + }, + }), + ); + if (isRefreshTotal) { + // + refreshMessageCount(); + } + }, + [dispatch, networkType], + ); + + return mute; +}; + +export const usePinChannel = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const pin = useCallback( + async (channelId: string, value: boolean) => { + // TODO: add pin request + + dispatch( + updateChannelAttribute({ + network: networkType, + channelId: channelId, + value: { + pin: value, + }, + }), + ); + }, + [dispatch, networkType], + ); + + return pin; +}; + +export const useHideChannel = () => { + const { networkType } = useCurrentNetworkInfo(); + const dispatch = useAppCommonDispatch(); + + const hide = useCallback( + async (channelId: string) => { + // TODO: add hide request + + dispatch( + removeChannel({ + network: networkType, + channelId, + }), + ); + }, + [dispatch, networkType], + ); + + return hide; +}; diff --git a/packages/im/README.md b/packages/im/README.md new file mode 100644 index 0000000000..70d3924a09 --- /dev/null +++ b/packages/im/README.md @@ -0,0 +1,11 @@ +# `@portkey-wallet/im` + +> TODO: description + +## Usage + +``` +const im = require('@portkey-wallet/im'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/im/index.ts b/packages/im/index.ts new file mode 100644 index 0000000000..5e0ec46a1f --- /dev/null +++ b/packages/im/index.ts @@ -0,0 +1,199 @@ +import RelationIM, { Im } from '@relationlabs/im'; +import * as utils from './utils'; +import { AElfWallet } from '@portkey-wallet/types/aelf'; +import { IMStatusEnum, Message, MessageCount } from './types'; +import { sleep } from '@portkey-wallet/utils'; + +class IM { + private _imInstance?: RelationIM; + private _token?: string; + private _msgObservers: Map void>> = new Map(); + private _channelMsgObservers: Map void>> = new Map(); + private _unreadMsgObservers: Map void> = new Map(); + private _errorObservers: Map void> = new Map(); + private _msgCount: MessageCount = { + unreadCount: 0, + mentionsCount: 0, + }; + private _msgCountObservers: Map void> = new Map(); + + public status = IMStatusEnum.INIT; + public userInfo?: { + avatar: string; + name: string; + relationId: string; + }; + + constructor() {} + + async init(account: AElfWallet, caHash: string) { + if (this._imInstance) return; + + if (this.status === IMStatusEnum.AUTHORIZING) { + throw new Error('IM is authorizing'); + } + this.status = IMStatusEnum.AUTHORIZING; + const token = await utils.sign(`${Date.now()}`, account, caHash); + if (!token) { + throw new Error('Can not get im token'); + } + this.status = IMStatusEnum.AUTHORIZED; + this._token = token; + this.initRelationIM(); + } + + initRelationIM() { + if (!this._token) { + throw new Error('IM token is not exist'); + } + + const APIKEY = '581c6c4fa0b54912b00088aa563342a4'; + this._imInstance = RelationIM.init({ token: this._token, apiKey: APIKEY, connect: true, refresh: true }); + this.listenRelationIM(this._imInstance); + this._imInstance.getUserInfo().then(result => { + console.log('getUserInfo', result.data); + this.userInfo = result.data; + }); + } + + listenRelationIM(imInstance: RelationIM) { + imInstance.bind(Im.CONNECT_OK, () => { + console.log('CONNECT_OK'); + this.status = IMStatusEnum.CONNECTED; + }); + + imInstance.bind(Im.CONNECT_ERR, (e: any) => { + console.log('CONNECT_ERR msg', e); + this.status = IMStatusEnum.ERROR; + }); + imInstance.bind(Im.CONNECT_CLOSE, async (e: any) => { + console.log('CONNECT_CLOSE msg', e); + this.status = IMStatusEnum.ERROR; + await sleep(1000); + this.updateErrorObservers(e); + try { + this.initRelationIM(); + } catch (error) { + console.log('initRelationIM error', error); + } + }); + + imInstance.bind(Im.RECEIVE_MSG_OK, (e: any) => { + console.log('RECEIVE_MSG_OK msg', e); + this.updateMsgObservers(e); + }); + } + + getInstance() { + return this._imInstance; + } + + registerUnreadMsgObservers(cb: (e: any) => void) { + const symbol = Symbol(); + const unreadMsgObservers = this._unreadMsgObservers; + unreadMsgObservers.set(symbol, cb); + return { + remove: () => { + unreadMsgObservers.has(symbol) && unreadMsgObservers.delete(symbol); + }, + }; + } + + updateUnreadMsgObservers(e: any) { + this._unreadMsgObservers.forEach(cb => { + cb(e); + }); + } + + registerChannelMsgObserver(channelId: string, cb: (e: any) => void) { + const symbol = Symbol(); + const channelMsgObservers = this._channelMsgObservers; + + let channelObservers = channelMsgObservers.get(channelId); + if (channelObservers) { + channelObservers.set(symbol, cb); + } else { + channelObservers = new Map([[symbol, cb]]); + channelMsgObservers.set(channelId, channelObservers); + } + + return { + remove: () => { + const channelObservers = channelMsgObservers.get(channelId); + if (!channelObservers) return; + channelObservers.has(symbol) && channelObservers.delete(symbol); + if (channelObservers.size === 0) { + channelMsgObservers.delete(channelId); + } + }, + }; + } + + updateMsgObservers(e: any) { + const rawMsg: Message = e['im-message']; + const channelId = rawMsg.channelUuid; + const channelObservers = this._channelMsgObservers.get(channelId); + if (channelObservers) { + channelObservers.forEach(cb => { + cb(e); + }); + } else { + // no observer, update message unreadCount + this.updateUnreadMsgObservers(e); + this.updateMessageCount({ + ...this._msgCount, + unreadCount: this._msgCount.unreadCount + 1, + }); + } + } + + registerErrorObserver(cb: (e: any) => void) { + const symbol = Symbol(); + const errorObservers = this._errorObservers; + errorObservers.set(symbol, cb); + return { + remove: () => { + errorObservers.has(symbol) && errorObservers.delete(symbol); + }, + }; + } + + updateErrorObservers(e: any) { + this._errorObservers.forEach(cb => { + cb(e); + }); + } + + getMessageCount(): MessageCount { + return ( + this._msgCount || { + unreadCount: 0, + mentionsCount: 0, + } + ); + } + + registerMessageCountObserver(cb: (e: MessageCount) => void) { + const symbol = Symbol(); + const msgCountObservers = this._msgCountObservers; + msgCountObservers.set(symbol, cb); + return { + remove: () => { + msgCountObservers.has(symbol) && msgCountObservers.delete(symbol); + }, + }; + } + + updateMessageCount(e: MessageCount) { + this._msgCount = e; + this._msgCountObservers.forEach(cb => { + cb(e); + }); + } +} + +const im = new IM(); + +export default im; +export { utils }; +export * from './types'; diff --git a/packages/im/package.json b/packages/im/package.json new file mode 100644 index 0000000000..283ff91881 --- /dev/null +++ b/packages/im/package.json @@ -0,0 +1,17 @@ +{ + "name": "@portkey-wallet/im", + "publishConfig": { + "access": "public" + }, + "version": "1.3.0", + "type": "commonjs", + "scripts": { + "test": "node ./__tests__/@portkey-wallet/im.test.js" + }, + "dependencies": { + "@portkey-wallet/types": "^1.3.0", + "@portkey-wallet/api": "^1.3.0", + "@portkey-wallet/utils": "^1.3.0", + "@relationlabs/im": "^0.3.3" + } +} diff --git a/packages/im/types/index.ts b/packages/im/types/index.ts new file mode 100644 index 0000000000..156969d2a2 --- /dev/null +++ b/packages/im/types/index.ts @@ -0,0 +1,74 @@ +export type MessageType = 'SYS' | 'TEXT' | 'CARD' | 'ANNOUNCEMENT' | 'BATCH_TRANSFER'; +export type ParsedContent = string; + +export type Message = { + channelUuid: string; + sendUuid: string; + type: MessageType; + content: string; + createAt: string; + from: string; + fromAvatar?: string; + fromName?: string; + + quote?: Message; + parsedContent?: ParsedContent; + unidentified?: boolean | undefined; +}; + +export type MemberInfo = { + relationId: string; + name: string; + avatar: string; + isAdmin: boolean; +}; +export enum ChannelTypeEnum { + GROUP = 'G', + P2P = 'P', +} +export type ChannelInfo = { + uuid: string; + name: string; + icon: string; + announcement: string; + pinAnnouncement: boolean; + openAccess: boolean; + type: ChannelTypeEnum; + members: MemberInfo[]; + mute: boolean; + pin: boolean; +}; + +export enum ChannelStatusEnum { + NORMAL = 0, + EXITED = 1, + BE_REMOVED = 2, + DISBAND = 3, +} +export type ChannelItem = { + status: ChannelStatusEnum; + channelUuid: string; + displayName: string; + channelIcon: string; + channelType: ChannelTypeEnum; + unreadMessageCount: number; + mentionsCount: number; + lastMessageType: MessageType; + lastMessageContent: string; + lastPostAt: string; + mute: boolean; + pin: boolean; +}; + +export enum IMStatusEnum { + INIT = 'init', + AUTHORIZING = 'authorizing', + AUTHORIZED = 'authorized', + CONNECTED = 'connected', + ERROR = 'error', +} + +export type MessageCount = { + unreadCount: number; + mentionsCount: number; +}; diff --git a/packages/im/utils/index.ts b/packages/im/utils/index.ts new file mode 100644 index 0000000000..0d876f32bd --- /dev/null +++ b/packages/im/utils/index.ts @@ -0,0 +1,2 @@ +export * from './sign'; +export * from './parser'; diff --git a/packages/im/utils/parser.ts b/packages/im/utils/parser.ts new file mode 100644 index 0000000000..86bcb9bbd2 --- /dev/null +++ b/packages/im/utils/parser.ts @@ -0,0 +1,6 @@ +import { messageParser as relationMessageParser, Message as RelationMessage } from '@relationlabs/im'; +import { Message } from '../types'; + +export const messageParser = (message: Message): Message => { + return relationMessageParser(message as RelationMessage) as Message; +}; diff --git a/packages/im/utils/sign.ts b/packages/im/utils/sign.ts new file mode 100644 index 0000000000..fe9b3346e1 --- /dev/null +++ b/packages/im/utils/sign.ts @@ -0,0 +1,51 @@ +import AElf from 'aelf-sdk'; +import { AElfWallet } from '@portkey-wallet/types/aelf'; +import RelationIM from '@relationlabs/im'; +import { FetchRequest } from '@portkey/request'; +const fetchRequest = new FetchRequest({ + baseURL: 'https://api.relationlabs.ai', + headers: { + // RelationOne api need Accept: 'application/json, text/plain, */*', + Accept: 'application/json, text/plain, */*', + }, +}); + +export const sign = async (message: string, account: AElfWallet, caHash: string) => { + if (!account.keyPair) { + throw new Error('no keyPair'); + } + + const hexMsg = AElf.utils.sha256(message); + const signature = account.keyPair.sign(Buffer.from(hexMsg, 'hex'), { + canonical: true, + }); + if (signature.recoveryParam === null) { + throw new Error('no recoveryParam'); + } + const signatureStr = [ + signature.r.toString('hex', 64), + signature.s.toString('hex', 64), + `0${signature.recoveryParam.toString()}`, + ].join(''); + + const { data: verifyData } = await fetchRequest.send({ + url: '/api/v1/verify/aelf', + method: 'POST', + body: JSON.stringify({ + message, + signature: signatureStr, + address: account.address, + caHash, + }), + }); + const { token } = verifyData || {}; + + // TODO: RelationIM test APIKEY + const APIKEY = '581c6c4fa0b54912b00088aa563342a4'; + const { error, token: unifiedAuthToken } = await RelationIM.getRelationToken(token, APIKEY); + + if (error) { + throw error; + } + return unifiedAuthToken; +}; diff --git a/packages/mobile-app-did/js/hooks/useAsyncStorageState.ts b/packages/mobile-app-did/js/hooks/useAsyncStorageState.ts new file mode 100644 index 0000000000..0a311adb0b --- /dev/null +++ b/packages/mobile-app-did/js/hooks/useAsyncStorageState.ts @@ -0,0 +1,76 @@ +import { Dispatch, SetStateAction, useCallback, useState, useRef, useLayoutEffect } from 'react'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +const deserializer = JSON.parse; + +const useAsyncStorageState = ( + key: string, + initialValue?: T, +): [T | undefined, Dispatch>, () => void] => { + if (!key) { + throw new Error('useAsyncStorage key may not be falsy'); + } + + // eslint-disable-next-line react-hooks/rules-of-hooks, @typescript-eslint/no-shadow + const initializer = useRef(async (key: string) => { + try { + const serializer = JSON.stringify; + + const AsyncStorageValue = await AsyncStorage.getItem(key); + if (AsyncStorageValue !== null) { + return deserializer(AsyncStorageValue); + } else { + initialValue && AsyncStorage.setItem(key, serializer(initialValue)); + return initialValue; + } + } catch { + // If user is in private mode or has storage restriction + // AsyncStorage can throw. JSON.parse and JSON.stringify + // can throw, too. + return initialValue; + } + }); + + const [state, setState] = useState(); + + // eslint-disable-next-line react-hooks/rules-of-hooks + useLayoutEffect(() => { + (async () => { + const init = await initializer.current(key); + setState(init); + })(); + }, [key]); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const set: Dispatch> = useCallback( + valOrFunc => { + try { + // eslint-disable-next-line @typescript-eslint/ban-types + const newState = typeof valOrFunc === 'function' ? (valOrFunc as Function)(state) : valOrFunc; + if (typeof newState === 'undefined') return; + const value = JSON.stringify(newState); + + AsyncStorage.setItem(key, value); + setState(deserializer(value)); + } catch { + // If user is in private mode or has storage restriction + // AsyncStorage can throw. Also JSON.stringify can throw. + } + }, + [key, state], + ); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const remove = useCallback(() => { + try { + AsyncStorage.removeItem(key); + setState(undefined); + } catch { + // If user is in private mode or has storage restriction + // AsyncStorage can throw. + } + }, [key, setState]); + + return [state, set, remove]; +}; + +export default useAsyncStorageState; diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index eb146f15d4..d6ef90ae73 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -9,11 +9,24 @@ import { useGetCurrentCAViewContract } from './contract'; import { useGetGuardiansInfoWriteStore, useGetVerifierServers } from './guardian'; import useEffectOnce from './useEffectOnce'; import { useBookmarkList, useCheckAndInitNetworkDiscoverMap } from './discover'; +import { usePin } from './store'; +import { getManagerAccount } from 'utils/redux'; +import im from '@portkey-wallet/im'; + +// const getCurrentCAContract = useGetCurrentCAContract(); + +// const getDeviceInfo = useGetDeviceInfo(); +// const originChainId = useOriginChainId(); +// const chainInfo = useCurrentChain(originChainId); +// const getHolderInfo = useGetHolderInfo(); +// const { userGuardiansList } = useGuardiansInfo(); +// const createChannel = useCreateP2pChannel(); export default function useInitData() { const dispatch = useAppDispatch(); + const pin = usePin(); const getCurrentCAViewContract = useGetCurrentCAViewContract(); - const { caHash } = useCurrentWalletInfo(); + const wallet = useCurrentWalletInfo(); const getVerifierServers = useGetVerifierServers(); const getGuardiansInfoWriteStore = useGetGuardiansInfoWriteStore(); @@ -22,6 +35,18 @@ export default function useInitData() { const { refresh: loadBookmarkList } = useBookmarkList(); + const initIM = useCallback(async () => { + if (!pin) return; + const account = getManagerAccount(pin); + if (!account || !wallet.caHash) return; + + try { + await im.init(account, wallet.caHash); + } catch (error) { + console.log('im init error', error); + } + }, [pin, wallet.caHash]); + const init = useCallback(async () => { try { // mainnet only @@ -34,23 +59,26 @@ export default function useInitData() { dispatch(getSymbolImagesAsync()); loadBookmarkList(); + initIM(); // getGuardiansInfoWriteStore after getVerifierServers await getVerifierServers(); getGuardiansInfoWriteStore({ - caHash, + caHash: wallet.caHash, }); } catch (error) { console.log(error, '====error'); } }, [ - caHash, dispatch, getCurrentCAViewContract, getGuardiansInfoWriteStore, getVerifierServers, + initIM, isMainNetwork, loadBookmarkList, + wallet.caHash, ]); + useEffectOnce(() => { // init data after transition animation const timer = setTimeout(init, 500); diff --git a/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts b/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts index aca2af7f72..7ee265d524 100644 --- a/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts +++ b/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts @@ -1,11 +1,34 @@ +import { useLatestRef } from '@portkey-wallet/hooks'; +import { windowHeight } from '@portkey-wallet/utils/mobile/device'; import { isIOS } from '@rneui/base'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { LayoutAnimation, KeyboardEvent } from 'react-native'; import { Keyboard } from 'react-native'; +import { nextAnimation } from 'utils/animation'; +import useEffectOnce from './useEffectOnce'; +import useAsyncStorageState from './useAsyncStorageState'; + +type KeyboardEventListener = (event: KeyboardEvent) => void; + +const DefaultKeyboardHeight = 336; // Note that only keyboardDidShow and keyboardDidHide events are available on Android const showEventName = isIOS ? 'keyboardWillShow' : 'keyboardDidShow'; const hideEventName = isIOS ? 'keyboardWillHide' : 'keyboardDidHide'; +export function useKeyboardListener({ show, hide }: { show?: KeyboardEventListener; hide?: KeyboardEventListener }) { + const latestShow = useLatestRef(show); + const latestHide = useLatestRef(hide); + useEffectOnce(() => { + const showListener = latestShow.current ? Keyboard.addListener(showEventName, latestShow.current) : undefined; + const hideListener = latestHide.current ? Keyboard.addListener(hideEventName, latestHide.current) : undefined; + return () => { + showListener?.remove(); + hideListener?.remove(); + }; + }); +} + export default function useKeyboardHeight() { const [keyboardHeight, setKeyboardHeight] = useState(0); useEffect(() => { @@ -18,3 +41,47 @@ export default function useKeyboardHeight() { }, []); return keyboardHeight; } + +export function useKeyboard(topSpacing = 0, isAnimation = false) { + const [keyboardHeight, setKeyboardHeight] = useAsyncStorageState('KeyboardHeight', DefaultKeyboardHeight); + const [isKeyboardOpened, setIsKeyboardOpened] = useState(); + const show: KeyboardEventListener = useCallback( + event => { + if (isAnimation) + nextAnimation({ + duration: event.duration, + create: { + duration: event.duration, + type: LayoutAnimation.Types[event.easing], + property: LayoutAnimation.Properties.opacity, + }, + }); + + setKeyboardHeight(isIOS ? event.endCoordinates.height : windowHeight - event.endCoordinates.screenY); + setIsKeyboardOpened(true); + }, + [isAnimation, setKeyboardHeight], + ); + + const hide: KeyboardEventListener = useCallback( + event => { + if (isAnimation) + nextAnimation({ + duration: event.duration, + create: { + duration: event.duration, + type: LayoutAnimation.Types[event.easing], + property: LayoutAnimation.Properties.opacity, + }, + }); + + setIsKeyboardOpened(false); + }, + [isAnimation], + ); + useKeyboardListener({ show, hide }); + return useMemo( + () => ({ keyboardHeight: (keyboardHeight || DefaultKeyboardHeight) - topSpacing, isKeyboardOpened }), + [isKeyboardOpened, keyboardHeight, topSpacing], + ); +} diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index d6b6a49b19..df94a82f6f 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -11,7 +11,7 @@ import useInitData from 'hooks/useInitData'; import { useTabMenuList } from 'hooks/cms'; import DiscoverHome from 'pages/Discover/DiscoverHome'; import ChatHome from 'pages/Chat/ChatHome'; -import { formatMessageNumToStr } from '@portkey-wallet/utils/chat'; +import { formatMessageCountToStr } from '@portkey-wallet/utils/chat'; const Tab = createBottomTabNavigator(); @@ -115,7 +115,7 @@ export default function TabRoot() { name={ele.name} component={ele.component} options={{ - tabBarBadge: ele.name === TabRouteNameEnum.CHAT ? formatMessageNumToStr(200) : undefined, + tabBarBadge: ele.name === TabRouteNameEnum.CHAT ? formatMessageCountToStr(200) : undefined, title: t(ele.label), tabBarActiveTintColor: defaultColors.font4, }} diff --git a/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx index f9199c0763..3968c529f1 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx @@ -1,51 +1,61 @@ -import React, { useCallback, useState } from 'react'; -import { View, Text, SafeAreaView, StyleSheet, TouchableOpacity } from 'react-native'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { View, Text, SafeAreaView, StyleSheet, TouchableOpacity, Image } from 'react-native'; import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; import { pTd } from 'utils/unit'; import { defaultColors } from 'assets/theme'; - -import { useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; -import { RouteInfoType } from 'utils/qrcode'; -import { useFocusEffect, useNavigation } from '@react-navigation/native'; import GStyles from 'assets/theme/GStyles'; import { isIOS, screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import { Camera } from 'expo-camera'; +import Touchable from 'components/Touchable'; +import useEffectOnce from 'hooks/useEffectOnce'; const QrScanner: React.FC = () => { - const { currentNetwork } = useWallet(); - - const navigation = useNavigation(); - const routesArr: RouteInfoType[] = navigation.getState().routes; - const previousRouteInfo = routesArr[routesArr.length - 2]; - console.log(previousRouteInfo, '=====previousRouteInfo'); + const cameraRef = useRef(); + const [imgUrl, setImgUrl] = useState(''); + const [status, requestCameraPermission] = Camera.useCameraPermissions(); - const [refresh, setRefresh] = useState(); + const takePicture = useCallback(async () => { + if (!cameraRef?.current) return; + try { + const result = await cameraRef.current?.takePictureAsync(); + console.log('======result===', result, result.uri); + setImgUrl(result.uri); + } catch (error) { + console.log('------', error); + } + }, []); - useFocusEffect( - useCallback(() => { - setRefresh(false); - }, []), - ); + useEffectOnce(() => { + (async () => { + const result = await requestCameraPermission(); + console.log('=====requestCameraPermission====result', result); + })(); + }); return ( - {refresh ? null : ( - - - - - { - navigationService.goBack(); - }}> - - - - - - )} + + + + + { + navigationService.goBack(); + }}> + + + + + + + + + ); }; @@ -87,35 +97,30 @@ export const PageStyle = StyleSheet.create({ svgWrap: { ...GStyles.paddingArg(16, 0, 16, 16), }, - scan: { - marginTop: pTd(136), - marginLeft: 'auto', - marginRight: 'auto', - }, - title: { - marginTop: pTd(62), - fontSize: pTd(16), - color: defaultColors.font2, - textAlign: 'center', + leftBlock: { + flex: 1, }, - tips: { - // position: 'absolute', - // bottom: 100, - color: defaultColors.font7, - textAlign: 'center', + buttonWrap: { width: screenWidth, - lineHeight: pTd(20), - marginTop: pTd(54), - }, - albumWrap: { position: 'absolute', - bottom: pTd(75), + zIndex: 100, + bottom: 100, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', }, - albumText: { - marginTop: pTd(4), - textAlign: 'center', + button: { + marginHorizontal: 'auto', + borderRadius: 50, + height: 100, + width: 100, + backgroundColor: defaultColors.bg1, }, - leftBlock: { - flex: 1, + img: { + position: 'absolute', + zIndex: 100, + top: 100, + width: 100, + height: 100, }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx index 9907e765c8..f24576a5ce 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import { StyleSheet, View, Image } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; @@ -7,10 +7,29 @@ import { pTd } from 'utils/unit'; import { TextM } from 'components/CommonText'; import Chats from '../components/Chats'; +import Svg from 'components/Svg'; +import Touchable from 'components/Touchable'; +import ChatOverlay from '../components/ChatOverlay'; +import navigationService from 'utils/navigationService'; +import { ChatOperationsEnum } from '@portkey-wallet/constants/constants-ca/chat'; const ChatDetails = () => { + const onPressMore = useCallback((event: { nativeEvent: { pageX: any; pageY: any } }) => { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showChatPopover( + [ + { title: ChatOperationsEnum.PROFILE, onPress: () => navigationService.navigate('Profile') }, + { title: ChatOperationsEnum.MUTE }, + ], + pageX, + pageY, + 'left', + ); + }, []); + return ( { source={{ uri: 'https://lmg.jj20.com/up/allimg/1111/05161Q64001/1P516164001-3-1200.jpg' }} style={{ width: 40, height: 40 }} /> - Mason + Masosn
    + } + rightDom={ + + + }>
    diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index f644320230..ec33ff881e 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -1,27 +1,38 @@ -import React, { useMemo } from 'react'; -import { FlatList, StyleSheet, TouchableOpacity, View } from 'react-native'; +import React, { useCallback, useEffect, useMemo } from 'react'; +import { View, StyleSheet } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; -import { pTd } from 'utils/unit'; - import navigationService from 'utils/navigationService'; -import ChatListItem from '../components/ChatHomeListItem'; import Svg from 'components/Svg'; import SafeAreaBox from 'components/SafeAreaBox'; import { BGStyles } from 'assets/theme/styles'; import CustomHeader from 'components/CustomHeader'; import ChatOverlay from '../components/ChatOverlay'; import Touchable from 'components/Touchable'; +import ChatList from '../components/ChatList'; +import CommonButton from 'components/CommonButton'; +import { useChannelList, useCreateP2pChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { pTd } from 'utils/unit'; +import im from '@portkey-wallet/im'; export default function DiscoverHome() { + const createChannel = useCreateP2pChannel(); + const { + list: channelList, + init: initChannelList, + next: nextChannelList, + hasNext: hasNextChannelList, + } = useChannelList(); + const RightDom = useMemo(() => { return ( - navigationService.navigate('SearchPeople')}> + navigationService.navigate('SearchPeople')}> { const { pageX, pageY } = event.nativeEvent; ChatOverlay.showChatPopover( @@ -40,29 +51,47 @@ export default function DiscoverHome() { ); }, []); + useEffect(() => { + console.log('channelList', channelList); + const imInstance = im.getInstance(); + if (!imInstance) return; + imInstance.getUserInfo().then(e => console.log(e)); + }, [channelList]); + + const createCha = useCallback(async () => { + try { + const result = await createChannel('nutbk-6aaaa-aaaaj-7hatq-cai'); + console.log('result', result); + } catch (error) { + console.log('createChannel: error', error); + } + }, [createChannel]); + + const initChannel = useCallback(async () => { + initChannelList(); + }, [initChannelList]); + + const sendMess = useCallback(async () => { + console.log('=== c7b961729aaa46e6ab73f70fa0ee6055'); + }, []); + return ( - console.log('delete')} />} - /> + + + + ); } -const styles = StyleSheet.create({ - containerStyles: { - backgroundColor: defaultColors.bg4, - paddingHorizontal: 0, - paddingBottom: 0, - flex: 1, - }, - inputContainer: { - ...GStyles.paddingArg(8, 20), +export const styles = StyleSheet.create({ + searchIcon: { + paddingHorizontal: pTd(12), }, - svgWrap: { - padding: pTd(16), + addIcon: { + paddingLeft: pTd(12), + paddingRight: pTd(16), }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx b/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx new file mode 100644 index 0000000000..ff1850f62d --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx @@ -0,0 +1,74 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { FlatList, StyleSheet, View, Image } from 'react-native'; +import PageContainer from 'components/PageContainer'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import { pTd } from 'utils/unit'; +import { TextM } from 'components/CommonText'; +import CommonInput from 'components/CommonInput'; +import navigationService from 'utils/navigationService'; +import Touchable from 'components/Touchable'; +import NoData from 'components/NoData'; +import useDebounce from 'hooks/useDebounce'; +import RecommendSection from '../components/RecommendSection'; +import { BGStyles } from 'assets/theme/styles'; +import Svg from 'components/Svg'; + +const mock_data = [{ id: 1 }]; + +const FindMorePeople = () => { + const [keyword, setKeyword] = useState(''); + const [loading, setLoading] = useState(false); + const debounceWord = useDebounce(keyword, 500); + const [list, setList] = useState(mock_data); + + useEffect(() => { + setLoading(true); + setList([{ id: 1 }]); + setLoading(false); + }, [debounceWord]); + + const renderItem = useCallback((item: any) => { + console.log(item); + return ( + navigationService.navigate('Profile')}> + + Sally + stranger + navigationService.navigate('ChatDetails')}> + + + + ); + }, []); + + return ( + + setKeyword(v)} /> + {!keyword && My Portkey Id: xxxxxxx} + {list.length ? ( + } renderItem={renderItem} /> + ) : ( + + )} + + ); +}; + +export default FindMorePeople; + +const styles = StyleSheet.create({ + container: { + backgroundColor: defaultColors.bg4, + flex: 1, + ...GStyles.paddingArg(0), + }, + svgWrap: { + padding: pTd(16), + }, + buttonGroupWrap: {}, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx index d04e64ef49..8f62b9ab5d 100644 --- a/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx @@ -1,27 +1,56 @@ -import React from 'react'; -import { StyleSheet } from 'react-native'; +import React, { useCallback, useState } from 'react'; +import { FlatList, StyleSheet } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; +import Touchable from 'components/Touchable'; +import { TextM } from 'components/CommonText'; +import navigationService from 'utils/navigationService'; +import NoData from 'components/NoData'; +import Svg from 'components/Svg'; +import CommonInput from 'components/CommonInput'; + +const mock_data = [{ id: 1 }]; const NewChatHome = () => { + const [keyword, setKeyword] = useState(''); + const [filterList, setFilterList] = useState(mock_data); + + const renderItem = useCallback((item: any) => { + return ( + navigationService.navigate('ChatDetails')}> + Sally + navigationService.navigate('ChatDetails')}> + + + + ); + }, []); + return ( + hideTouchable={true} + containerStyles={styles.containerStyles} + titleDom="New Chat"> + setKeyword(v)} value={keyword} /> + } renderItem={renderItem} /> + ); }; export default NewChatHome; const styles = StyleSheet.create({ - container: { + containerStyles: { backgroundColor: defaultColors.bg4, + paddingHorizontal: 0, flex: 1, - ...GStyles.paddingArg(0), + }, + inputContainer: { + ...GStyles.paddingArg(8, 20), }, svgWrap: { padding: pTd(16), diff --git a/packages/mobile-app-did/js/pages/Chat/Profile/index.tsx b/packages/mobile-app-did/js/pages/Chat/Profile/index.tsx index b8871d9400..b35239710f 100644 --- a/packages/mobile-app-did/js/pages/Chat/Profile/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/Profile/index.tsx @@ -1,8 +1,102 @@ -import React from 'react'; +import React, { useCallback, useState } from 'react'; +import { StyleSheet, View, Image, ScrollView } from 'react-native'; +import PageContainer from 'components/PageContainer'; +import { defaultColors } from 'assets/theme'; +import GStyles from 'assets/theme/GStyles'; +import FormItem from 'components/FormItem'; -import { TextM } from 'components/CommonText'; +import { pTd } from 'utils/unit'; +import CommonInput from 'components/CommonInput'; +import { useLanguage } from 'i18n/hooks'; +import CommonButton from 'components/CommonButton'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; import navigationService from 'utils/navigationService'; +import { TextM } from 'components/CommonText'; +import CommonAvatar from 'components/CommonAvatar'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; + +const Profile = () => { + const { t } = useLanguage(); + + const [keyword, setKeyword] = useState('11'); + const [errorMessage, setErrorMessage] = useState(''); + + const onKeywordChange = useCallback((v: string) => { + setKeyword(v.trim()); + }, []); + + return ( + + + + + + Wallet Name + + + + + + + + + + + + + navigationService.navigate('ChatDetails')} /> + + + + ); +}; + +export default Profile; -export default function DiscoverHome() { - return navigationService.navigate('ChatDetails')}>ChatHome; -} +const styles = StyleSheet.create({ + container: { + backgroundColor: defaultColors.bg4, + flex: 1, + ...GStyles.paddingArg(20), + }, + svgWrap: { + padding: pTd(16), + }, + buttonGroupWrap: { + position: 'absolute', + bottom: 0, + width: screenWidth, + paddingHorizontal: pTd(20), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx b/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx index e62392754a..1ea5f54d1c 100644 --- a/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx @@ -1,15 +1,57 @@ -import React, { useState } from 'react'; -import { StyleSheet } from 'react-native'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { FlatList, StyleSheet } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import PageContainer from 'components/PageContainer'; import InputWithCancel from 'components/InputWithCancel'; -import SearchPeopleItem from '../components/SearchPeopleItem'; +import CommonButton from 'components/CommonButton'; +import { useFocusEffect } from '@react-navigation/native'; +import NoData from 'components/NoData'; +import { Image } from '@rneui/base'; +import { TextM } from 'components/CommonText'; +import Touchable from 'components/Touchable'; +const mock_data = [0, 1, 2]; export default function SearchPeople() { + const iptRef = useRef(); + const timerRef = useRef(null); + const [keyword, setKeyword] = useState(''); + const [filterList, setFilterList] = useState(mock_data); + + useFocusEffect( + useCallback(() => { + if (iptRef?.current) { + timerRef.current = setTimeout(() => { + iptRef.current.focus(); + }, 300); + } + }, []), + ); + + useEffect(() => { + setFilterList(mock_data); + }, [keyword]); + + useEffect( + () => () => { + if (timerRef.current) clearTimeout(timerRef.current); + }, + [], + ); + + const renderItem = useCallback((item: any) => { + console.log(item); + return ( + navigationService.navigate('ChatDetails')}> + + Sally + 2 + + ); + }, []); return ( setKeyword(v)} value={keyword} clearText={() => setKeyword('')} onCancel={() => navigationService.goBack()} /> - - + navigationService.navigate('FindMorePeople')}>FindMorePeople + } renderItem={renderItem} /> ); } diff --git a/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx new file mode 100644 index 0000000000..8cf620464d --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import OverlayModal from 'components/OverlayModal'; +import { Keyboard, StyleSheet } from 'react-native'; +import { TextM } from 'components/CommonText'; +import { ModalBody } from 'components/ModalBody'; +import { useBookmarkList } from 'hooks/discover'; + +type ValueType = string | number; +type DefaultValueType = string; + +type ItemTypeBase = { + chainId: T; + [key: string]: any; +}; + +type SelectListProps, ItemValueType extends ValueType> = { + value?: ItemValueType; + list: Array; + callBack: (item: ItemType) => void; + labelAttrName?: string; +}; + +const BookmarksOverlay = () => { + const { bookmarkList } = useBookmarkList(); + + return ( + + {bookmarkList.map(ele => ( + {JSON.stringify(ele)} + ))} + + ); +}; + +const showList = , ItemValueType extends ValueType = DefaultValueType>( + params: SelectListProps, +) => { + console.log(params); + Keyboard.dismiss(); + OverlayModal.show(, { + position: 'bottom', + }); +}; + +export default { + showList, +}; + +const styles = StyleSheet.create({ + bubbleWrap: {}, + bubbleToolWrap: { + zIndex: 1000, + position: 'absolute', + bottom: -30, + left: 100, + }, + bubbleToolItem: { + width: 60, + height: 60, + // backgroundColor: 'green', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItem/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItem/index.tsx deleted file mode 100644 index d97aec2cbc..0000000000 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItem/index.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { TextM } from 'components/CommonText'; -import React, { memo, useCallback } from 'react'; -import { StyleSheet, View, Image } from 'react-native'; -import { ListItem } from '@rneui/themed'; -import Svg from 'components/Svg'; -import { pTd } from 'utils/unit'; -import { defaultColors } from 'assets/theme'; -import CommonButton from 'components/CommonButton'; -import GStyles from 'assets/theme/GStyles'; -import navigationService from 'utils/navigationService'; -import { BGStyles } from 'assets/theme/styles'; -import ChatOverlay from '../ChatOverlay'; - -type ChatListItemProps = { - onDelete: () => void; -}; - -export default memo(function ChatListItem(props: ChatListItemProps) { - const { onDelete } = props; - - const deleteItem = useCallback(() => { - onDelete(); - }, [onDelete]); - - return ( - { - const { pageX, pageY } = event.nativeEvent; - ChatOverlay.showChatPopover([{ title: '1111111' }, { title: '22222222' }], pageX, pageY, 'left'); - }} - onPress={() => navigationService.navigate('ChatDetails')} - rightContent={reset => ( - reset()} - icon={{ name: 'delete', color: 'white' }} - buttonStyle={{ height: '100%', backgroundColor: 'red' }} - /> - )}> - - - - - Potter - hello Portkey - - - - - - 16:00 - - 99+ - - - - ); -}); - -const styles = StyleSheet.create({ - itemWrap: { - borderBottomColor: 'green', - borderBottomWidth: StyleSheet.hairlineWidth, - }, - underlayLeftBox: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: pTd(16), - justifyContent: 'flex-end', - backgroundColor: defaultColors.bg17, - color: defaultColors.font1, - textAlign: 'center', - }, - itemRow: { - padding: pTd(12), - height: pTd(72), - }, - deleteIconWrap: { - marginRight: pTd(16), - }, - websiteIconStyle: { - marginRight: pTd(16), - }, - infoWrap: { - flex: 1, - }, -}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx new file mode 100644 index 0000000000..435813bdaf --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx @@ -0,0 +1,154 @@ +import GStyles from 'assets/theme/GStyles'; +import { TextL, TextM, TextS } from 'components/CommonText'; +import Touchable from 'components/Touchable'; +import React, { memo, useCallback, useRef, useState } from 'react'; +import { StyleSheet, View, Image, GestureResponderEvent } from 'react-native'; +import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import { defaultColors } from 'assets/theme'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { formatChatListTime, formatMessageCountToStr } from '@portkey-wallet/utils/chat'; +import { ChannelItem } from '@portkey-wallet/im/types'; +import CommonAvatar from 'components/CommonAvatar'; + +type ChatHomeListItemSwipedType = { + item: T; + onPress: (item: T) => void; + onLongPress: (event: GestureResponderEvent, item: T) => void; + onDelete: (item: T) => void; +}; + +const DELETE_BUTTON_WIDTH = pTd(64); +const DELETE_TO_END = screenWidth; + +export default memo(function ChatHomeListItemSwiped(props: ChatHomeListItemSwipedType) { + const { item, onPress, onLongPress, onDelete } = props; + const [isEdit, setIsEdit] = useState(false); + const swipeableRef = useRef(null); + + const deleteItem = useCallback(() => { + swipeableRef.current?.close(); + onDelete(item); + }, [item, onDelete]); + + const renderUnderlayLeft = useCallback( + () => ( + + Delete + + ), + [deleteItem], + ); + + const onPressItem = useCallback(() => { + if (isEdit) return; + onPress(item); + }, [isEdit, item, onPress]); + + const onLongPressItem = useCallback( + (e: GestureResponderEvent) => { + if (isEdit) return; + onLongPress(e, item); + }, + [isEdit, item, onLongPress], + ); + + const onDrag = useCallback( + (params: { openDirection: OpenDirection; snapPoint: number }) => { + setIsEdit(params.snapPoint !== 0); + + if (params.snapPoint === DELETE_TO_END) { + swipeableRef.current?.close(); + onDelete(item); + } + }, + [item, onDelete], + ); + + return ( + + + + + + + + {item.displayName + 'xasdsdasdsdasdsadsadasdasdsadasdas'} + + + + {formatChatListTime(item.lastPostAt || '19933300020')} + + + {item.lastMessageContent || ' haha'} + {/* */} + + {formatMessageCountToStr(item.unreadMessageCount)} + + + + + + ); +}); + +const styles = StyleSheet.create({ + container: { + height: pTd(72), + }, + underlayLeftBox: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-end', + backgroundColor: defaultColors.bg17, + color: defaultColors.font1, + }, + deleteButton: { + width: DELETE_BUTTON_WIDTH, + textAlign: 'center', + }, + avatar: { + ...GStyles.marginArg(12, 16, 12, 20), + }, + rightDom: { + flex: 1, + paddingRight: pTd(20), + height: pTd(72) - StyleSheet.hairlineWidth, + borderBottomColor: defaultColors.border1, + borderBottomWidth: StyleSheet.hairlineWidth, + }, + deleteIconWrap: { + marginRight: pTd(16), + }, + websiteIconStyle: { + marginRight: pTd(16), + }, + infoWrap: { + flex: 1, + }, + messageNum: { + borderRadius: pTd(8), + backgroundColor: 'red', + minWidth: pTd(16), + paddingHorizontal: pTd(4), + textAlign: 'center', + overflow: 'hidden', + color: defaultColors.font2, + }, + hide: { + display: 'none', + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx new file mode 100644 index 0000000000..72c4326310 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { FlatList, StyleSheet } from 'react-native'; +import GStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import navigationService from 'utils/navigationService'; +import { BGStyles } from 'assets/theme/styles'; +import ChatOverlay from '../ChatOverlay'; +import ChatHomeListItemSwiped from '../ChatHomeListItemSwiper'; +import { ChannelItem } from '@portkey-wallet/im/types'; +import NoData from 'components/NoData'; + +type ChatListType = { + chatList: ChannelItem[]; +}; + +export default function ChatList(props: ChatListType) { + const { chatList = [] } = props; + + return ( + } + renderItem={item => ( + navigationService.navigate('ChatDetails')} + onLongPress={(event, i) => { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showChatPopover( + [ + { title: 'pin', onPress: () => navigationService.navigate('NewChatHome', { item }) }, + { title: 'mute', onPress: () => navigationService.navigate('NewChatHome', { item }) }, + { title: 'delete', onPress: () => navigationService.navigate('NewChatHome', { item }) }, + ], + pageX, + pageY, + 'left', + ); + }} + {...item} + onDelete={() => console.log('delete')} + /> + )} + /> + ); +} + +const styles = StyleSheet.create({ + containerStyles: { + backgroundColor: defaultColors.bg4, + paddingHorizontal: 0, + paddingBottom: 0, + flex: 1, + }, + inputContainer: { + ...GStyles.paddingArg(8, 20), + }, + svgWrap: { + padding: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index b9341795e9..f79c9583dd 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -1,14 +1,17 @@ -import React, { useState, useEffect } from 'react'; -import { GiftedChat, IMessage } from 'react-native-gifted-chat'; +import React, { useState, useEffect, useCallback } from 'react'; +import { GiftedChat, GiftedChatProps, IMessage, MessageTextProps, Time } from 'react-native-gifted-chat'; import initialMessages from '../messages'; -import { CustomInputToolbar, renderActions, renderSend, renderAccessory } from '../InputToolbar'; -import { RenderBubble, renderSystemMessage, renderMessage, renderMessageText } from '../MessageContainer'; +import { AccessoryBar, BottomBarContainer } from '../InputToolbar'; +import { renderSystemMessage, renderMessage } from '../MessageContainer'; import { randomId } from '@portkey-wallet/utils'; -import { Keyboard, StyleSheet } from 'react-native'; -import { defaultColors } from 'assets/theme'; +import { Keyboard } from 'react-native'; import GStyles from 'assets/theme/GStyles'; -import { pTd } from 'utils/unit'; -import { useDiscoverJumpWithNetWork } from 'hooks/discover'; +import Touchable from 'components/Touchable'; +import { useChatsDispatch } from '../context/hooks'; +import CustomBubble from '../CustomBubble'; +import { setBottomBarStatus, setChatText } from '../context/chatsContext'; +import useEffectOnce from 'hooks/useEffectOnce'; +import MessageText from '../Message/MessageText'; const user = { _id: 1, @@ -16,82 +19,72 @@ const user = { avatar: 'https://lmg.jj20.com/up/allimg/1111/05161Q64001/1P516164001-3-1200.jpg', }; -const Chats = () => { - const [text, setText] = useState(''); +const ChatsUI = () => { const [messages, setMessages] = useState([]); - - const jump = useDiscoverJumpWithNetWork(); + const dispatch = useChatsDispatch(); useEffect(() => { setMessages(initialMessages as IMessage[]); }, []); const onSend = (newMessages: IMessage[]) => { - console.log('=======newMessages=============================', newMessages); setMessages(prevMessages => GiftedChat.append(prevMessages, newMessages)); }; + useEffectOnce(() => { + return () => { + dispatch(setChatText('')); + }; + }); + + const onDismiss = useCallback(() => { + Keyboard.dismiss(); + dispatch(setBottomBarStatus(undefined)); + }, [dispatch]); + + const renderMessageText: GiftedChatProps['renderMessageText'] = useCallback( + (props: MessageTextProps) => , + [], + ); + const renderTime: GiftedChatProps['renderTime'] = useCallback((props: MessageTextProps) => { + if (props.currentMessage?.text) return null; + return
    -
    - -
    - - - -
    ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index 6302882e38..700828762b 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -18,6 +18,7 @@ export interface IViewContactProps extends BaseHeaderProps { }; editText: string; chatText: string; + addedText: string; addContactText: string; handleEdit: () => void; handleChat: () => void; @@ -34,6 +35,7 @@ export default function ViewContact() { const title = t('Contacts'); const editText = t('Edit'); const chatText = t('Chat'); + const addedText = t('Added'); const addContactText = t('Add Contact'); const goBack = useCallback(() => { @@ -61,6 +63,7 @@ export default function ViewContact() { headerTitle={title} editText={editText} chatText={chatText} + addedText={addedText} addContactText={addContactText} data={state} goBack={goBack} @@ -74,6 +77,7 @@ export default function ViewContact() { headerTitle={title} editText={editText} chatText={chatText} + addedText={addedText} addContactText={addContactText} data={state} goBack={goBack} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.less index 6540ee7f2f..41ae9fa709 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.less @@ -16,12 +16,21 @@ margin-right: 10px; } .chain { - margin-top: 4px; - color: @font-13; + margin-top: 8px; + .chain-img { + width: 16px; + height: 16px; + border-radius: 50%; + background-color: #f7f8f9; + } + .chain-text { + margin-left: 8px; + color: @font-13; + } } .address-copy-icon { - width: 18px; - height: 18px; + width: 16px; + height: 16px; cursor: pointer; } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.tsx index 184a90174a..7d655e67a2 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ContactAddressList/index.tsx @@ -30,7 +30,10 @@ export default function ContactAddressList({ list }: { list: AddressItem[] }) {
    handleCopy(ads?.address)} type="Copy" className="address-copy-icon" />
    -
    {transNetworkText(ads.chainId, isTestNet)}
    +
    + + {transNetworkText(ads.chainId, isTestNet)} +
    ))}
    From 9ba7025488fc3fb64d8a95f2af97f95c4c2eb18e Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 14 Aug 2023 21:23:48 +0800 Subject: [PATCH 512/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20chat=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/chat.ts | 14 +- .../js/assets/image/pngs/chat-robot.png | Bin 0 -> 4930 bytes .../mobile-app-did/js/assets/image/svgs.js | 2 +- .../js/assets/image/svgs/chat-add-contact.svg | 3 + .../js/assets/image/svgs/chat-add.svg | 3 + .../js/assets/image/svgs/chat-delete.svg | 3 + .../js/assets/image/svgs/chat-emoji.svg | 6 + .../js/assets/image/svgs/chat-file.svg | 12 ++ .../js/assets/image/svgs/chat-find-more.svg | 6 + .../js/assets/image/svgs/chat-keyboard.svg | 12 ++ .../js/assets/image/svgs/chat-mute.svg | 5 + .../js/assets/image/svgs/chat-new-chat.svg | 3 + .../js/assets/image/svgs/chat-pin.svg | 3 + .../js/assets/image/svgs/chat-profile.svg | 5 + .../js/assets/image/svgs/chat-reshutter.svg | 5 + .../js/assets/image/svgs/chat-robot.svg | 24 ++++ .../js/assets/image/svgs/chat-send.svg | 3 + .../js/assets/image/svgs/chat-shutter.svg | 4 + .../js/assets/image/svgs/chat-tab.svg | 3 + .../js/assets/image/svgs/chat-unmute.svg | 5 + .../js/assets/image/svgs/chat-unpin.svg | 5 + .../mobile-app-did/js/assets/theme/index.ts | 1 + .../mobile-app-did/js/assets/theme/styles.ts | 7 + .../js/components/CommonAvatar/index.tsx | 2 +- .../js/components/Touchable/index.tsx | 3 + packages/mobile-app-did/js/navigation/Tab.tsx | 53 ++++++- .../js/pages/Chat/ChatCamera/index.tsx | 95 ++++++++----- .../js/pages/Chat/ChatDetails/index.tsx | 37 +++-- .../js/pages/Chat/ChatHome/index.tsx | 7 +- .../js/pages/Chat/NewChatHome/index.tsx | 60 ++++++-- .../js/pages/Chat/SearchPeople/index.tsx | 49 +++++-- .../Chat/components/BookmarkOverlay/index.tsx | 131 ++++++++++++------ .../ChatHomeListItemSwiper/index.tsx | 36 +++-- .../pages/Chat/components/ChatList/index.tsx | 36 +++-- .../Chat/components/ChatOverlay/index.tsx | 27 ++-- .../js/pages/Chat/components/Chats/index.tsx | 4 +- .../Chat/components/FindMoreButton/index.tsx | 30 ++++ .../InputToolbar/BottomBarContainer/index.tsx | 92 ++++++++++-- .../InputToolbar/SendMessageButton/index.tsx | 9 +- .../InputToolbar/SendPicModal/index.tsx | 89 ++++++++++++ .../components/InputToolbar/ToolBar/index.tsx | 53 +++---- .../Chat/components/InputToolbar/index.ts | 2 +- .../components/RecommendSection/index.tsx | 73 +++++++--- .../Bookmark/components/BookmarkItem.tsx | 15 +- 44 files changed, 788 insertions(+), 249 deletions(-) create mode 100644 packages/mobile-app-did/js/assets/image/pngs/chat-robot.png create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-add-contact.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-add.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-delete.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-emoji.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-file.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-find-more.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-keyboard.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-new-chat.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-pin.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-profile.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-reshutter.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-robot.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-send.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-shutter.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-tab.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-unmute.svg create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-unpin.svg create mode 100644 packages/mobile-app-did/js/pages/Chat/components/FindMoreButton/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendPicModal/index.tsx diff --git a/packages/constants/constants-ca/chat.ts b/packages/constants/constants-ca/chat.ts index 936b8d1e48..63e3b47f55 100644 --- a/packages/constants/constants-ca/chat.ts +++ b/packages/constants/constants-ca/chat.ts @@ -1,11 +1,11 @@ export enum ChatOperationsEnum { - PROFILE = 'profile', - MUTE = 'mute', - UNMUTE = 'unmute', - PIN = 'pin', - UNPIN = 'unPin', - DELETE_CHAT = 'deleteChat', - ADD_CONTACT = 'addContact', + PROFILE = 'Profile', + MUTE = 'Mute', + UNMUTE = 'Unmute', + PIN = 'Pin', + UNPIN = 'Unpin', + DELETE_CHAT = 'Delete', + ADD_CONTACT = 'Add Contact', } export const ChatHomeOperationList = [ diff --git a/packages/mobile-app-did/js/assets/image/pngs/chat-robot.png b/packages/mobile-app-did/js/assets/image/pngs/chat-robot.png new file mode 100644 index 0000000000000000000000000000000000000000..ec092a9f7ad645ca80f90c4ed492dde5c0eca5d0 GIT binary patch literal 4930 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 AoB#j- literal 0 HcmV?d00001 diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index 29062109c0..c76af036d4 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","chat-add-contact":"\n\n\n","chat-add":"\n\n\n","chat-delete":"\n\n\n","chat-emoji":"\n\n\n\n\n\n","chat-file":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-find-more":"\n\n\n\n\n\n","chat-keyboard":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-mute":"\n\n\n\n\n","chat-new-chat":"\n\n\n","chat-pin":"\n\n\n","chat-profile":"\n\n\n\n\n","chat-reshutter":"\n\n\n\n\n","chat-robot":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","chat-send":"\n\n\n","chat-shutter":"\n\n\n\n","chat-tab":"\n\n\n","chat-unmute":"\n\n\n\n\n","chat-unpin":"\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-add-contact.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-add-contact.svg new file mode 100644 index 0000000000..a0b4d588b4 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-add-contact.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-add.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-add.svg new file mode 100644 index 0000000000..d51bd2c6b4 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-add.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-delete.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-delete.svg new file mode 100644 index 0000000000..833739a28c --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-emoji.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-emoji.svg new file mode 100644 index 0000000000..61d45ae35f --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-emoji.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-file.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-file.svg new file mode 100644 index 0000000000..61f90ab15f --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-file.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-find-more.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-find-more.svg new file mode 100644 index 0000000000..abe77e1c53 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-find-more.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-keyboard.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-keyboard.svg new file mode 100644 index 0000000000..719da9247c --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-keyboard.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg new file mode 100644 index 0000000000..4dae520b73 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-new-chat.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-new-chat.svg new file mode 100644 index 0000000000..21e5d47925 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-new-chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-pin.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-pin.svg new file mode 100644 index 0000000000..2ac1a7e22b --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-profile.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-profile.svg new file mode 100644 index 0000000000..cf124c9b2e --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-profile.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-reshutter.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-reshutter.svg new file mode 100644 index 0000000000..b12e577a04 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-reshutter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-robot.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-robot.svg new file mode 100644 index 0000000000..bdfcad772e --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-robot.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-send.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-send.svg new file mode 100644 index 0000000000..3a5d324960 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-send.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-shutter.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-shutter.svg new file mode 100644 index 0000000000..84fe5e8f07 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-shutter.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-tab.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-tab.svg new file mode 100644 index 0000000000..c77ab578f7 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-tab.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-unmute.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-unmute.svg new file mode 100644 index 0000000000..f1601d862d --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-unmute.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-unpin.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-unpin.svg new file mode 100644 index 0000000000..5a20f66ef6 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-unpin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/theme/index.ts b/packages/mobile-app-did/js/assets/theme/index.ts index 204680cd4a..c5ac210941 100644 --- a/packages/mobile-app-did/js/assets/theme/index.ts +++ b/packages/mobile-app-did/js/assets/theme/index.ts @@ -25,6 +25,7 @@ export const defaultColors = { bg16: '#C5CBD5', bg17: '#EA4F45', bg18: '#F0F1F4', + bg19: '#000000', font1: '#464B53', font2: 'white', diff --git a/packages/mobile-app-did/js/assets/theme/styles.ts b/packages/mobile-app-did/js/assets/theme/styles.ts index 0adcb63ec6..961a9ba4af 100644 --- a/packages/mobile-app-did/js/assets/theme/styles.ts +++ b/packages/mobile-app-did/js/assets/theme/styles.ts @@ -15,6 +15,13 @@ export const BGStyles = StyleSheet.create({ bg10: { backgroundColor: defaultColors.bg10 }, bg11: { backgroundColor: defaultColors.bg11 }, bg12: { backgroundColor: defaultColors.bg12 }, + bg13: { backgroundColor: defaultColors.bg13 }, + bg14: { backgroundColor: defaultColors.bg14 }, + bg15: { backgroundColor: defaultColors.bg15 }, + bg16: { backgroundColor: defaultColors.bg16 }, + bg17: { backgroundColor: defaultColors.bg17 }, + bg18: { backgroundColor: defaultColors.bg18 }, + bg19: { backgroundColor: defaultColors.bg19 }, transparent: { backgroundColor: 'transparent' }, }); diff --git a/packages/mobile-app-did/js/components/CommonAvatar/index.tsx b/packages/mobile-app-did/js/components/CommonAvatar/index.tsx index 72bf6292bb..2fe3e0087f 100644 --- a/packages/mobile-app-did/js/components/CommonAvatar/index.tsx +++ b/packages/mobile-app-did/js/components/CommonAvatar/index.tsx @@ -33,7 +33,7 @@ export default function CommonAvatar(props: CommonAvatarProps) { const sizeStyle = { width: Number(avatarSize), height: Number(avatarSize), - lineHeight: Number(avatarSize), + lineHeight: hasBorder ? Number(avatarSize) - pTd(2) : Number(avatarSize), borderRadius: shapeType === 'square' ? pTd(6) : Number(avatarSize) / 2, }; diff --git a/packages/mobile-app-did/js/components/Touchable/index.tsx b/packages/mobile-app-did/js/components/Touchable/index.tsx index af1de04790..17d17958fa 100644 --- a/packages/mobile-app-did/js/components/Touchable/index.tsx +++ b/packages/mobile-app-did/js/components/Touchable/index.tsx @@ -1,6 +1,7 @@ import { useThrottleCallback } from '@portkey-wallet/hooks'; import React, { memo } from 'react'; import { TouchableOpacity, TouchableHighlight, TouchableOpacityProps } from 'react-native'; +import { pTd } from 'utils/unit'; type TouchableProps = { onPressWithSecond?: number; @@ -15,6 +16,7 @@ const Touchable: React.FC = props => { if (highlight) return ( = props => { return ( = { [TabRouteNameEnum.WALLET]: { name: TabRouteNameEnum.WALLET, index: 0, - label: 'wallet', + label: 'Wallet', icon: 'logo-icon', component: DashBoard, }, @@ -50,13 +54,13 @@ export const tabMenuTypeMap: Record = { name: TabRouteNameEnum.CHAT, index: 2, label: 'Chat', - icon: 'my', + icon: 'chat-tab', component: ChatHome, }, [TabRouteNameEnum.SETTINGS]: { name: TabRouteNameEnum.SETTINGS, index: 3, - label: 'Wallet', + label: 'My', icon: 'my', component: MyMenu, }, @@ -68,6 +72,7 @@ export default function TabRoot() { const { t } = useLanguage(); const { address } = useCurrentWalletInfo(); const tabMenuListStore = useTabMenuList(); + const unreadCount = useUnreadCount(); const tabMenuList = useMemo(() => { if (__DEV__) return defaultTabMenuList; @@ -106,8 +111,23 @@ export default function TabRoot() { tabBarAllowFontScaling: false, header: () => null, tabBarIcon: ({ focused }) => { - const iconName: IconName = tabMenuList.find(tab => tab.name === route.name)?.icon ?? 'logo-icon'; - return ; + const tabMenu = tabMenuList.find(tab => tab.name === route.name); + if (tabMenu?.name === TabRouteNameEnum.CHAT && unreadCount > 0) { + return ( + + {formatMessageCountToStr(unreadCount)} + + + ); + } + + return ( + + ); }, })}> {tabMenuList.map(ele => ( @@ -116,7 +136,6 @@ export default function TabRoot() { name={ele.name} component={ele.component} options={{ - tabBarBadge: ele.name === TabRouteNameEnum.CHAT ? formatMessageCountToStr(200) : undefined, title: t(ele.label), tabBarActiveTintColor: defaultColors.font4, }} @@ -125,3 +144,25 @@ export default function TabRoot() { ); } + +const styles = StyleSheet.create({ + chatWrap: { + position: 'relative', + }, + messageCount: { + position: 'absolute', + zIndex: 1000, + left: pTd(15), + top: -pTd(6), + height: pTd(18), + minWidth: pTd(18), + borderColor: defaultColors.bg1, + borderWidth: pTd(1), + borderRadius: pTd(9), + backgroundColor: defaultColors.bg17, + color: defaultColors.font2, + overflow: 'hidden', + textAlign: 'center', + paddingHorizontal: pTd(4), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx index 3968c529f1..fe5f7e9fa6 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatCamera/index.tsx @@ -9,6 +9,9 @@ import { isIOS, screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/d import { Camera } from 'expo-camera'; import Touchable from 'components/Touchable'; import useEffectOnce from 'hooks/useEffectOnce'; +import CommonButton from 'components/CommonButton'; +import { BGStyles } from 'assets/theme/styles'; +import SafeAreaBox from 'components/SafeAreaBox'; const QrScanner: React.FC = () => { const cameraRef = useRef(); @@ -21,11 +24,18 @@ const QrScanner: React.FC = () => { const result = await cameraRef.current?.takePictureAsync(); console.log('======result===', result, result.uri); setImgUrl(result.uri); + cameraRef.current.pausePreview(); } catch (error) { console.log('------', error); } }, []); + const resetCamera = useCallback(() => { + if (!cameraRef?.current) return; + cameraRef.current.resumePreview(); + setImgUrl(''); + }, []); + useEffectOnce(() => { (async () => { const result = await requestCameraPermission(); @@ -34,12 +44,12 @@ const QrScanner: React.FC = () => { }); return ( - - - + + + { - - - - + + + {imgUrl && ( + + + + )} + {!imgUrl && ( + + + + )} + {imgUrl && } + + - - + ); }; export default QrScanner; export const PageStyle = StyleSheet.create({ + safeAreaBox: { + backgroundColor: defaultColors.bg19, + }, wrapper: { width: '100%', height: '100%', - opacity: 0.85, backgroundColor: defaultColors.bgColor1, }, barCodeScanner: { width: '100%', - height: '100%', - position: 'absolute', + flex: 1, zIndex: 100, }, barCodeScannerAndroid: { width: screenWidth, height: screenHeight, }, - innerView: { - width: '100%', - height: '100%', - }, iconWrap: { - marginTop: pTd(16), + marginTop: pTd(32), width: '100%', display: 'flex', flexDirection: 'row', @@ -102,25 +126,22 @@ export const PageStyle = StyleSheet.create({ }, buttonWrap: { width: screenWidth, - position: 'absolute', + height: pTd(112), zIndex: 100, - bottom: 100, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + paddingHorizontal: pTd(20), }, - button: { - marginHorizontal: 'auto', - borderRadius: 50, - height: 100, - width: 100, - backgroundColor: defaultColors.bg1, + reshutterWrap: { + borderRadius: pTd(20), + overflow: 'hidden', }, - img: { - position: 'absolute', - zIndex: 100, - top: 100, - width: 100, - height: 100, + shutter: { + flex: 1, + }, + sendButton: { + height: pTd(40), + paddingHorizontal: pTd(16), + }, + previewImage: { + width: '100%', }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx index 8e963f395a..70d97a262e 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx @@ -4,7 +4,7 @@ import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; -import { TextM } from 'components/CommonText'; +import { TextM, TextL } from 'components/CommonText'; import Chats from '../components/Chats'; import Svg from 'components/Svg'; @@ -12,14 +12,22 @@ import Touchable from 'components/Touchable'; import ChatOverlay from '../components/ChatOverlay'; import navigationService from 'utils/navigationService'; import { ChatOperationsEnum } from '@portkey-wallet/constants/constants-ca/chat'; +import CommonAvatar from 'components/CommonAvatar'; +import { FontStyles } from 'assets/theme/styles'; const ChatDetails = () => { const onPressMore = useCallback((event: { nativeEvent: { pageX: any; pageY: any } }) => { const { pageX, pageY } = event.nativeEvent; ChatOverlay.showChatPopover({ list: [ - { title: ChatOperationsEnum.PROFILE, onPress: () => navigationService.navigate('Profile') }, - { title: ChatOperationsEnum.MUTE }, + { + title: ChatOperationsEnum.PROFILE, + iconName: 'chat-profile', + onPress: () => navigationService.navigate('Profile'), + }, + { title: ChatOperationsEnum.PIN, iconName: 'chat-unpin' }, + { title: ChatOperationsEnum.MUTE, iconName: 'chat-unmute' }, + { title: ChatOperationsEnum.DELETE_CHAT, iconName: 'chat-delete' }, ], px: pageX, py: pageY, @@ -29,22 +37,23 @@ const ChatDetails = () => { return ( - - Masosn + leftDom={ + + + + + + Name } rightDom={ - - + + }> @@ -60,7 +69,7 @@ const styles = StyleSheet.create({ flex: 1, ...GStyles.paddingArg(0), }, - svgWrap: { - padding: pTd(16), + headerAvatar: { + fontSize: pTd(14), }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index b876188c77..a45521ca70 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -30,7 +30,7 @@ export default function DiscoverHome() { return ( navigationService.navigate('SearchPeople')}> - + navigationService.navigate('NewChatHome'), }, - { title: 'Add Contact asdasd' }, + { title: 'Add Contact', iconName: 'chat-add-contact' }, ], formatType: 'dynamicWidth', customPosition: { right: pTd(20), top: pageY + 20 }, customBounds: { x: screenWidth - pTd(20), y: pageY + 20, width: 0, height: 0 }, }); }}> - + ); diff --git a/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx index 8f62b9ab5d..d426a1ad21 100644 --- a/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx @@ -1,17 +1,20 @@ import React, { useCallback, useState } from 'react'; -import { FlatList, StyleSheet } from 'react-native'; +import { FlatList, StyleSheet, View } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; -import { TextM } from 'components/CommonText'; +import { TextL, TextM, TextS } from 'components/CommonText'; import navigationService from 'utils/navigationService'; import NoData from 'components/NoData'; import Svg from 'components/Svg'; import CommonInput from 'components/CommonInput'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import CommonAvatar from 'components/CommonAvatar'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; -const mock_data = [{ id: 1 }]; +const mock_data = new Array(100).map(() => ({ id: 1 })); const NewChatHome = () => { const [keyword, setKeyword] = useState(''); @@ -19,11 +22,14 @@ const NewChatHome = () => { const renderItem = useCallback((item: any) => { return ( - navigationService.navigate('ChatDetails')}> - Sally - navigationService.navigate('ChatDetails')}> - - + navigationService.navigate('ChatDetails')}> + + + Sally + navigationService.navigate('ChatDetails')}> + Chat + + ); }, []); @@ -35,8 +41,20 @@ const NewChatHome = () => { hideTouchable={true} containerStyles={styles.containerStyles} titleDom="New Chat"> - setKeyword(v)} value={keyword} /> - } renderItem={renderItem} /> + + { + setKeyword(v); + }} + /> + + } + renderItem={renderItem} + /> ); }; @@ -55,4 +73,26 @@ const styles = StyleSheet.create({ svgWrap: { padding: pTd(16), }, + itemWrap: { + width: screenWidth, + height: pTd(72), + }, + avatarStyle: { + marginHorizontal: pTd(20), + marginVertical: pTd(18), + }, + rightSection: { + height: pTd(72), + flex: 1, + borderBottomColor: defaultColors.border1, + borderBottomWidth: StyleSheet.hairlineWidth, + paddingRight: pTd(20), + }, + chatButton: { + backgroundColor: defaultColors.bg5, + borderRadius: pTd(6), + overflow: 'hidden', + paddingHorizontal: pTd(12), + paddingVertical: pTd(4), + }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx b/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx index 1ea5f54d1c..75f419e4ed 100644 --- a/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { FlatList, StyleSheet } from 'react-native'; +import { FlatList, StyleSheet, View } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; @@ -10,8 +10,11 @@ import CommonButton from 'components/CommonButton'; import { useFocusEffect } from '@react-navigation/native'; import NoData from 'components/NoData'; import { Image } from '@rneui/base'; -import { TextM } from 'components/CommonText'; +import { TextM, TextL } from 'components/CommonText'; import Touchable from 'components/Touchable'; +import FindMoreButton from '../components/FindMoreButton'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import CommonAvatar from 'components/CommonAvatar'; const mock_data = [0, 1, 2]; export default function SearchPeople() { @@ -45,10 +48,13 @@ export default function SearchPeople() { const renderItem = useCallback((item: any) => { console.log(item); return ( - navigationService.navigate('ChatDetails')}> - - Sally - 2 + navigationService.navigate('ChatDetails')}> + + + Sally + ); }, []); @@ -68,8 +74,13 @@ export default function SearchPeople() { clearText={() => setKeyword('')} onCancel={() => navigationService.goBack()} /> - navigationService.navigate('FindMorePeople')}>FindMorePeople - } renderItem={renderItem} /> + + Chats} + ListEmptyComponent={} + renderItem={renderItem} + /> ); } @@ -80,10 +91,24 @@ const styles = StyleSheet.create({ paddingHorizontal: 0, flex: 1, }, - inputContainer: { - ...GStyles.paddingArg(8, 20), + listHeader: { + ...GStyles.paddingArg(16, 20, 8), + color: defaultColors.font9, + }, + itemWrap: { + width: screenWidth, + height: pTd(72), }, - svgWrap: { - padding: pTd(16), + avatarStyle: { + marginHorizontal: pTd(20), + marginVertical: pTd(18), + }, + rightSection: { + height: pTd(72), + flex: 1, + borderBottomColor: defaultColors.border1, + borderBottomWidth: 1, + paddingRight: pTd(20), + justifyContent: 'center', }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx index 8cf620464d..a3586f62f1 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/BookmarkOverlay/index.tsx @@ -1,65 +1,118 @@ -import React from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import OverlayModal from 'components/OverlayModal'; -import { Keyboard, StyleSheet } from 'react-native'; -import { TextM } from 'components/CommonText'; +import { FlatList, Keyboard, StyleSheet, View } from 'react-native'; +import { TextM, TextS } from 'components/CommonText'; import { ModalBody } from 'components/ModalBody'; import { useBookmarkList } from 'hooks/discover'; +import Touchable from 'components/Touchable'; +import DiscoverWebsiteImage from 'pages/Discover/components/DiscoverWebsiteImage'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import GStyles from 'assets/theme/GStyles'; +import { getFaviconUrl } from '@portkey-wallet/utils/dapp/browser'; +import TextWithProtocolIcon from 'components/TextWithProtocolIcon'; +import { pTd } from 'utils/unit'; +import { IBookmarkItem } from '@portkey-wallet/store/store-ca/discover/type'; +import Lottie from 'lottie-react-native'; +import useEffectOnce from 'hooks/useEffectOnce'; +import NoData from 'components/NoData'; +import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; -type ValueType = string | number; -type DefaultValueType = string; - -type ItemTypeBase = { - chainId: T; - [key: string]: any; +type SelectListProps = { + onPressCallBack: (item: IBookmarkItem) => void; }; -type SelectListProps, ItemValueType extends ValueType> = { - value?: ItemValueType; - list: Array; - callBack: (item: ItemType) => void; - labelAttrName?: string; -}; +const BookmarksOverlay = (props: SelectListProps) => { + const { onPressCallBack } = props; + const { bookmarkList, refresh } = useBookmarkList(); + + const [initializing, setInitializing] = useState(true); + const [totalAccount, setTotalAccount] = useState(0); + + const renderItem = useCallback( + ({ item }: { item: IBookmarkItem }) => { + return ( + onPressCallBack(item)}> + + + + + {item?.url} + + + + ); + }, + [onPressCallBack], + ); -const BookmarksOverlay = () => { - const { bookmarkList } = useBookmarkList(); + const fetchBookmarkList = useLockCallback(async () => { + try { + if (!initializing && totalAccount <= bookmarkList.length) return; + const result = await refresh(bookmarkList.length); + setTotalAccount(result.totalCount); + } catch (error) { + console.log(error); + } + }, [bookmarkList.length, refresh]); + + useEffectOnce(() => { + (async () => { + try { + setInitializing(true); + await fetchBookmarkList(); + setInitializing(false); + } catch (error) { + console.log(error); + } + })(); + }); return ( - - {bookmarkList.map(ele => ( - {JSON.stringify(ele)} - ))} + + {initializing ? ( + + + + ) : ( + } + renderItem={renderItem} + /> + )} ); }; -const showList = , ItemValueType extends ValueType = DefaultValueType>( - params: SelectListProps, -) => { +const showBookmarkList = (params: SelectListProps) => { console.log(params); Keyboard.dismiss(); - OverlayModal.show(, { + OverlayModal.show(, { position: 'bottom', }); }; export default { - showList, + showBookmarkList, }; const styles = StyleSheet.create({ - bubbleWrap: {}, - bubbleToolWrap: { - zIndex: 1000, - position: 'absolute', - bottom: -30, - left: 100, + websiteIconStyle: { + marginRight: pTd(16), + }, + infoWrap: { + flex: 1, + }, + itemWrap: { + width: '100%', + height: pTd(72), + paddingHorizontal: pTd(20), }, - bubbleToolItem: { - width: 60, - height: 60, - // backgroundColor: 'green', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + loadingIcon: { + width: pTd(24), + height: pTd(24), }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx index 435813bdaf..1392f078f5 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx @@ -77,26 +77,35 @@ export default memo(function ChatHomeListItemSwiped(props: ChatHomeListItemSwipe snapPointsLeft={[DELETE_BUTTON_WIDTH, DELETE_TO_END]} renderUnderlayLeft={renderUnderlayLeft}> - - {item.displayName + 'xasdsdasdsdasdsadsadasdasdsadasdas'} + + {/* TODO: Remark */} + {item.displayName} - + - {formatChatListTime(item.lastPostAt || '19933300020')} + {formatChatListTime(item.lastPostAt)} - - {item.lastMessageContent || ' haha'} - {/* */} - - {formatMessageCountToStr(item.unreadMessageCount)} + + + {/* TODO: Image */} + + {item.lastMessageContent ? item.lastMessageContent : '[Image]'} + {item.pin ? ( + + ) : ( + + {formatMessageCountToStr(item.unreadMessageCount)} + + )} @@ -139,6 +148,10 @@ const styles = StyleSheet.create({ infoWrap: { flex: 1, }, + blank: { + width: '100%', + height: pTd(2), + }, messageNum: { borderRadius: pTd(8), backgroundColor: 'red', @@ -151,4 +164,7 @@ const styles = StyleSheet.create({ hide: { display: 'none', }, + muteMessage: { + backgroundColor: defaultColors.bg7, + }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx index 0c9518cd72..ac7b6c08a8 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx @@ -21,7 +21,7 @@ export default function ChatList(props: ChatListType) { } + ListEmptyComponent={} renderItem={item => ( navigationService.navigate('ChatDetails')} @@ -30,12 +30,25 @@ export default function ChatList(props: ChatListType) { ChatOverlay.showChatPopover({ list: [ - { title: 'pin', onPress: () => navigationService.navigate('NewChatHome', { item }) }, - { title: 'mute', onPress: () => navigationService.navigate('NewChatHome', { item }) }, - { title: 'delete', onPress: () => navigationService.navigate('NewChatHome', { item }) }, + { + title: 'pin', + iconName: 'chat-pin', + onPress: () => navigationService.navigate('NewChatHome', { item }), + }, + { + title: 'mute', + iconName: 'chat-mute', + onPress: () => navigationService.navigate('NewChatHome', { item }), + }, + { + title: 'delete', + iconName: 'chat-delete', + onPress: () => navigationService.navigate('NewChatHome', { item }), + }, ], px: pageX, py: pageY, + formatType: 'dynamicWidth', }); }} {...item} @@ -45,18 +58,3 @@ export default function ChatList(props: ChatListType) { /> ); } - -const styles = StyleSheet.create({ - containerStyles: { - backgroundColor: defaultColors.bg4, - paddingHorizontal: 0, - paddingBottom: 0, - flex: 1, - }, - inputContainer: { - ...GStyles.paddingArg(8, 20), - }, - svgWrap: { - padding: pTd(16), - }, -}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx index f479a909ee..ac4f3edea2 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx @@ -3,15 +3,19 @@ import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; import OverlayModal, { CustomBounds } from 'components/OverlayModal'; import Touchable from 'components/Touchable'; import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; +import Svg, { IconName, SvgProps } from 'components/Svg'; +import { pTd } from 'utils/unit'; +import { defaultColors } from 'assets/theme'; +import { TextL } from 'components/CommonText'; const vertical = 20; const horizontal = 20; -const itemHeight = 40; +const itemHeight = pTd(48); const BoxWidth = 100; const horizontalSpacing = 120; const verticalSpacing = 300; -export type ListItemType = { onPress?: () => void; title: string }; +export type ListItemType = { onPress?: () => void; title: string; iconName: IconName }; type ShowChatPopoverParams = { list: ListItemType[]; @@ -73,6 +77,7 @@ function ChatPopover({ OverlayModal.hide(); }} style={formatType === 'fixedWidth' ? styles.itemStyles : styles.dynamicWidthItemStyles}> + {item.title} ); @@ -117,10 +122,9 @@ export default { const itemStyle = StyleSheet.create({ item: { height: itemHeight, + paddingHorizontal: pTd(16), flexDirection: 'row', alignItems: 'center', - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: 'rgba(0,0,0,0.1)', }, }); @@ -134,14 +138,21 @@ const styles = StyleSheet.create({ ...itemStyle.item, }, container: { - paddingTop: 5, + paddingVertical: pTd(4), position: 'absolute', - backgroundColor: 'white', - borderRadius: 5, + borderRadius: pTd(6), zIndex: 100, + minWidth: pTd(136), + shadowOffset: { width: 2, height: 5 }, + backgroundColor: defaultColors.bg1, + shadowColor: defaultColors.shadow1, + shadowOpacity: 0.15, + shadowRadius: 10, + elevation: 2, }, textStyles: { - marginLeft: 10, + marginLeft: pTd(16), + color: defaultColors.font5, }, backgroundBox: { height: screenHeight, width: screenWidth, backgroundColor: 'transparent' }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index f79c9583dd..b5a98b4d46 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -12,6 +12,7 @@ import CustomBubble from '../CustomBubble'; import { setBottomBarStatus, setChatText } from '../context/chatsContext'; import useEffectOnce from 'hooks/useEffectOnce'; import MessageText from '../Message/MessageText'; +import { BGStyles } from 'assets/theme/styles'; const user = { _id: 1, @@ -56,12 +57,11 @@ const ChatsUI = () => { return ( <> - + navigationService.navigate('FindMorePeople')}> + + Find More People + + ); +} + +const styles = StyleSheet.create({ + wrap: { + height: pTd(48), + paddingLeft: pTd(20), + borderBottomColor: defaultColors.border6, + borderBottomWidth: StyleSheet.hairlineWidth, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx index e5a7843e14..1d342fcc9e 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx @@ -13,13 +13,30 @@ import { useKeyboardAnim } from '../../hooks'; import { useBottomBarStatus, useChatText, useChatsDispatch } from '../../context/hooks'; import { ChatBottomBarStatus } from 'store/chat/slice'; import { setBottomBarStatus, setChatText } from '../../context/chatsContext'; +import { BGStyles } from 'assets/theme/styles'; +import { defaultColors } from 'assets/theme'; +import { SendMessageButton } from '../SendMessageButton'; export const ActionsIcon = memo(function ActionsIcon({ onPress }: { onPress?: () => void }) { - return } optionTintColor="#222B45" />; + return ( + } + optionTintColor="#222B45" + /> + ); }); export const EmojiIcon = memo(function EmojiIcon({ onPress }: { onPress?: () => void }) { - return } optionTintColor="#222B45" />; + return ( + } + optionTintColor="#222B45" + /> + ); }); export function AndroidInputContainer({ @@ -94,28 +111,37 @@ export function BottomBarContainer({ children }: { children?: ReactNode; showKey timer.current && clearTimeout(timer.current); }; }); + return ( <> - + onPressActionButton(ChatBottomBarStatus.tools)} /> {isIOS ? ( - dispatch(setChatText(v))} - value={text} - style={GStyles.flex1} - ref={textInputRef as any} - /> - ) : ( - + dispatch(setChatText(v))} - style={GStyles.flex1} + value={text} ref={textInputRef as any} /> + onPressActionButton(ChatBottomBarStatus.emoji)} /> + + ) : ( + + + dispatch(setChatText(v))} + ref={textInputRef as any} + /> + onPressActionButton(ChatBottomBarStatus.emoji)} /> + )} - onPressActionButton(ChatBottomBarStatus.emoji)} /> + {children} @@ -123,6 +149,44 @@ export function BottomBarContainer({ children }: { children?: ReactNode; showKey } const styles = StyleSheet.create({ + wrap: { + position: 'relative', + paddingHorizontal: pTd(16), + paddingVertical: pTd(10), + }, + fileSvg: { + marginLeft: 0, + marginBottom: pTd(8), + marginRight: pTd(8), + }, + emojiSvg: { + marginLeft: 0, + marginBottom: 0, + position: 'absolute', + right: pTd(8), + bottom: pTd(8), + }, + inputWrapStyle: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + backgroundColor: defaultColors.bg1, + borderRadius: pTd(20), + paddingRight: pTd(40), + paddingVertical: pTd(6), + minHeight: pTd(40), + maxHeight: pTd(200), + }, + input: { + width: '100%', + paddingLeft: pTd(16), + }, + sendStyle: { + marginBottom: pTd(8), + marginLeft: pTd(8), + width: pTd(24), + height: pTd(24), + }, toolsItem: { width: '25%', margin: pTd(2), diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendMessageButton/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendMessageButton/index.tsx index fc78e138ed..89c97e4c19 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendMessageButton/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendMessageButton/index.tsx @@ -1,9 +1,10 @@ import Svg from 'components/Svg'; import React from 'react'; -import { Send, GiftedChatProps } from 'react-native-gifted-chat'; +import { IMessage, Send, SendProps } from 'react-native-gifted-chat'; +import { pTd } from 'utils/unit'; -export const SendMessageButton: GiftedChatProps['renderSend'] = props => ( - - +export const SendMessageButton: React.FC> = props => ( + + ); diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendPicModal/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendPicModal/index.tsx new file mode 100644 index 0000000000..e5cba6b0ed --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/SendPicModal/index.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { View, Keyboard, Image } from 'react-native'; +import ButtonRow, { ButtonRowProps } from 'components/ButtonRow'; +import { StyleSheet } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import OverlayModal from 'components/OverlayModal'; + +type ShowSendPicProps = { + uri: string; + buttons?: ButtonRowProps['buttons']; + autoClose?: boolean; +}; + +function SendPicBody({ uri, buttons, autoClose = true }: ShowSendPicProps) { + return ( + + + ({ + ...i, + onPress: () => { + if (autoClose) OverlayModal.hide(); + i.onPress?.(); + }, + }))} + /> + + ); +} + +const showSendPic = (props: ShowSendPicProps) => { + Keyboard.dismiss(); + OverlayModal.show(, { + modal: true, + type: 'zoomOut', + position: 'center', + }); +}; +export default { + showSendPic, +}; + +export const styles = StyleSheet.create({ + sheetBox: { + overflow: 'hidden', + borderRadius: 5, + backgroundColor: 'white', + }, + itemText: { + color: defaultColors.primaryColor, + fontSize: 16, + }, + itemBox: { + width: '100%', + paddingVertical: 15, + overflow: 'hidden', + borderBottomWidth: StyleSheet.hairlineWidth, + alignItems: 'center', + justifyContent: 'center', + borderBottomColor: defaultColors.border1, + }, + cancelText: { + fontSize: 16, + }, + cancelBox: { + width: '100%', + paddingVertical: 15, + marginTop: 20, + borderRadius: 5, + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'white', + }, + alertBox: { + overflow: 'hidden', + borderRadius: 8, + alignItems: 'center', + width: screenWidth - 48, + backgroundColor: 'white', + padding: pTd(24), + }, + imagePreview: { + width: pTd(280), + height: pTd(280), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx index fdb56653cd..ec82602c21 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx @@ -1,7 +1,5 @@ -import Svg from 'components/Svg'; -import React, { memo } from 'react'; +import React, { memo, useCallback } from 'react'; import { StyleSheet, View } from 'react-native'; -import { Actions } from 'react-native-gifted-chat'; import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; import navigationService from 'utils/navigationService'; @@ -9,37 +7,44 @@ import { TextM } from 'components/CommonText'; import * as ImagePicker from 'expo-image-picker'; import GStyles from 'assets/theme/GStyles'; +import SendPicModal from '../SendPicModal'; +import BookmarkOverlay from '../../BookmarkOverlay'; -export const ActionsIcon = memo(function ActionsIcon({ onPress }: { onPress?: () => void }) { - return } optionTintColor="#222B45" />; -}); +export const ToolBar = memo(function ToolBar() { + const selectPhoto = useCallback(async () => { + const result = (await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: false, + allowsMultipleSelection: false, + })) as unknown as { uri: string }; + console.log(result); -export const EmojiIcon = memo(function EmojiIcon({ onPress }: { onPress?: () => void }) { - return } optionTintColor="#222B45" />; -}); + if (result.uri) { + SendPicModal.showSendPic({ + uri: result.uri, + buttons: [ + { + title: 'Cancel', + type: 'outline', + }, + { + title: 'Send', + type: 'primary', + }, + ], + }); + } + }, []); -export const ToolBar = memo(function ToolBar() { return ( navigationService.navigate('ChatCamera')}> camera - - { - const result = (await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.Images, - allowsEditing: false, - selectionLimit: 9, - allowsMultipleSelection: true, - })) as unknown as { uri: string }; - console.log(result); - }}> + photo - - + BookmarkOverlay.showBookmarkList()}> Bookmark diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/index.ts b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/index.ts index 4c53d7be6b..0f6e0ec94e 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/index.ts +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/index.ts @@ -2,4 +2,4 @@ export * from './AccessoryBar'; export * from './BottomBarContainer'; export * from './Emoticons'; export * from './SendMessageButton'; -export * from './SendMessageButton'; +export * from './ToolBar'; diff --git a/packages/mobile-app-did/js/pages/Chat/components/RecommendSection/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/RecommendSection/index.tsx index 8c02ff1d2b..7c468b76a3 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/RecommendSection/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/RecommendSection/index.tsx @@ -1,47 +1,74 @@ -import React from 'react'; -import { FlatList, StyleSheet, View } from 'react-native'; +import React, { useCallback } from 'react'; +import { FlatList, StyleSheet, View, Image } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; -import { BGStyles } from 'assets/theme/styles'; -import { TextM } from 'components/CommonText'; +import { BGStyles, FontStyles } from 'assets/theme/styles'; +import { TextL, TextM, TextS } from 'components/CommonText'; import Touchable from 'components/Touchable'; -import navigationService from 'utils/navigationService'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import chatRobot from 'assets/image/pngs/chat-robot.png'; export default function RecommendSection() { + const renderItem = useCallback(() => { + return ( + + + + Robot + + Chat + + + + ); + }, []); + return ( - Recommend - ( - - Robot - navigationService.navigate('ChatDetails')} style={styles.chatButton}> - chat! - - - )} - /> + + Recommend + + Skip + + + ); } const styles = StyleSheet.create({ containerStyles: { - paddingHorizontal: 0, - paddingBottom: 0, flex: 1, backgroundColor: defaultColors.bg1, }, - inputContainer: { - ...GStyles.paddingArg(8, 20), + top: { + ...GStyles.paddingArg(16, 20, 8), }, svgWrap: { padding: pTd(16), }, + itemWrap: { + width: screenWidth, + height: pTd(72), + }, + avatarStyle: { + width: pTd(48), + height: pTd(48), + marginHorizontal: pTd(20), + marginVertical: pTd(12), + }, + rightSection: { + borderBottomColor: defaultColors.border1, + borderBottomWidth: StyleSheet.hairlineWidth, + paddingRight: pTd(20), + }, chatButton: { - backgroundColor: 'red', + backgroundColor: defaultColors.bg5, + color: defaultColors.font2, + paddingHorizontal: pTd(12), + paddingVertical: pTd(4), + borderRadius: pTd(6), + overflow: 'hidden', }, }); diff --git a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx index d2dd36d53c..f28f0abd1d 100644 --- a/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx +++ b/packages/mobile-app-did/js/pages/Discover/Bookmark/components/BookmarkItem.tsx @@ -87,16 +87,7 @@ export default memo( snapPointsLeft={[80]} renderUnderlayLeft={renderUnderlayLeft}> - + {EditDom} @@ -105,9 +96,6 @@ export default memo( {item?.url} - {/* - drag - */} @@ -120,7 +108,6 @@ export default memo( ); const styles = StyleSheet.create({ - marginContainer: {}, underlayLeftBox: { flex: 1, flexDirection: 'row', From f3ce8e1bc63b61ddf56218677c1c3e820a00303b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Mon, 14 Aug 2023 21:25:13 +0800 Subject: [PATCH 513/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20args?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Chat/components/InputToolbar/ToolBar/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx index ec82602c21..3bb4146c30 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx @@ -44,7 +44,15 @@ export const ToolBar = memo(function ToolBar() { photo - BookmarkOverlay.showBookmarkList()}> + + BookmarkOverlay.showBookmarkList({ + onPressCallBack: item => { + console.log(item); + }, + }) + }> Bookmark
    From c6cc4f819b587b180bc13cde8a378c8f864d6252 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Tue, 15 Aug 2023 11:47:13 +0800 Subject: [PATCH 514/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20chat=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Chat/components/Chats/index.tsx | 13 +++-- .../InputToolbar/AccessoryBar/index.tsx | 49 +++++++++------- .../Chat/components/context/chatsContext.tsx | 2 +- .../js/pages/Chat/components/context/hooks.ts | 5 ++ .../js/pages/Chat/utils/index.ts | 56 +++++++++++++++++++ .../mobile-app-did/js/store/chat/slice.ts | 9 +++ 6 files changed, 108 insertions(+), 26 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Chat/utils/index.ts diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index f79c9583dd..f2e3865398 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -9,9 +9,10 @@ import GStyles from 'assets/theme/GStyles'; import Touchable from 'components/Touchable'; import { useChatsDispatch } from '../context/hooks'; import CustomBubble from '../CustomBubble'; -import { setBottomBarStatus, setChatText } from '../context/chatsContext'; +import { setBottomBarStatus, setChatText, setShowSoftInputOnFocus } from '../context/chatsContext'; import useEffectOnce from 'hooks/useEffectOnce'; import MessageText from '../Message/MessageText'; +import { destroyChatInputRecorder, initChatInputRecorder } from 'pages/Chat/utils'; const user = { _id: 1, @@ -22,7 +23,6 @@ const user = { const ChatsUI = () => { const [messages, setMessages] = useState([]); const dispatch = useChatsDispatch(); - useEffect(() => { setMessages(initialMessages as IMessage[]); }, []); @@ -32,8 +32,11 @@ const ChatsUI = () => { }; useEffectOnce(() => { + initChatInputRecorder(); return () => { dispatch(setChatText('')); + dispatch(setShowSoftInputOnFocus(true)); + destroyChatInputRecorder(); }; }); @@ -62,20 +65,20 @@ const ChatsUI = () => { user={user} alwaysShowSend scrollToBottom - isCustomViewBottom onSend={onSend} + isCustomViewBottom messages={messages} showUserAvatar={false} + renderTime={renderTime} renderAvatar={() => null} - renderInputToolbar={() => null} renderBubble={renderBubble} messageIdGenerator={randomId} renderMessage={renderMessage} + renderInputToolbar={() => null} showAvatarForEveryMessage={false} isKeyboardInternallyHandled={false} renderMessageText={renderMessageText} renderSystemMessage={renderSystemMessage} - renderTime={renderTime} /> diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx index 50d6284df6..bd7d13b640 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx @@ -1,34 +1,43 @@ -import React, { useMemo } from 'react'; +import React, { memo, useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; import { pTd } from 'utils/unit'; import GStyles from 'assets/theme/GStyles'; import Emoticons from '../Emoticons'; import { BGStyles } from 'assets/theme/styles'; -import { useBottomBarStatus, useChatsDispatch, useLatestText } from '../../context/hooks'; +import { useBottomBarStatus, useChatsDispatch } from '../../context/hooks'; import { ChatBottomBarStatus } from 'store/chat/slice'; import { ToolBar } from '../ToolBar'; +import { handleInputText } from 'pages/Chat/utils'; import { setChatText } from '../../context/chatsContext'; -export function AccessoryBar() { - const bottomBarStatus = useBottomBarStatus(); - const dispatch = useChatsDispatch(); - const latestText = useLatestText(); - const showTools = useMemo(() => !!bottomBarStatus, [bottomBarStatus]); - return ( - - - dispatch(setChatText(latestText.current + item.code))} /> +export const AccessoryBar = memo( + function AccessoryBar() { + const bottomBarStatus = useBottomBarStatus(); + const dispatch = useChatsDispatch(); + + const showTools = useMemo(() => !!bottomBarStatus, [bottomBarStatus]); + return ( + + + { + const text = handleInputText(item.code); + dispatch(setChatText(text)); + }} + /> + + - - - ); -} + ); + }, + () => true, +); const styles = StyleSheet.create({ toolsItem: { diff --git a/packages/mobile-app-did/js/pages/Chat/components/context/chatsContext.tsx b/packages/mobile-app-did/js/pages/Chat/components/context/chatsContext.tsx index 51d8fb8ed8..13b6b01818 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/context/chatsContext.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/context/chatsContext.tsx @@ -1,3 +1,3 @@ import { chatSlice } from 'store/chat/slice'; -export const { setChatText, setBottomBarStatus } = chatSlice.actions; +export const { setChatText, setBottomBarStatus, setShowSoftInputOnFocus } = chatSlice.actions; diff --git a/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts b/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts index 3d12e75c59..d7f8ae7fdf 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts +++ b/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts @@ -8,6 +8,11 @@ export function useBottomBarStatus() { export function useChatText() { return useAppSelector(state => state.chats.text); } + +export function useShowSoftInputOnFocus() { + return useAppSelector(state => state.chats.showSoftInputOnFocus); +} + export function useChatsDispatch() { return useAppDispatch(); } diff --git a/packages/mobile-app-did/js/pages/Chat/utils/index.ts b/packages/mobile-app-did/js/pages/Chat/utils/index.ts new file mode 100644 index 0000000000..f4453140ea --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/utils/index.ts @@ -0,0 +1,56 @@ +import { TextInputScrollEventData, TextInputSelectionChangeEventData } from 'react-native'; + +export class ChatInputRecorder { + public text?: string; + public selection?: TextInputSelectionChangeEventData['selection']; + public contentOffset?: TextInputScrollEventData['contentOffset']; + public setText(text: string) { + this.text = text; + } + public setSelection(selection: TextInputSelectionChangeEventData['selection']) { + this.selection = selection; + } + public setContentOffset(contentOffset?: TextInputScrollEventData['contentOffset']) { + this.contentOffset = contentOffset; + } +} +let chatInputRecorder: ChatInputRecorder | undefined; + +function initChatInputRecorder() { + if (chatInputRecorder) return; + chatInputRecorder = new ChatInputRecorder(); +} + +function destroyChatInputRecorder() { + if (!chatInputRecorder) return; + chatInputRecorder = undefined; +} + +function handleInputText(code: string): string { + let text = chatInputRecorder?.text || ''; + if (chatInputRecorder?.selection) { + const { start, end } = chatInputRecorder?.selection; + if (start === end) { + const first = text.slice(0, start) + code; + const last = text.slice(start); + chatInputRecorder.setSelection({ start: first.length, end: first.length }); + text = first + last; + } else { + const first = text.slice(0, start); + const last = text.slice(end); + text = first + last; + chatInputRecorder.setText(text); + chatInputRecorder.setSelection({ start: first.length, end: first.length }); + return handleInputText(code); + } + } else { + text = chatInputRecorder?.text + code; + chatInputRecorder?.setSelection({ start: text.length, end: text.length }); + } + if (chatInputRecorder) { + chatInputRecorder.setText(text); + } + return text; +} + +export { chatInputRecorder, initChatInputRecorder, destroyChatInputRecorder, handleInputText }; diff --git a/packages/mobile-app-did/js/store/chat/slice.ts b/packages/mobile-app-did/js/store/chat/slice.ts index 34402e1b44..bb53d48a06 100644 --- a/packages/mobile-app-did/js/store/chat/slice.ts +++ b/packages/mobile-app-did/js/store/chat/slice.ts @@ -1,4 +1,5 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; +import { TextInputSelectionChangeEventData } from 'react-native'; export enum ChatBottomBarStatus { input, @@ -10,6 +11,8 @@ export interface ChatsState { showTools?: boolean; bottomBarStatus?: ChatBottomBarStatus; text: string; + selection?: TextInputSelectionChangeEventData['selection']; + showSoftInputOnFocus?: boolean; } const initialState: ChatsState = { text: '' }; @@ -20,8 +23,14 @@ export const chatSlice = createSlice({ setChatText: (state, action: PayloadAction) => { state.text = action.payload; }, + setChatSelection: (state, action: PayloadAction) => { + state.selection = action.payload; + }, setBottomBarStatus: (state, action: PayloadAction) => { state.bottomBarStatus = action.payload; }, + setShowSoftInputOnFocus: (state, action: PayloadAction) => { + state.showSoftInputOnFocus = action.payload; + }, }, }); From 0a843d8ac18d052e1250ed9ed3dac00218b7e04d Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 15 Aug 2023 13:48:56 +0800 Subject: [PATCH 515/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/constants/constants-ca/assets.ts | 2 +- .../app/web/Popup/routes/index.tsx | 6 +- .../app/web/Prompt/routes/index.tsx | 10 +- .../Popup/index.less | 8 +- .../Popup/index.tsx | 14 +-- .../Prompt/index.less | 12 +- .../Prompt/index.tsx | 23 ++-- .../{FindMorePeople => FindMore}/index.tsx | 49 ++------ .../Contacts/ViewContact/Popup/index.less | 48 +------- .../Contacts/ViewContact/Popup/index.tsx | 52 ++------- .../Contacts/ViewContact/Prompt/index.less | 104 +---------------- .../Contacts/ViewContact/Prompt/index.tsx | 68 ++--------- .../web/pages/Contacts/ViewContact/index.tsx | 55 +++++---- .../components/ContactsBody/index.less | 6 +- .../components/ContactsBody/index.tsx | 12 +- .../components/ViewContactBody/index.less | 109 ++++++++++++++++++ .../components/ViewContactBody/index.tsx | 92 +++++++++++++++ 17 files changed, 311 insertions(+), 359 deletions(-) rename packages/web-extension-did/app/web/pages/Contacts/{FindMorePeople => FindMore}/Popup/index.less (84%) rename packages/web-extension-did/app/web/pages/Contacts/{FindMorePeople => FindMore}/Popup/index.tsx (72%) rename packages/web-extension-did/app/web/pages/Contacts/{FindMorePeople => FindMore}/Prompt/index.less (76%) rename packages/web-extension-did/app/web/pages/Contacts/{FindMorePeople => FindMore}/Prompt/index.tsx (58%) rename packages/web-extension-did/app/web/pages/Contacts/{FindMorePeople => FindMore}/index.tsx (64%) create mode 100644 packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less create mode 100644 packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx diff --git a/packages/constants/constants-ca/assets.ts b/packages/constants/constants-ca/assets.ts index 9502feeb2a..63c17bec34 100644 --- a/packages/constants/constants-ca/assets.ts +++ b/packages/constants/constants-ca/assets.ts @@ -39,5 +39,5 @@ export enum BalanceTab { export enum ContactsTab { ALL = 'All', - PORTKEY_CHAT = 'Portkey Chat', + Chats = 'Chats', } diff --git a/packages/web-extension-did/app/web/Popup/routes/index.tsx b/packages/web-extension-did/app/web/Popup/routes/index.tsx index 22cd34aa5f..c4a2011cda 100644 --- a/packages/web-extension-did/app/web/Popup/routes/index.tsx +++ b/packages/web-extension-did/app/web/Popup/routes/index.tsx @@ -33,7 +33,7 @@ import WalletName from 'pages/Wallet/WalletName'; import RecentDetail from 'pages/Send/components/RecentDetail'; import ConnectedSites from 'pages/WalletSecurity/ConnectedSites'; import SiteDetail from 'pages/WalletSecurity/ConnectedSites/SiteDetail'; -import FindMorePeople from 'pages/Contacts/FindMorePeople'; +import FindMore from 'pages/Contacts/FindMore'; export const PageRouter = () => useRoutes([ @@ -138,8 +138,8 @@ export const PageRouter = () => element: , }, { - path: '/setting/contacts/find-more-people', - element: , + path: '/setting/contacts/find-more', + element: , }, { path: '/setting/account-setting', diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index 94beab951d..05bd12b0d7 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -50,7 +50,7 @@ import SiteDetail from 'pages/WalletSecurity/ConnectedSites/SiteDetail'; import SendTransactions from 'pages/SendTransactions'; import GetSignature from 'pages/GetSignature'; import DappAutoTx from 'pages/DappAutoTx'; -import FindMorePeople from 'pages/Contacts/FindMorePeople'; +import FindMore from 'pages/Contacts/FindMore'; export const PageRouter = () => { const { isNotLessThan768 } = useCommonState(); @@ -219,8 +219,8 @@ export const PageRouter = () => { element: , }, { - path: '/setting/contacts/find-more-people', - element: , + path: '/setting/contacts/find-more', + element: , }, ], }, @@ -355,8 +355,8 @@ export const PageRouter = () => { element: , }, { - path: '/setting/contacts/find-more-people', - element: , + path: '/setting/contacts/find-more', + element: , }, { path: '/setting/account-setting', diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Popup/index.less b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less similarity index 84% rename from packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Popup/index.less rename to packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less index 96668f6cb4..548c56ae6c 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Popup/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less @@ -1,10 +1,10 @@ @import '../../../../assets/theme/color.less'; -.find-more-people-popup { +.find-more-popup { width: 100%; height: 100%; - .find-more-people-top { + .find-more-top { padding-top: 16px; padding-bottom: 10px; border: 1px solid #dee2e8; @@ -14,14 +14,14 @@ height: 62px; } - .find-more-people-id { + .find-more-id { color: #515a62; font-weight: normal; text-align: center; } } - .find-more-people-body { + .find-more-body { overflow-y: hidden; .no-data { diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx similarity index 72% rename from packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Popup/index.tsx rename to packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx index 69f790711d..61202fa241 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx @@ -1,11 +1,11 @@ import ContactsSearchInput from 'pages/Contacts/components/ContactsSearchInput'; -import { IFindMorePeopleProps } from '..'; +import { IFindMoreProps } from '..'; import ContactList from 'pages/Contacts/components/ContactList'; import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import './index.less'; -export default function FindMorePeoplePopup({ +export default function FindMorePopup({ headerTitle, myPortkeyId, contactList, @@ -14,19 +14,19 @@ export default function FindMorePeoplePopup({ handleSearch, clickItem, clickChat, -}: IFindMorePeopleProps) { +}: IFindMoreProps) { return ( -
    -
    +
    +
    } /> -
    My Portkey ID: {myPortkeyId}
    +
    My Portkey ID: {myPortkeyId}
    -
    +
    {contactList && contactList.length > 0 && ( )} diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Prompt/index.less b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less similarity index 76% rename from packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Prompt/index.less rename to packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less index 613abb02fb..e1268b04b2 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Prompt/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less @@ -1,30 +1,30 @@ @import '../../../../assets/theme/color.less'; -.find-more-people-prompt { +.find-more-prompt { border-left: 1px solid @border-2; flex: 1; - .find-more-people-top { + .find-more-top { margin-bottom: 24px; - .find-more-people-header { + .find-more-header { position: relative; .title-right-col { position: absolute; right: 0; } } - .find-more-people-search { + .find-more-search { width: auto; margin-bottom: 8px; margin-left: 12px; } - .find-more-people-id { + .find-more-id { color: #515a62; font-weight: normal; text-align: center; } } - .find-more-people-body { + .find-more-body { .no-data { color: #b6babf; font-size: 16px; diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx similarity index 58% rename from packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Prompt/index.tsx rename to packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx index 35da45833b..08a05d0a94 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx @@ -1,10 +1,10 @@ import ContactsSearchInput from 'pages/Contacts/components/ContactsSearchInput'; import SecondPageHeader from 'pages/components/SecondPageHeader'; -import { IFindMorePeopleProps } from '..'; +import { IFindMoreProps } from '..'; import ContactList from 'pages/Contacts/components/ContactList'; import './index.less'; -export default function FindMorePeoplePrompt({ +export default function FindMorePrompt({ headerTitle, myPortkeyId, contactList, @@ -13,24 +13,19 @@ export default function FindMorePeoplePrompt({ handleSearch, clickItem, clickChat, -}: IFindMorePeopleProps) { +}: IFindMoreProps) { return ( -
    -
    - +
    +
    + -
    My Portkey ID: {myPortkeyId}
    +
    My Portkey ID: {myPortkeyId}
    -
    +
    {contactList && contactList.length > 0 && ( )} diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx similarity index 64% rename from packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/index.tsx rename to packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx index 87941ed4b1..3d7129a9f7 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMorePeople/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx @@ -4,11 +4,11 @@ import CustomModal from 'pages/components/CustomModal'; import { useCommonState } from 'store/Provider/hooks'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; -import FindMorePeoplePrompt from './Prompt'; -import FindMorePeoplePopup from './Popup'; +import FindMorePrompt from './Prompt'; +import FindMorePopup from './Popup'; import { BaseHeaderProps } from 'types/UI'; -export interface IFindMorePeopleProps extends BaseHeaderProps { +export interface IFindMoreProps extends BaseHeaderProps { myPortkeyId: string; contactList: ContactItemType[]; isMainnet: boolean; @@ -18,10 +18,12 @@ export interface IFindMorePeopleProps extends BaseHeaderProps { clickChat: (e: any, item: ContactItemType) => void; } -export default function FindMorePeople() { +export default function FindMore() { const navigate = useNavigate(); const { isPrompt, isNotLessThan768 } = useCommonState(); const isMainnet = useIsMainnet(); + + const headerTitle = 'Find More'; const [portkeyId, setPortkeyId] = useState('mock portkey id'); const [contactList, setContactList] = useState([]); // mock data @@ -57,8 +59,8 @@ export default function FindMorePeople() { ); return isNotLessThan768 ? ( - handleChat(e, item)} /> ) : ( - ); } - -//
    -//
    -// -// -//
    My Portkey ID: {portkeyId}
    -//
    -//
    -// {contactList && contactList.length > 0 && ( -// { -// navigate('/setting/contacts/view', { state: item }); -// }} -// clickChat={(e, item) => handleChat(e, item)} -// /> -// )} -//
    -//
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.less b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.less index dd21a038be..97d721758d 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.less @@ -1,56 +1,14 @@ @import '../../../../assets/theme/color.less'; -@import '../../../../assets/theme/constants.less'; .view-contact-popup { + height: 100%; .view-contact-nav { padding-top: 16px; background-color: @bg-13; border-bottom: 1px solid @border-2; } - .name-section { - display: flex; - align-items: center; - margin-top: 16px; - height: 110px; - background-color: @bg-13; - color: @font-11; - width: 100%; - - .name-index { - margin-left: 16px; - margin-right: 16px; - width: 60px; - height: 60px; - border-radius: 50%; - background-color: @bg-13; - border: 1px solid @border-1; - font-size: 24px; - line-height: 28px; - flex-shrink: 0; - } - - .name { - font-size: 20px; - line-height: 24px; - flex: 1; - width: 0; - word-wrap: break-word; - .text-overflow(1) - } - } - .contact-body { - padding: 16px 24px; - - .@{app-prefix}-btn-primary { - margin-bottom: 20px; - height: 48px; - border-radius: 24px; - background-color: @bg-11; - border: 1px solid @border-3; - color: @font-9; - font-size: 14px; - line-height: 20px; - } + .view-contact-body { + height: calc(100% - 97px); } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx index 80e36af841..3e6efc0c52 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx @@ -1,9 +1,8 @@ -import { Button } from 'antd'; import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; -import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import './index.less'; import { IViewContactProps } from '..'; +import ViewContactBody from 'pages/Contacts/components/ViewContactBody'; export default function ViewContactPopup({ headerTitle, @@ -27,44 +26,17 @@ export default function ViewContactPopup({ rightElement={} />
    -
    -
    {data.index}
    -
    {data.name}
    -
    - -
    - Remark - {data.remark} -
    - -
    - Portkey ID -
    - {data.portkeyId} - handleCopy(data.portkeyId)} type="Copy" className="address-copy-icon" /> -
    -
    - -
    - {`ID (relation one)`} -
    - {data.relationOneId} - handleCopy(data.relationOneId)} type="Copy" className="address-copy-icon" /> -
    -
    - -
    - - - - -
    +
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less index e8208ac334..1aed992a29 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.less @@ -1,107 +1,7 @@ -@import '../../../../assets/theme/color.less'; -@import '../../../../assets/theme/constants.less'; - .view-contact-prompt { height: calc(100% - 73px); - .view-contact-content { - height: 100%; - } - .name-section { - align-items: center; - background-color: @bg-13; - padding-bottom: 0 !important; - - .name-index { - width: 60px; - height: 60px; - font-size: 24px; - line-height: 28px; - background-color: @bg-13; - border: 1px solid @border-1; - border-radius: 50%; - flex-shrink: 0; - } - .name { - margin-top: 8px; - font-size: 20px; - line-height: 24px; - word-wrap: break-word; - .text-overflow(1); - } - .remark { - font-size: 12px; - line-height: 16px; - margin-top: 4px; - color: #515a62; - margin-bottom: 24px; - } - .action { - width: 100%; - gap: 8px; - margin-bottom: 16px; - .action-item { - flex: 1; - height: 64px; - background-color: #fff; - color: #5b8ef4; - justify-content: center; - border-radius: 6px; - cursor: pointer; - .custom-svg { - width: 24px; - height: 24px; - margin-bottom: 4px; - } - } - .added-contact { - color: #b6babf; - cursor: default; - } - } - } - - .info-section { - display: flex; - flex-direction: column; - padding: 24px; - color: @font-11; - width: 100%; - .info-title { - font-size: 16px; - font-weight: bold; - color: #262626; - margin-bottom: 16px; - } - .info-content { - .info-desc { - font-size: 14px; - font-weight: normal; - color: #515a62; - word-break: break-all; - margin-right: 16px; - .text-overflow(1); - } - .address-copy-icon { - display: inline-flex; - } - } - } - - .section-border-bottom { - border-bottom: 1px solid #dee2e8; - } - - .footer { - padding: 16px 24px; - border-top: 1px solid #dee2e8; - - .edit-btn { - height: 48px; - color: #fff; - font-size: 16px; - line-height: 22px; - border-radius: 24px; - } + .view-contact-body { + height: 100%; } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx index 6344f147c6..a3927ab12a 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx @@ -1,9 +1,7 @@ -import { Button } from 'antd'; import './index.less'; -import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import SecondPageHeader from 'pages/components/SecondPageHeader'; import { IViewContactProps } from '..'; -import CustomSvg from 'components/CustomSvg'; +import ViewContactBody from 'pages/Contacts/components/ViewContactBody'; export default function ViewContactPrompt({ headerTitle, @@ -21,59 +19,17 @@ export default function ViewContactPrompt({ return (
    -
    -
    -
    -
    {data.index}
    -
    {data.name}
    -
    - {`Remark: `} - {data?.remark || 'No set'} -
    - -
    -
    - - {addedText} -
    -
    - - {addContactText} -
    -
    - - {chatText} -
    -
    -
    - -
    -
    Portkey ID
    -
    -
    {data.portkeyId}
    - handleCopy(data.portkeyId)} type="Copy" className="address-copy-icon" /> -
    -
    - -
    -
    {`ID (relation one)`}
    -
    -
    {data.relationOneId}
    - handleCopy(data.relationOneId)} type="Copy" className="address-copy-icon" /> -
    -
    - -
    -
    {`DID`}
    - -
    -
    -
    - -
    -
    +
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index 700828762b..ad9a938743 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -2,37 +2,23 @@ import ViewContactPrompt from './Prompt'; import ViewContactPopup from './Popup'; import { useLocation, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; -import { AddressItem } from '@portkey-wallet/types/types-ca/contact'; import { useCallback } from 'react'; import { BaseHeaderProps } from 'types/UI'; import { useCommonState } from 'store/Provider/hooks'; +import { IViewContactBodyProps } from '../components/ViewContactBody'; +import { useCopyToClipboard } from 'react-use'; +import { message } from 'antd'; +import CustomModal from 'pages/components/CustomModal'; -export interface IViewContactProps extends BaseHeaderProps { - data: { - name: string; - remark: string; - portkeyId: string; - relationOneId: string; - index: string; - addresses: AddressItem[]; - }; - editText: string; - chatText: string; - addedText: string; - addContactText: string; - handleEdit: () => void; - handleChat: () => void; - handleAdd: () => void; - handleCopy: (v: string) => void; -} +export type IViewContactProps = BaseHeaderProps & IViewContactBodyProps; export default function ViewContact() { - const { isNotLessThan768 } = useCommonState(); + const { isPrompt, isNotLessThan768 } = useCommonState(); const { state } = useLocation(); const navigate = useNavigate(); const { t } = useTranslation(); - const title = t('Contacts'); + const title = t('Details'); const editText = t('Edit'); const chatText = t('Chat'); const addedText = t('Added'); @@ -46,17 +32,30 @@ export default function ViewContact() { navigate('/setting/contacts/edit', { state: state }); }, [navigate, state]); - const handleChat = useCallback(() => { - navigate('/chat-list', { state: state }); - }, [navigate, state]); - const handleAdd = useCallback(() => { navigate('/setting/contacts/add', { state: state }); }, [navigate, state]); - const handleCopy = useCallback((v: string) => { - console.log('🌈 🌈 🌈 🌈 🌈 🌈 v', v); - }, []); + const handleChat = useCallback(() => { + if (isPrompt) { + CustomModal({ + content: ( + <>{`Please click on the Portkey browser extension in the top right corner to access the chat feature`} + ), + }); + } else { + navigate('/chat-list', { state: state }); + } + }, [isPrompt, navigate, state]); + + const [, setCopied] = useCopyToClipboard(); + const handleCopy = useCallback( + (v: string) => { + setCopied(v); + message.success(t('Copy Success')); + }, + [setCopied, t], + ); return isNotLessThan768 ? ( { - navigate('/setting/contacts/find-more-people'); + navigate('/setting/contacts/find-more'); }, [navigate]); const handleChat = useCallback((e: any) => { @@ -80,9 +80,9 @@ export default function ContactsBody({ const portkeyChatListUI = useMemo(() => { return ( <> -
    - - Find more people +
    + + Find more
    {portkeyChatCount === 0 ? ( isSearchPortkeyChat ? ( @@ -120,8 +120,8 @@ export default function ContactsBody({ children: allContactListUI, }, { - label: t('Portkey Chat'), - key: ContactsTab.PORTKEY_CHAT, + label: t('Chats'), + key: ContactsTab.Chats, children: portkeyChatListUI, }, ], diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less new file mode 100644 index 0000000000..252d63545d --- /dev/null +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less @@ -0,0 +1,109 @@ +@import '../../../../assets/theme/color.less'; +@import '../../../../assets/theme/constants.less'; + +.view-contact-body { + + .view-contact-body-main { + overflow-y: auto; + } + + .name-section { + align-items: center; + background-color: @bg-13; + padding-bottom: 0 !important; + + .name-index { + width: 60px; + height: 60px; + font-size: 24px; + line-height: 28px; + background-color: @bg-13; + border: 1px solid @border-1; + border-radius: 50%; + flex-shrink: 0; + } + .name { + margin-top: 8px; + font-size: 20px; + line-height: 24px; + word-wrap: break-word; + .text-overflow(1); + } + .remark { + font-size: 12px; + line-height: 16px; + margin-top: 4px; + color: #515a62; + margin-bottom: 24px; + } + .action { + width: 100%; + gap: 8px; + margin-bottom: 16px; + .action-item { + flex: 1; + height: 64px; + background-color: #fff; + color: #5b8ef4; + justify-content: center; + border-radius: 6px; + cursor: pointer; + .custom-svg { + width: 24px; + height: 24px; + margin-bottom: 4px; + } + } + .added-contact { + color: #b6babf; + cursor: default; + } + } + } + + .info-section { + display: flex; + flex-direction: column; + padding: 24px; + color: @font-11; + width: 100%; + + .info-title { + font-size: 16px; + font-weight: bold; + color: #262626; + margin-bottom: 16px; + } + .info-content { + .info-desc { + font-size: 14px; + font-weight: normal; + color: #515a62; + word-break: break-all; + margin-right: 16px; + .text-overflow(1); + } + .id-copy-icon { + display: inline-flex; + cursor: pointer; + } + } + } + + .section-border-bottom { + border-bottom: 1px solid #dee2e8; + } + + .footer { + padding: 16px 24px; + border-top: 1px solid #dee2e8; + + .edit-btn { + height: 48px; + color: #fff; + font-size: 16px; + line-height: 22px; + border-radius: 24px; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx new file mode 100644 index 0000000000..acd94eaa86 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx @@ -0,0 +1,92 @@ +import { Button } from 'antd'; +import './index.less'; +import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; +import CustomSvg from 'components/CustomSvg'; +import { AddressItem } from '@portkey-wallet/types/types-ca/contact'; + +export interface IViewContactBodyProps { + data: { + name: string; + remark: string; + portkeyId: string; + relationOneId: string; + index: string; + addresses: AddressItem[]; + }; + editText: string; + chatText: string; + addedText: string; + addContactText: string; + handleEdit: () => void; + handleChat: () => void; + handleAdd: () => void; + handleCopy: (v: string) => void; +} + +export default function ViewContactBody({ + data, + editText, + chatText, + addedText, + addContactText, + handleEdit, + handleChat, + handleAdd, + handleCopy, +}: IViewContactBodyProps) { + return ( +
    +
    +
    +
    {data.index}
    +
    {data.name}
    +
    + {`Remark: `} + {data?.remark || 'No set'} +
    + +
    +
    + + {addedText} +
    +
    + + {addContactText} +
    +
    + + {chatText} +
    +
    +
    + +
    +
    Portkey ID
    +
    +
    {data.portkeyId}
    + handleCopy(data.portkeyId)} type="Copy" className="id-copy-icon" /> +
    +
    + +
    +
    {`ID (relation one)`}
    +
    +
    {data.relationOneId}
    + handleCopy(data.relationOneId)} type="Copy" className="id-copy-icon" /> +
    +
    + +
    +
    {`DID`}
    + +
    +
    +
    + +
    +
    + ); +} From 3dcfa22ca2ef0156531479d05665408aa2345fe7 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 15 Aug 2023 15:31:00 +0800 Subject: [PATCH 516/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20my=20did?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/hooks/useProfile.ts | 60 +++++++++ .../app/web/pages/Contacts/FindMore/index.tsx | 1 + .../Contacts/ViewContact/Popup/index.tsx | 4 +- .../Contacts/ViewContact/Prompt/index.tsx | 4 +- .../web/pages/Contacts/ViewContact/index.tsx | 57 +++------ .../components/ViewContactBody/index.less | 12 +- .../components/ViewContactBody/index.tsx | 116 ++++++++++-------- .../pages/Wallet/WalletName/Popup/index.less | 6 + .../pages/Wallet/WalletName/Popup/index.tsx | 32 ++++- .../pages/Wallet/WalletName/Prompt/index.less | 4 + .../pages/Wallet/WalletName/Prompt/index.tsx | 32 ++++- .../app/web/pages/Wallet/WalletName/index.tsx | 43 ++++++- .../app/web/pages/Wallet/index.tsx | 13 +- .../app/web/types/Profile.ts | 29 +++++ 14 files changed, 298 insertions(+), 115 deletions(-) create mode 100644 packages/web-extension-did/app/web/hooks/useProfile.ts create mode 100644 packages/web-extension-did/app/web/types/Profile.ts diff --git a/packages/web-extension-did/app/web/hooks/useProfile.ts b/packages/web-extension-did/app/web/hooks/useProfile.ts new file mode 100644 index 0000000000..c6fa7d4418 --- /dev/null +++ b/packages/web-extension-did/app/web/hooks/useProfile.ts @@ -0,0 +1,60 @@ +import { message } from 'antd'; +import CustomModal from 'pages/components/CustomModal'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router'; +import { useCopyToClipboard } from 'react-use'; +import { useCommonState } from 'store/Provider/hooks'; + +export const useProfileEdit = () => { + const navigate = useNavigate(); + + return useCallback( + (state: any) => { + navigate('/setting/contacts/edit', { state }); + }, + [navigate], + ); +}; + +export const useProfileAddContact = () => { + const navigate = useNavigate(); + + return useCallback( + (state: any) => { + navigate('/setting/contacts/add', { state }); + }, + [navigate], + ); +}; + +export const useProfileChat = () => { + const navigate = useNavigate(); + const { isPrompt } = useCommonState(); + + return useCallback( + (state: any) => { + if (isPrompt) { + CustomModal({ + content: `Please click on the Portkey browser extension in the top right corner to access the chat feature`, + }); + } else { + navigate('/chat-list', { state }); + } + }, + [isPrompt, navigate], + ); +}; + +export const useProfileCopy = () => { + const [, setCopied] = useCopyToClipboard(); + const { t } = useTranslation(); + + return useCallback( + (val: string) => { + setCopied(val); + message.success(t('Copy Success')); + }, + [setCopied, t], + ); +}; diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx index 3d7129a9f7..b5bf23bd17 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx @@ -24,6 +24,7 @@ export default function FindMore() { const isMainnet = useIsMainnet(); const headerTitle = 'Find More'; + const cantAddYourselfText = 'Unable to add yourself as a contact'; const [portkeyId, setPortkeyId] = useState('mock portkey id'); const [contactList, setContactList] = useState([]); // mock data diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx index 3e6efc0c52..932fbd16d9 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Popup/index.tsx @@ -1,8 +1,8 @@ import BackHeader from 'components/BackHeader'; import CustomSvg from 'components/CustomSvg'; import './index.less'; -import { IViewContactProps } from '..'; import ViewContactBody from 'pages/Contacts/components/ViewContactBody'; +import { IProfileDetailProps } from 'types/Profile'; export default function ViewContactPopup({ headerTitle, @@ -16,7 +16,7 @@ export default function ViewContactPopup({ handleChat, handleAdd, handleCopy, -}: IViewContactProps) { +}: IProfileDetailProps) { return (
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx index a3927ab12a..28985e5a51 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/Prompt/index.tsx @@ -1,7 +1,7 @@ import './index.less'; import SecondPageHeader from 'pages/components/SecondPageHeader'; -import { IViewContactProps } from '..'; import ViewContactBody from 'pages/Contacts/components/ViewContactBody'; +import { IProfileDetailProps } from 'types/Profile'; export default function ViewContactPrompt({ headerTitle, @@ -15,7 +15,7 @@ export default function ViewContactPrompt({ handleChat, handleAdd, handleCopy, -}: IViewContactProps) { +}: IProfileDetailProps) { return (
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index ad9a938743..c0324434bf 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -3,17 +3,11 @@ import ViewContactPopup from './Popup'; import { useLocation, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import { useCallback } from 'react'; -import { BaseHeaderProps } from 'types/UI'; import { useCommonState } from 'store/Provider/hooks'; -import { IViewContactBodyProps } from '../components/ViewContactBody'; -import { useCopyToClipboard } from 'react-use'; -import { message } from 'antd'; -import CustomModal from 'pages/components/CustomModal'; - -export type IViewContactProps = BaseHeaderProps & IViewContactBodyProps; +import { useProfileAddContact, useProfileChat, useProfileCopy, useProfileEdit } from 'hooks/useProfile'; export default function ViewContact() { - const { isPrompt, isNotLessThan768 } = useCommonState(); + const { isNotLessThan768 } = useCommonState(); const { state } = useLocation(); const navigate = useNavigate(); const { t } = useTranslation(); @@ -28,35 +22,12 @@ export default function ViewContact() { navigate('/setting/contacts'); }, [navigate]); - const handleEdit = useCallback(() => { - navigate('/setting/contacts/edit', { state: state }); - }, [navigate, state]); - - const handleAdd = useCallback(() => { - navigate('/setting/contacts/add', { state: state }); - }, [navigate, state]); - - const handleChat = useCallback(() => { - if (isPrompt) { - CustomModal({ - content: ( - <>{`Please click on the Portkey browser extension in the top right corner to access the chat feature`} - ), - }); - } else { - navigate('/chat-list', { state: state }); - } - }, [isPrompt, navigate, state]); - - const [, setCopied] = useCopyToClipboard(); - const handleCopy = useCallback( - (v: string) => { - setCopied(v); - message.success(t('Copy Success')); - }, - [setCopied, t], - ); + const handleEdit = useProfileEdit(); + const handleAdd = useProfileAddContact(); + const handleChat = useProfileChat(); + const handleCopy = useProfileCopy(); + // TODO btn show logic return isNotLessThan768 ? ( handleEdit(state)} + handleAdd={() => handleAdd(state)} + handleChat={() => handleChat(state)} + handleCopy={useProfileCopy} /> ) : ( handleEdit(state)} + handleAdd={() => handleAdd(state)} + handleChat={() => handleChat(state)} handleCopy={handleCopy} /> ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less index 252d63545d..d526928470 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less @@ -2,7 +2,6 @@ @import '../../../../assets/theme/constants.less'; .view-contact-body { - .view-contact-body-main { overflow-y: auto; } @@ -34,7 +33,6 @@ line-height: 16px; margin-top: 4px; color: #515a62; - margin-bottom: 24px; } .action { width: 100%; @@ -94,6 +92,16 @@ border-bottom: 1px solid #dee2e8; } + .empty-placeholder-8 { + height: 8px; + width: 100%; + } + + .empty-placeholder-24 { + height: 24px; + width: 100%; + } + .footer { padding: 16px 24px; border-top: 1px solid #dee2e8; diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx index acd94eaa86..435e6dc1a5 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx @@ -2,86 +2,96 @@ import { Button } from 'antd'; import './index.less'; import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import CustomSvg from 'components/CustomSvg'; -import { AddressItem } from '@portkey-wallet/types/types-ca/contact'; - -export interface IViewContactBodyProps { - data: { - name: string; - remark: string; - portkeyId: string; - relationOneId: string; - index: string; - addresses: AddressItem[]; - }; - editText: string; - chatText: string; - addedText: string; - addContactText: string; - handleEdit: () => void; - handleChat: () => void; - handleAdd: () => void; - handleCopy: (v: string) => void; -} +import { IProfileDetailBodyProps } from 'types/Profile'; export default function ViewContactBody({ data, - editText, - chatText, - addedText, - addContactText, + editText = 'Edit', + chatText = 'Chat', + addedText = 'Added', + addContactText = 'Add Contact', + isShowRemark = true, + isShowAddContactBtn = true, + isShowAddedBtn = true, + isShowChatBtn = true, handleEdit, handleChat, handleAdd, handleCopy, -}: IViewContactBodyProps) { +}: IProfileDetailBodyProps) { return (
    {data.index}
    {data.name}
    -
    - {`Remark: `} - {data?.remark || 'No set'} -
    -
    -
    - - {addedText} -
    -
    - - {addContactText} -
    -
    - - {chatText} + {/* Section - Remark */} + {isShowRemark && ( +
    + {`Remark: `} + {data?.remark || 'No set'}
    + )} + {!isShowRemark && !isShowAddContactBtn && !isShowAddedBtn && !isShowChatBtn && ( +
    + )} + {isShowRemark && (isShowAddContactBtn || isShowAddedBtn || isShowChatBtn) && ( +
    + )} + + {/* Section - Action: Added | Add Contact | Chat */} + +
    + {isShowAddedBtn && ( +
    + + {addedText} +
    + )} + {isShowAddContactBtn && ( +
    + + {addContactText} +
    + )} + {isShowChatBtn && ( +
    + + {chatText} +
    + )}
    -
    -
    Portkey ID
    -
    -
    {data.portkeyId}
    - handleCopy(data.portkeyId)} type="Copy" className="id-copy-icon" /> + {/* Section - ID */} + {data?.portkeyId && ( +
    +
    Portkey ID
    +
    +
    {data.portkeyId}
    + handleCopy(data.portkeyId)} type="Copy" className="id-copy-icon" /> +
    -
    + )} -
    -
    {`ID (relation one)`}
    -
    -
    {data.relationOneId}
    - handleCopy(data.relationOneId)} type="Copy" className="id-copy-icon" /> + {!data?.portkeyId && data?.relationOneId && ( +
    +
    {`ID (relation one)`}
    +
    +
    {data.relationOneId}
    + handleCopy(data.relationOneId)} type="Copy" className="id-copy-icon" /> +
    -
    + )} + {/* Section - Address */}
    {`DID`}
    +
    + +
    +
    + + ); +} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less index 5dc845a662..94326782d4 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less @@ -1,9 +1,6 @@ @import '../../../../assets/theme/color.less'; .edit-contact-form { - height: 504px; - justify-content: space-between; - .form-content { padding: 12px 24px 8px; @@ -19,88 +16,12 @@ color: @font-12; line-height: 20px; } - .address-item { - .address-item-title { - color: @font-11; - line-height: 20px; - .address-item-delete { - cursor: pointer; - } - } - .address-item-body { - margin-top: 8px; - .select-svg { - width: 24px; - height: 24px; - } - .@{app-prefix}-input-affix-wrapper { - height: 48px; - border-radius: 6px; - border: 1px solid @border-1; - - .@{app-prefix}-input-prefix { - margin-right: 12px; - } - } - .@{app-prefix}-form-item { - width: 100%; - height: 48px; - margin-top: 8px; - - .@{app-prefix}-input-group-wrapper-status-error .@{app-prefix}-input-group-addon { - color: @font-13; - } - .@{app-prefix}-input-group-addon { - border-radius: 6px; - border: 1px solid @border-1; - &:first-child { - border-right: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - &:last-child { - border-left: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } - } - } - } - .addresses-add-btn { - margin-top: 8px; - margin-bottom: 24px; - color: @font-9; - font-size: 14px; - line-height: 20px; - cursor: pointer; - & > div { - display: flex; - margin-right: 8px; - } - .plus-svg { - width: 20px; - height: 20px; - } - } } .form-btn { width: 100%; padding: 16px; border-top: 1px solid @border-2; - .form-btn-add { - .@{app-prefix}-form-item { - margin-bottom: 0; - } - .add-btn { - color: @font-5; - font-size: 16px; - line-height: 22px; - height: 48px; - border-radius: 22px; - background-color: @bg-7; - } - } + .form-btn-edit { gap: 16px; .@{app-prefix}-btn-dangerous, diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx index fd6f152c6c..a4915ba34b 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx @@ -1,46 +1,42 @@ +import { Button, Form, Input, message, FormProps } from 'antd'; import { useState, useCallback } from 'react'; -import { useNavigate } from 'react-router'; -import { Button, Form, Input, FormProps, message } from 'antd'; -import { useTranslation } from 'react-i18next'; -import CustomSvg from 'components/CustomSvg'; -import { CustomAddressItem, ValidData } from 'pages/Contacts/EditContact'; -import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import DeleteContact from 'pages/Contacts/DeleteContact'; import { useDeleteContact } from '@portkey-wallet/hooks/hooks-ca/contact'; +import { useNavigate } from 'react-router'; +import { useTranslation } from 'react-i18next'; +import IdAndAddress from '../IdAndAddress'; +import { ValidData } from 'pages/Contacts/EditContact'; import './index.less'; const { Item: FormItem } = Form; -export interface IEditContactFormProps extends FormProps { - isEdit: boolean; - isDisable: boolean; - validName: ValidData; - validRemark: ValidData; +interface IEditContactFormProps extends FormProps { state: any; - addressArr: CustomAddressItem[]; + validName: ValidData; + validRemark?: ValidData; + isNameDisable?: boolean; + isShowRemark?: boolean; + canSave?: boolean; handleInputValueChange: (v: string) => void; handleInputRemarkChange: (v: string) => void; - handleSelectNetwork: (i: number) => void; - handleAddressChange: (i: number, value: string) => void; + handleCopy: (val: string) => void; } export default function EditContactForm({ form, - isEdit, - isDisable, state, - addressArr, validName, validRemark, - onFinish, + isNameDisable = false, + isShowRemark = true, + canSave = false, handleInputValueChange, handleInputRemarkChange, - handleSelectNetwork, - handleAddressChange, + handleCopy, + onFinish, }: IEditContactFormProps) { const { t } = useTranslation(); const navigate = useNavigate(); - const symbolImages = useSymbolImages(); const deleteContactApi = useDeleteContact(); const [delOpen, setDelOpen] = useState(false); @@ -51,111 +47,62 @@ export default function EditContactForm({ }, [deleteContactApi, navigate, state]); return ( - <> -
    -
    - - handleInputValueChange(e.target.value)} - maxLength={16} - /> - + +
    + + handleInputValueChange(e.target.value)} + maxLength={16} + disabled={isNameDisable} + /> + + + {isShowRemark && ( + validateStatus={validRemark?.validateStatus} + help={validRemark?.errorMsg}> handleInputRemarkChange(e.target.value)} maxLength={16} /> + )} +
    - - {(fields) => ( -
    - {fields.map(({ key, name, ...restField }, i) => ( -
    -
    {`Address${i + 1}`}
    - - - } - suffix={ - { - handleSelectNetwork(i); - }} - /> - } - onClick={() => { - handleSelectNetwork(i); - }} - /> - - - handleAddressChange(i, e.target.value)} - placeholder={t("Enter contact's address")} - addonBefore="ELF" - addonAfter={addressArr[i]?.chainId} - /> - - -
    - ))} -
    - )} -
    -
    -
    - {!isEdit && ( -
    - - - -
    - )} - {isEdit && ( -
    - - -
    - )} + + +
    +
    + +
    - - { - setDelOpen(false); - }} - onConfirm={handleDelConfirm} - /> - + { + setDelOpen(false); + }} + onConfirm={handleDelConfirm} + /> +
    + ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less new file mode 100644 index 0000000000..094d10dadf --- /dev/null +++ b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less @@ -0,0 +1,37 @@ +@import '../../../../assets/theme/color.less'; +@import '../../../../assets/theme/constants.less'; + +.id-and-address { + .info-section { + display: flex; + flex-direction: column; + padding: 24px; + color: @font-11; + width: 100%; + + .info-title { + font-size: 16px; + font-weight: bold; + color: #262626; + margin-bottom: 16px; + } + .info-content { + .info-desc { + font-size: 14px; + font-weight: normal; + color: #515a62; + word-break: break-all; + margin-right: 16px; + .text-overflow(1); + } + .id-copy-icon { + display: inline-flex; + cursor: pointer; + } + } + } + + .section-border-bottom { + border-bottom: 1px solid #dee2e8; + } +} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx new file mode 100644 index 0000000000..7d3313b4df --- /dev/null +++ b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx @@ -0,0 +1,44 @@ +import './index.less'; +import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; +import CustomSvg from 'components/CustomSvg'; +import { AddressItem } from '@portkey-wallet/types/types-ca/contact'; + +interface IIdAndAddressProps { + portkeyId: string; + relationOneId: string; + addresses: AddressItem[]; + handleCopy: (val: string) => void; +} + +export default function IdAndAddress({ portkeyId, relationOneId, addresses, handleCopy }: IIdAndAddressProps) { + return ( +
    + {/* Section - ID */} + {portkeyId && ( +
    +
    Portkey ID
    +
    +
    {portkeyId}
    + handleCopy(portkeyId)} type="Copy" className="id-copy-icon" /> +
    +
    + )} + + {!portkeyId && relationOneId && ( +
    +
    {`ID (relation one)`}
    +
    +
    {relationOneId}
    + handleCopy(relationOneId)} type="Copy" className="id-copy-icon" /> +
    +
    + )} + + {/* Section - Address */} +
    +
    {`DID`}
    + +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx index 2354f4beb8..db444aa805 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx @@ -18,7 +18,7 @@ export default function NoContacts({ initData }: { initData: Partial { - navigate('/setting/contacts/add', { state: initData }); + navigate('/setting/contacts/add/2', { state: initData }); }}> {t('Add New Contact')} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less index d526928470..702a61c8df 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.less @@ -65,31 +65,6 @@ padding: 24px; color: @font-11; width: 100%; - - .info-title { - font-size: 16px; - font-weight: bold; - color: #262626; - margin-bottom: 16px; - } - .info-content { - .info-desc { - font-size: 14px; - font-weight: normal; - color: #515a62; - word-break: break-all; - margin-right: 16px; - .text-overflow(1); - } - .id-copy-icon { - display: inline-flex; - cursor: pointer; - } - } - } - - .section-border-bottom { - border-bottom: 1px solid #dee2e8; } .empty-placeholder-8 { diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx index 435e6dc1a5..0768237624 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx @@ -3,6 +3,7 @@ import './index.less'; import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import CustomSvg from 'components/CustomSvg'; import { IProfileDetailBodyProps } from 'types/Profile'; +import IdAndAddress from '../IdAndAddress'; export default function ViewContactBody({ data, @@ -64,32 +65,12 @@ export default function ViewContactBody({
    - {/* Section - ID */} - {data?.portkeyId && ( -
    -
    Portkey ID
    -
    -
    {data.portkeyId}
    - handleCopy(data.portkeyId)} type="Copy" className="id-copy-icon" /> -
    -
    - )} - - {!data?.portkeyId && data?.relationOneId && ( -
    -
    {`ID (relation one)`}
    -
    -
    {data.relationOneId}
    - handleCopy(data.relationOneId)} type="Copy" className="id-copy-icon" /> -
    -
    - )} - - {/* Section - Address */} -
    -
    {`DID`}
    - -
    +
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/index.tsx index 8ee9a348b4..150214ccae 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/index.tsx @@ -102,7 +102,7 @@ export default function Contacts() { }, [navigate]); const handleAdd = useCallback(() => { - navigate('/setting/contacts/add', { state: initContactItem }); + navigate('/setting/contacts/add/2', { state: initContactItem }); }, [navigate]); return isNotLessThan768 ? ( diff --git a/packages/web-extension-did/app/web/pages/Send/components/RecentDetail/index.tsx b/packages/web-extension-did/app/web/pages/Send/components/RecentDetail/index.tsx index e1e9857280..9cd47264b2 100644 --- a/packages/web-extension-did/app/web/pages/Send/components/RecentDetail/index.tsx +++ b/packages/web-extension-did/app/web/pages/Send/components/RecentDetail/index.tsx @@ -54,7 +54,7 @@ export default function RecentDetail() { name: '', addresses: [{ chainId: targetChainId || 'AELF', address: targetAddress || '' }], }; - nav('/setting/contacts/add', { state: initContactItem }); + nav('/setting/contacts/add/2', { state: initContactItem }); }, [targetChainId, targetAddress, nav]); const viewOnExplorer = useCallback(() => { diff --git a/packages/web-extension-did/app/web/pages/Wallet/WalletName/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Wallet/WalletName/Popup/index.tsx index 6584f89d35..e0f7e2a361 100644 --- a/packages/web-extension-did/app/web/pages/Wallet/WalletName/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Wallet/WalletName/Popup/index.tsx @@ -14,8 +14,6 @@ export default function WalletNamePopup({ isShowAddedBtn = false, isShowChatBtn = false, handleEdit, - handleChat, - handleAdd, handleCopy, }: IProfileDetailProps) { return ( @@ -35,8 +33,6 @@ export default function WalletNamePopup({ isShowAddedBtn={isShowAddedBtn} isShowChatBtn={isShowChatBtn} handleEdit={handleEdit} - handleChat={handleChat} - handleAdd={handleAdd} handleCopy={handleCopy} />
    diff --git a/packages/web-extension-did/app/web/pages/Wallet/WalletName/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Wallet/WalletName/Prompt/index.tsx index 87bb113c89..8d9a3ebc83 100644 --- a/packages/web-extension-did/app/web/pages/Wallet/WalletName/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Wallet/WalletName/Prompt/index.tsx @@ -13,8 +13,6 @@ export default function WalletNamePrompt({ isShowAddedBtn = false, isShowChatBtn = false, handleEdit, - handleChat, - handleAdd, handleCopy, }: IProfileDetailProps) { return ( @@ -28,8 +26,6 @@ export default function WalletNamePrompt({ isShowAddedBtn={isShowAddedBtn} isShowChatBtn={isShowChatBtn} handleEdit={handleEdit} - handleChat={handleChat} - handleAdd={handleAdd} handleCopy={handleCopy} />
    diff --git a/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx b/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx index 4747ad9186..21508940ee 100644 --- a/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx +++ b/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx @@ -6,7 +6,7 @@ import { useNavigate } from 'react-router'; import { useCommonState } from 'store/Provider/hooks'; import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import { IProfileDetailDataProps } from 'types/Profile'; -import { useProfileEdit, useProfileAddContact, useProfileChat, useProfileCopy } from 'hooks/useProfile'; +import { useProfileEdit, useProfileCopy } from 'hooks/useProfile'; import { useTranslation } from 'react-i18next'; export default function WalletName() { @@ -28,8 +28,6 @@ export default function WalletName() { const headerTitle = isMainnet ? walletName : t('My DID'); const handleEdit = useProfileEdit(); - const handleAdd = useProfileAddContact(); - const handleChat = useProfileChat(); const handleCopy = useProfileCopy(); const goBack = useCallback(() => navigate('/setting/wallet'), [navigate]); @@ -39,9 +37,7 @@ export default function WalletName() { data={state} editText={editText} goBack={goBack} - handleEdit={() => handleEdit(state)} - handleAdd={() => handleAdd(state)} - handleChat={() => handleChat(state)} + handleEdit={() => handleEdit('1', state)} handleCopy={handleCopy} /> ) : ( @@ -50,9 +46,7 @@ export default function WalletName() { data={state} editText={editText} goBack={goBack} - handleEdit={() => handleEdit(state)} - handleAdd={() => handleAdd(state)} - handleChat={() => handleChat(state)} + handleEdit={() => handleEdit('1', state)} handleCopy={handleCopy} /> ); diff --git a/packages/web-extension-did/app/web/types/Profile.ts b/packages/web-extension-did/app/web/types/Profile.ts index 70e37dd659..6144a5cbf5 100644 --- a/packages/web-extension-did/app/web/types/Profile.ts +++ b/packages/web-extension-did/app/web/types/Profile.ts @@ -1,6 +1,8 @@ import { AddressItem } from '@portkey-wallet/types/types-ca/contact'; import { BaseHeaderProps } from './UI'; +export type ChatType = '1' | '2'; // '1' can chat, '2' cont chat or not sure + export interface IProfileDetailDataProps { name: string; remark?: string; @@ -21,9 +23,9 @@ export interface IProfileDetailBodyProps { isShowAddedBtn?: boolean; isShowChatBtn?: boolean; handleEdit: () => void; - handleChat: () => void; - handleAdd: () => void; handleCopy: (v: string) => void; + handleChat?: () => void; + handleAdd?: () => void; } export type IProfileDetailProps = BaseHeaderProps & IProfileDetailBodyProps; From f88a6980f5325710a9eabf12145e8f85bb8a4c9d Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Wed, 16 Aug 2023 11:48:18 +0800 Subject: [PATCH 518/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20caht=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/assets/image/svgs.js | 2 +- .../assets/image/svgs/chat-delete-emoji.svg | 5 + .../js/assets/image/svgs/chat-mute.svg | 6 +- .../mobile-app-did/js/assets/theme/index.ts | 1 + .../js/components/ContactItem/index.tsx | 30 ++++- .../ContactList/ContactFlashList/style.ts | 1 - .../js/pages/Chat/ChatDetails/index.tsx | 8 +- .../js/pages/Chat/ChatHome/index.tsx | 6 +- .../js/pages/Chat/FindMorePeople/index.tsx | 19 ++- .../components/AddContactButton/index.tsx | 55 ++++++++ .../js/pages/Chat/components/Chats/index.tsx | 23 ++-- .../Chat/components/CustomBubble/index.tsx | 53 ++++---- .../Chat/components/FindMoreButton/index.tsx | 3 +- .../InputToolbar/Emoticons/index.tsx | 21 +++- .../components/InputToolbar/ToolBar/index.tsx | 1 - .../components/Message/MessageImage/index.tsx | 86 +++++++++++++ .../components/Message/MessageText/index.tsx | 53 +++++--- .../components/MessageContainer/index.tsx | 72 +++-------- .../js/pages/Chat/components/messages.js | 3 +- .../components/ContactAddress/index.tsx | 4 +- .../pages/My/Contacts/ContactEdit/index.tsx | 38 +++--- .../pages/My/Contacts/ContactsHome/index.tsx | 29 ++++- .../pages/My/Contacts/ContactsHome/style.ts | 13 -- .../SearchContactListSection/index.tsx | 6 +- .../js/pages/My/WalletHome/AboutUs/index.tsx | 4 +- .../My/WalletHome/EditWalletName/index.tsx | 118 ++++++++++++++++++ 26 files changed, 482 insertions(+), 178 deletions(-) create mode 100644 packages/mobile-app-did/js/assets/image/svgs/chat-delete-emoji.svg create mode 100644 packages/mobile-app-did/js/pages/Chat/components/AddContactButton/index.tsx create mode 100644 packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/style.ts create mode 100644 packages/mobile-app-did/js/pages/My/WalletHome/EditWalletName/index.tsx diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index c76af036d4..abd57f1a14 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","chat-add-contact":"\n\n\n","chat-add":"\n\n\n","chat-delete":"\n\n\n","chat-emoji":"\n\n\n\n\n\n","chat-file":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-find-more":"\n\n\n\n\n\n","chat-keyboard":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-mute":"\n\n\n\n\n","chat-new-chat":"\n\n\n","chat-pin":"\n\n\n","chat-profile":"\n\n\n\n\n","chat-reshutter":"\n\n\n\n\n","chat-robot":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","chat-send":"\n\n\n","chat-shutter":"\n\n\n\n","chat-tab":"\n\n\n","chat-unmute":"\n\n\n\n\n","chat-unpin":"\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","chat-add-contact":"\n\n\n","chat-add":"\n\n\n","chat-delete-emoji":"\n\n\n\n\n","chat-delete":"\n\n\n","chat-emoji":"\n\n\n\n\n\n","chat-file":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-find-more":"\n\n\n\n\n\n","chat-keyboard":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-mute":"\n\n\n","chat-new-chat":"\n\n\n","chat-pin":"\n\n\n","chat-profile":"\n\n\n\n\n","chat-reshutter":"\n\n\n\n\n","chat-robot":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","chat-send":"\n\n\n","chat-shutter":"\n\n\n\n","chat-tab":"\n\n\n","chat-unmute":"\n\n\n\n\n","chat-unpin":"\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-delete-emoji.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-delete-emoji.svg new file mode 100644 index 0000000000..e95d07f4e7 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-delete-emoji.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg index 4dae520b73..153541ae66 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg @@ -1,5 +1,3 @@ - - - - + + diff --git a/packages/mobile-app-did/js/assets/theme/index.ts b/packages/mobile-app-did/js/assets/theme/index.ts index c5ac210941..b50ddd2e2b 100644 --- a/packages/mobile-app-did/js/assets/theme/index.ts +++ b/packages/mobile-app-did/js/assets/theme/index.ts @@ -26,6 +26,7 @@ export const defaultColors = { bg17: '#EA4F45', bg18: '#F0F1F4', bg19: '#000000', + bg20: '#515A62', font1: '#464B53', font2: 'white', diff --git a/packages/mobile-app-did/js/components/ContactItem/index.tsx b/packages/mobile-app-did/js/components/ContactItem/index.tsx index d500910ba6..0bca4a54ba 100644 --- a/packages/mobile-app-did/js/components/ContactItem/index.tsx +++ b/packages/mobile-app-did/js/components/ContactItem/index.tsx @@ -2,29 +2,39 @@ import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { BGStyles, BorderStyles, FontStyles } from 'assets/theme/styles'; -import { TextM, TextXXL } from 'components/CommonText'; +import { TextM, TextS, TextXXL } from 'components/CommonText'; +import Touchable from 'components/Touchable'; import React, { memo } from 'react'; import { View, TouchableOpacity, StyleSheet } from 'react-native'; // import { formatStr2EllipsisStr } from 'utils'; import { pTd } from 'utils/unit'; export interface ItemType { + isShowChat?: boolean; contact: ContactItemType; onPress?: (item: any) => void; + onPressChat?: (item: any) => void; } const ContactItem: React.FC = props => { - const { contact, onPress } = props; + const { isShowChat = false, contact, onPress, onPressChat } = props; return ( onPress?.(contact)}> - {contact.index} + {contact.name[0].toUpperCase()} - {contact.name} + + {contact.name} + + {isShowChat && ( + onPressChat?.(contact)}> + Chat + + )} ); @@ -34,6 +44,7 @@ export default memo(ContactItem); export const styles = StyleSheet.create({ itemWrap: { + backgroundColor: defaultColors.bg1, height: pTd(72), width: '100%', display: 'flex', @@ -44,8 +55,8 @@ export const styles = StyleSheet.create({ ...GStyles.paddingArg(0, 20), }, itemAvatar: { - width: pTd(40), - height: pTd(40), + width: pTd(36), + height: pTd(36), borderRadius: pTd(20), marginRight: pTd(12), display: 'flex', @@ -56,4 +67,11 @@ export const styles = StyleSheet.create({ itemNameWrap: { flex: 1, }, + chatButton: { + backgroundColor: defaultColors.bg5, + borderRadius: pTd(6), + overflow: 'hidden', + paddingHorizontal: pTd(12), + paddingVertical: pTd(4), + }, }); diff --git a/packages/mobile-app-did/js/components/ContactList/ContactFlashList/style.ts b/packages/mobile-app-did/js/components/ContactList/ContactFlashList/style.ts index 038ca928a6..1d79e098d7 100644 --- a/packages/mobile-app-did/js/components/ContactList/ContactFlashList/style.ts +++ b/packages/mobile-app-did/js/components/ContactList/ContactFlashList/style.ts @@ -4,7 +4,6 @@ import { pTd } from 'utils/unit'; export const styles = StyleSheet.create({ sectionListWrap: { flex: 1, - paddingRight: pTd(20), overflow: 'hidden', }, sectionListWrapFull: { diff --git a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx index 70d97a262e..755f6c62f1 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx @@ -4,7 +4,7 @@ import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; -import { TextM, TextL } from 'components/CommonText'; +import { TextL } from 'components/CommonText'; import Chats from '../components/Chats'; import Svg from 'components/Svg'; @@ -14,6 +14,7 @@ import navigationService from 'utils/navigationService'; import { ChatOperationsEnum } from '@portkey-wallet/constants/constants-ca/chat'; import CommonAvatar from 'components/CommonAvatar'; import { FontStyles } from 'assets/theme/styles'; +import AddContactButton from '../components/AddContactButton'; const ChatDetails = () => { const onPressMore = useCallback((event: { nativeEvent: { pageX: any; pageY: any } }) => { @@ -48,7 +49,8 @@ const ChatDetails = () => { - Name + Name + } rightDom={ @@ -56,6 +58,7 @@ const ChatDetails = () => { }> + ); @@ -65,6 +68,7 @@ export default ChatDetails; const styles = StyleSheet.create({ container: { + position: 'relative', backgroundColor: defaultColors.bg4, flex: 1, ...GStyles.paddingArg(0), diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index a45521ca70..99b5e1184e 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -44,7 +44,11 @@ export default function DiscoverHome() { iconName: 'chat-new-chat', onPress: () => navigationService.navigate('NewChatHome'), }, - { title: 'Add Contact', iconName: 'chat-add-contact' }, + { + title: 'Add Contact', + iconName: 'chat-add-contact', + onPress: () => navigationService.navigate('ContactEdit'), + }, ], formatType: 'dynamicWidth', customPosition: { right: pTd(20), top: pageY + 20 }, diff --git a/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx b/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx index ff1850f62d..ffa4779865 100644 --- a/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx @@ -44,12 +44,18 @@ const FindMorePeople = () => { return ( - setKeyword(v)} /> - {!keyword && My Portkey Id: xxxxxxx} + + setKeyword(v)} /> + + {!keyword && ( + + {`My Portkey ID : xxxxxxx`} + + )} {list.length ? ( } renderItem={renderItem} /> ) : ( @@ -71,4 +77,11 @@ const styles = StyleSheet.create({ padding: pTd(16), }, buttonGroupWrap: {}, + portkeyId: { + textAlign: 'center', + height: pTd(46), + lineHeight: pTd(46), + borderBottomColor: defaultColors.border6, + borderBottomWidth: StyleSheet.hairlineWidth, + }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/AddContactButton/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/AddContactButton/index.tsx new file mode 100644 index 0000000000..9e67960a05 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/AddContactButton/index.tsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import Touchable from 'components/Touchable'; +import Svg from 'components/Svg'; +import { TextM } from 'components/CommonText'; +import GStyles from 'assets/theme/GStyles'; +import { pTd } from 'utils/unit'; +import { FontStyles } from 'assets/theme/styles'; +import { defaultColors } from 'assets/theme'; +import { StyleSheet } from 'react-native'; +import { screenWidth } from '@portkey-wallet/utils/mobile/device'; + +type AddContactButtonPropsType = { + onPressButton?: () => void; +}; + +export default function AddContactButton(props: AddContactButtonPropsType) { + const { onPressButton } = props; + const [isShow, setIsShow] = useState(true); + + if (!isShow) return null; + + return ( + onPressButton?.()}> + + Add Contact + setIsShow(false)}> + + + + ); +} + +const styles = StyleSheet.create({ + wrap: { + position: 'absolute', + top: 0, + zIndex: 100, + width: screenWidth, + backgroundColor: defaultColors.bg1, + height: pTd(48), + borderBottomColor: defaultColors.border6, + borderBottomWidth: StyleSheet.hairlineWidth, + shadowColor: defaultColors.shadow1, + shadowOpacity: 0.2, + shadowRadius: 10, + elevation: 2, + }, + closeIconWrap: { + position: 'absolute', + right: pTd(19), + top: pTd(19), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index b5a98b4d46..ee332ea994 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useCallback } from 'react'; -import { GiftedChat, GiftedChatProps, IMessage, MessageTextProps, Time } from 'react-native-gifted-chat'; +import { GiftedChat, GiftedChatProps, IMessage, MessageImageProps, MessageTextProps } from 'react-native-gifted-chat'; import initialMessages from '../messages'; import { AccessoryBar, BottomBarContainer } from '../InputToolbar'; import { renderSystemMessage, renderMessage } from '../MessageContainer'; @@ -12,6 +12,8 @@ import CustomBubble from '../CustomBubble'; import { setBottomBarStatus, setChatText } from '../context/chatsContext'; import useEffectOnce from 'hooks/useEffectOnce'; import MessageText from '../Message/MessageText'; +import MessageImage from '../Message/MessageImage'; + import { BGStyles } from 'assets/theme/styles'; const user = { @@ -47,10 +49,12 @@ const ChatsUI = () => { (props: MessageTextProps) => , [], ); - const renderTime: GiftedChatProps['renderTime'] = useCallback((props: MessageTextProps) => { - if (props.currentMessage?.text) return null; - return ; - }, []); + + const renderMessageImage: GiftedChatProps['renderMessageImage'] = useCallback( + (props: MessageImageProps) => , + [], + ); + const renderBubble = useCallback((data: any) => { return ; }, []); @@ -65,17 +69,18 @@ const ChatsUI = () => { isCustomViewBottom onSend={onSend} messages={messages} - showUserAvatar={false} + minInputToolbarHeight={0} renderAvatar={() => null} renderInputToolbar={() => null} - renderBubble={renderBubble} + renderTime={() => null} messageIdGenerator={randomId} - renderMessage={renderMessage} showAvatarForEveryMessage={false} isKeyboardInternallyHandled={false} + renderBubble={renderBubble} + renderMessage={renderMessage} renderMessageText={renderMessageText} + renderMessageImage={renderMessageImage} renderSystemMessage={renderSystemMessage} - renderTime={renderTime} /> diff --git a/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx index 58f1a84907..2cc711c771 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx @@ -1,47 +1,36 @@ import React from 'react'; -import { StyleSheet, View } from 'react-native'; +import { StyleSheet } from 'react-native'; import { BubbleProps, IMessage } from 'react-native-gifted-chat'; import { Bubble } from 'react-native-gifted-chat'; -import ChatOverlay from '../ChatOverlay'; import Touchable from 'components/Touchable'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; export default function CustomBubble(props: BubbleProps) { - // return ( - // - // BookMark - // - // ); - return ( - { - const { pageX, pageY } = event.nativeEvent; - ChatOverlay.showChatPopover({ - list: [{ title: '1111111' }, { title: '22222222' }], - px: pageX, - py: pageY, - position: props.position || 'left', - }); - }}> - + + ); } const styles = StyleSheet.create({ - bubbleWrap: {}, - bubbleToolWrap: { - zIndex: 1000, - position: 'absolute', - bottom: -30, - left: 100, + wrapperStyle: { + borderRadius: pTd(20), + color: defaultColors.font5, + padding: pTd(4), + }, + wrapLeft: { + backgroundColor: defaultColors.bg18, + borderTopLeftRadius: pTd(2), + marginLeft: -pTd(8), }, - bubbleToolItem: { - width: 60, - height: 60, - // backgroundColor: 'green', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + wrapRight: { + backgroundColor: defaultColors.bg9, + borderTopRightRadius: pTd(2), }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/FindMoreButton/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/FindMoreButton/index.tsx index ca803625d2..7ff8aed54c 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/FindMoreButton/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/FindMoreButton/index.tsx @@ -15,7 +15,7 @@ export default function FindMoreButton() { style={[GStyles.flexRow, GStyles.itemCenter, styles.wrap]} onPress={() => navigationService.navigate('FindMorePeople')}> - Find More People + Find More ); } @@ -24,6 +24,7 @@ const styles = StyleSheet.create({ wrap: { height: pTd(48), paddingLeft: pTd(20), + backgroundColor: defaultColors.bg1, borderBottomColor: defaultColors.border6, borderBottomWidth: StyleSheet.hairlineWidth, }, diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx index cb2b9d5733..f2d16cbf3e 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx @@ -6,6 +6,7 @@ import { windowWidth } from '@portkey-wallet/utils/mobile/device'; import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; import Svg from 'components/Svg'; +import { defaultColors } from 'assets/theme'; const NumColumns = 8; const ItemWidth = windowWidth / NumColumns; @@ -25,23 +26,37 @@ function Emoticons({ onPress, onDelete }: { onPress?: (item: EmojiItem) => void; item.name + index.toString()} numColumns={NumColumns} initialNumToRender={emojiList.length} onEndReachedThreshold={0.2} /> - - + + ); } export default memo(Emoticons, () => true); const styles = StyleSheet.create({ - itemStyle: { color: 'FFF', fontSize: pTd(30) }, + itemStyle: { color: defaultColors.font2, fontSize: pTd(30) }, itemBoxStyle: { width: ItemWidth, ...GStyles.center, }, + listContent: { + paddingBottom: pTd(40), + }, + delete: { + width: pTd(56), + height: pTd(40), + backgroundColor: defaultColors.bg1, + borderRadius: pTd(6), + overflow: 'hidden', + position: 'absolute', + right: pTd(8), + bottom: 0, + }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx index 3bb4146c30..74eccecf38 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx @@ -17,7 +17,6 @@ export const ToolBar = memo(function ToolBar() { allowsEditing: false, allowsMultipleSelection: false, })) as unknown as { uri: string }; - console.log(result); if (result.uri) { SendPicModal.showSendPic({ diff --git a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx new file mode 100644 index 0000000000..b373185fa4 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx @@ -0,0 +1,86 @@ +import React, { memo } from 'react'; +import { IMessage, MessageImageProps, Time } from 'react-native-gifted-chat'; +import { StyleSheet } from 'react-native'; +import CacheImage from 'components/CacheImage'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import Touchable from 'components/Touchable'; +import ChatOverlay from '../../ChatOverlay'; + +function MessageImage(props: MessageImageProps) { + const { currentMessage } = props; + + return ( + { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showChatPopover({ + list: [ + { title: 'Copy', iconName: 'copy' }, + { title: 'Delete', iconName: 'chat-delete' }, + ], + px: pageX, + py: pageY, + formatType: 'dynamicWidth', + }); + }}> + + + + ); +} + +export default memo(MessageImage); + +const styles = StyleSheet.create({ + image: { + borderRadius: pTd(20), + width: pTd(280), + height: pTd(280), + }, + textStyles: { + fontSize: pTd(16), + lineHeight: pTd(24), + marginVertical: pTd(4), + marginHorizontal: pTd(8), + }, + linkStyle: { + color: defaultColors.font4, + }, + timeBoxStyle: { + position: 'absolute', + backgroundColor: defaultColors.bg20, + paddingHorizontal: pTd(8), + borderRadius: pTd(8), + opacity: 0.8, + bottom: 0, + right: -pTd(4), + height: pTd(16), + }, + timeTextStyle: { + color: defaultColors.font2, + fontSize: pTd(10), + lineHeight: pTd(16), + }, +}); + +const timeContainerStyle = { + left: styles.timeBoxStyle, + right: styles.timeBoxStyle, +}; + +const timeTextStyle = { + left: styles.timeTextStyle, + right: styles.timeTextStyle, +}; diff --git a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx index 3e4826a789..5465cbc48b 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx @@ -5,6 +5,10 @@ import ParsedText from 'react-native-parsed-text'; import { StyleSheet, Text, TextStyle, View } from 'react-native'; import { useDiscoverJumpWithNetWork } from 'hooks/discover'; import { useThrottleCallback } from '@portkey-wallet/hooks'; +import { defaultColors } from 'assets/theme'; +import { pTd } from 'utils/unit'; +import Touchable from 'components/Touchable'; +import ChatOverlay from '../../ChatOverlay'; const UNICODE_SPACE = isIOS ? '\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0' : '\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0'; @@ -31,7 +35,19 @@ function MessageText(props: MessageTextProps) { ); return ( - + { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showChatPopover({ + list: [ + { title: 'Copy', iconName: 'copy' }, + { title: 'Delete', iconName: 'chat-delete' }, + ], + px: pageX, + py: pageY, + formatType: 'dynamicWidth', + }); + }}> ) { {UNICODE_SPACE} - - + + ); } @@ -50,32 +66,39 @@ export default memo(MessageText); const styles = StyleSheet.create({ textStyles: { - fontSize: 16, - lineHeight: 20, - marginTop: 5, - marginBottom: 5, - marginLeft: 10, - marginRight: 10, + fontSize: pTd(16), + lineHeight: pTd(24), + marginVertical: pTd(8), + marginHorizontal: pTd(12), }, linkStyle: { - color: 'red', + color: defaultColors.font4, }, timeBoxStyle: { position: 'absolute', - right: 10, - bottom: 2, + right: pTd(8), + bottom: pTd(0), + }, + timeTextStyle: { + color: defaultColors.font7, }, }); -const textContainerStyle = { +const timeContainerStyle = { left: styles.timeBoxStyle, right: styles.timeBoxStyle, }; + +const timeTextStyle = { + left: styles.timeTextStyle, + right: styles.timeTextStyle, +}; + const messageStyles = { left: StyleSheet.create({ container: {}, text: { - color: 'black', + color: defaultColors.font5, ...styles.textStyles, }, link: { @@ -86,7 +109,7 @@ const messageStyles = { right: StyleSheet.create({ container: {}, text: { - color: 'white', + color: defaultColors.font5, ...styles.textStyles, }, link: { diff --git a/packages/mobile-app-did/js/pages/Chat/components/MessageContainer/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/MessageContainer/index.tsx index bf955799dc..85535b1e1f 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/MessageContainer/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/MessageContainer/index.tsx @@ -1,66 +1,32 @@ import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; -import { BubbleProps, GiftedChatProps, IMessage } from 'react-native-gifted-chat'; -import { Avatar, Bubble, SystemMessage, Message, MessageText } from 'react-native-gifted-chat'; -import ChatOverlay from '../ChatOverlay'; -import Touchable from 'components/Touchable'; -export const renderAvatar: GiftedChatProps['renderAvatar'] = props => ( - -); - -export const renderBubble = (props: BubbleProps) => { - return ( - { - const { pageX, pageY } = event.nativeEvent; - ChatOverlay.showChatPopover({ - list: [{ title: '1111111' }, { title: '22222222' }], - px: pageX, - py: pageY, - position: props.position, - }); - }}> - - - ); -}; +import { StyleSheet } from 'react-native'; +import { GiftedChatProps } from 'react-native-gifted-chat'; +import { SystemMessage, Message, MessageText } from 'react-native-gifted-chat'; +import { pTd } from 'utils/unit'; export const renderSystemMessage: GiftedChatProps['renderSystemMessage'] = props => ; -export const renderMessage: GiftedChatProps['renderMessage'] = props => ; +export const renderMessage: GiftedChatProps['renderMessage'] = props => ( + +); export const renderMessageText: GiftedChatProps['renderMessageText'] = props => { return ; }; -export const renderCustomView: GiftedChatProps['renderCustomView'] = ({ user }) => ( - - - Current user: - {user?.name} - - From CustomView - -); - const styles = StyleSheet.create({ - bubbleWrap: {}, - bubbleToolWrap: { - zIndex: 1000, - position: 'absolute', - bottom: -30, - left: 100, + leftMessageContainer: { + marginLeft: pTd(16), + marginRight: 0, }, - bubbleToolItem: { - width: 60, - height: 60, - // backgroundColor: 'green', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + rightMessageContainer: { + marginLeft: 0, + marginRight: pTd(16), }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/messages.js b/packages/mobile-app-did/js/pages/Chat/components/messages.js index 8700cb817d..148e0b4804 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/messages.js +++ b/packages/mobile-app-did/js/pages/Chat/components/messages.js @@ -17,7 +17,6 @@ const messages = [ }, { _id: 3, - text: 'Hi! I work from home today!', createdAt: new Date(Date.UTC(2016, 5, 13, 17, 20, 0)), user: { _id: 1, @@ -83,13 +82,13 @@ const messages = [ }, { _id: 6, - text: 'Come on!1', createdAt: new Date(Date.UTC(2016, 5, 12, 17, 20, 0)), user: { _id: 2, name: 'React Native', avatar: 'https://lmg.jj20.com/up/allimg/1111/05161Q64001/1P516164001-3-1200.jpg', }, + image: ' https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSbxIduwVS7PV83GF5JAyWr9LcDuf0xIgonra3pClCZ&s', }, { _id: 7, diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ContactAddress/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ContactAddress/index.tsx index f628f6efd8..54859160bd 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ContactAddress/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactEdit/components/ContactAddress/index.tsx @@ -58,12 +58,12 @@ const ContactAddress: React.FC = ({ {`${t('Address')} ${editAddressIdx + 1}`} - + {/* {isDeleteShow && ( - )} + )} */} { })); }, []); - const addAddress = useCallback(() => { - if (editContact.addresses.length >= ADDRESS_NUM_LIMIT) return; - if (chainList.length < 1) return; - setEditContact(preEditContact => ({ - ...preEditContact, - addresses: [ - ...preEditContact.addresses, - { - id: '', - chainType: currentNetwork, - chainId: chainList[0].chainId, - address: '', - error: { ...INIT_HAS_ERROR }, - }, - ], - })); - }, [chainList, currentNetwork, editContact.addresses.length]); + // const addAddress = useCallback(() => { + // if (editContact.addresses.length >= ADDRESS_NUM_LIMIT) return; + // if (chainList.length < 1) return; + // setEditContact(preEditContact => ({ + // ...preEditContact, + // addresses: [ + // ...preEditContact.addresses, + // { + // id: '', + // chainType: currentNetwork, + // chainId: chainList[0].chainId, + // address: '', + // error: { ...INIT_HAS_ERROR }, + // }, + // ], + // })); + // }, [chainList, currentNetwork, editContact.addresses.length]); const deleteAddress = useCallback((deleteIdx: number) => { setEditContact(preEditContact => ({ @@ -352,14 +352,14 @@ const ContactEdit: React.FC = () => { /> ))} - {editContact.addresses.length < 5 && ( + {/* {editContact.addresses.length < 5 && ( {t('Add Address')} - )} + )} */} diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx index 2811728da4..68ea063157 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx @@ -1,9 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { TouchableOpacity, View } from 'react-native'; - import navigationService from 'utils/navigationService'; import Svg from 'components/Svg'; -import { pageStyles } from './style'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; @@ -11,11 +9,13 @@ import { useLanguage } from 'i18n/hooks'; import ContactsList from 'components/ContactList'; import CommonTopTab from 'components/CommonTopTab'; import { BGStyles } from 'assets/theme/styles'; -import GStyles from 'assets/theme/GStyles'; import CommonInput from 'components/CommonInput'; import { useContactList } from '@portkey-wallet/hooks/hooks-ca/contact'; import useDebounce from 'hooks/useDebounce'; import SearchContactListSection from '../SearchContactListSection'; +import { StyleSheet } from 'react-native'; +import GStyles from 'assets/theme/GStyles'; +import FindMoreButton from 'pages/Chat/components/FindMoreButton'; const ContactsHome: React.FC = () => { const { t } = useLanguage(); @@ -27,8 +27,16 @@ const ContactsHome: React.FC = () => { const tabList = useMemo( () => [ - { name: 'All', tabItemDom: }, - { name: 'Portkey Chat', tabItemDom: }, + { name: 'All', tabItemDom: }, + { + name: 'Portkey Chat', + tabItemDom: ( + <> + + + + ), + }, ], [], ); @@ -65,3 +73,14 @@ const ContactsHome: React.FC = () => { }; export default ContactsHome; + +export const pageStyles = StyleSheet.create({ + pageWrap: { + flex: 1, + backgroundColor: defaultColors.bg1, + ...GStyles.paddingArg(0), + }, + contactListStyle: { + backgroundColor: defaultColors.bg1, + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/style.ts b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/style.ts deleted file mode 100644 index d902b6f1c6..0000000000 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/style.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { StyleSheet } from 'react-native'; -import { defaultColors } from 'assets/theme'; -import GStyles from 'assets/theme/GStyles'; - -const { bgColor } = defaultColors; - -export const pageStyles = StyleSheet.create({ - pageWrap: { - flex: 1, - backgroundColor: bgColor, - ...GStyles.paddingArg(0), - }, -}); diff --git a/packages/mobile-app-did/js/pages/My/Contacts/SearchContactListSection/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/SearchContactListSection/index.tsx index 30940bcc26..b772d4e020 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/SearchContactListSection/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/SearchContactListSection/index.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react'; import { StyleSheet, FlatList } from 'react-native'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; -import { TextM } from 'components/CommonText'; +import ContactItem from 'components/ContactItem'; type SearchContactListSectionType = { list: any[]; @@ -11,8 +11,8 @@ type SearchContactListSectionType = { const SearchContactListSection: React.FC = (props: SearchContactListSectionType) => { const { list } = props; - const renderItem = useCallback((item: any) => { - return {JSON.stringify(item)}; + const renderItem = useCallback(({ item }: any) => { + return ; }, []); return ; diff --git a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx index 4e9f190b09..c0bc6637df 100644 --- a/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx +++ b/packages/mobile-app-did/js/pages/My/WalletHome/AboutUs/index.tsx @@ -33,7 +33,7 @@ const AboutUs = () => { {socialMediaList.map((item, index) => ( - <> + { }} /> {index !== socialMediaList.length - 1 && } - + ))} diff --git a/packages/mobile-app-did/js/pages/My/WalletHome/EditWalletName/index.tsx b/packages/mobile-app-did/js/pages/My/WalletHome/EditWalletName/index.tsx new file mode 100644 index 0000000000..0c8264fdd2 --- /dev/null +++ b/packages/mobile-app-did/js/pages/My/WalletHome/EditWalletName/index.tsx @@ -0,0 +1,118 @@ +import PageContainer from 'components/PageContainer'; +import { useLanguage } from 'i18n/hooks'; +import React, { useCallback, useState } from 'react'; +import CommonButton from 'components/CommonButton'; +import CommonInput from 'components/CommonInput'; +import { ErrorType } from 'types/common'; +import { INIT_HAS_ERROR } from 'constants/common'; +import { isValidCAWalletName } from '@portkey-wallet/utils/reg'; +import navigationService from 'utils/navigationService'; +import CommonToast from 'components/CommonToast'; +import { useSetWalletName, useWallet } from '@portkey-wallet/hooks/hooks-ca/wallet'; +import Loading from 'components/Loading'; +import FormItem from 'components/FormItem'; +import { View } from 'react-native'; +import { TextM } from 'components/CommonText'; +import Svg from 'components/Svg'; +import { pTd } from 'utils/unit'; +import Touchable from 'components/Touchable'; +import { StyleSheet } from 'react-native'; +import { defaultColors } from 'assets/theme'; +import gStyles from 'assets/theme/GStyles'; + +const WalletName: React.FC = () => { + const { t } = useLanguage(); + const { walletName } = useWallet(); + const [nameValue, setNameValue] = useState(walletName); + const [nameError, setNameError] = useState(INIT_HAS_ERROR); + const setWalletName = useSetWalletName(); + + const onNameChange = useCallback((value: string) => { + setNameValue(value); + setNameError({ ...INIT_HAS_ERROR }); + }, []); + + const onSave = useCallback(async () => { + const _nameValue = nameValue.trim(); + if (_nameValue === '') { + setNameValue(''); + setNameError({ + isError: true, + errorMsg: t('Please Enter Wallet Name'), + }); + return; + } + if (!isValidCAWalletName(_nameValue)) { + setNameError({ ...INIT_HAS_ERROR, errorMsg: t('3-16 characters, only a-z, A-Z, 0-9 and "_" allowed') }); + return; + } + // if (_nameValue === walletName) { + // CommonToast.success(t('Saved Successful'), undefined, 'bottom'); + // navigationService.goBack(); + // return; + // } + Loading.show(); + try { + await setWalletName(_nameValue); + navigationService.goBack(); + CommonToast.success(t('Saved Successful'), undefined, 'bottom'); + } catch (error: any) { + CommonToast.failError(error.error); + console.log('setWalletName: error', error); + } + Loading.hide(); + }, [nameValue, setWalletName, t]); + + return ( + + + + + + + + xxxxxx-yyyy-zzzzz + + + + + + + + + xxxxxx-yyyy-zzzzz + + + + + + + {t('Save')} + + + ); +}; +export default WalletName; + +export const pageStyles = StyleSheet.create({ + pageWrap: { + flex: 1, + backgroundColor: defaultColors.bg4, + justifyContent: 'space-between', + ...gStyles.paddingArg(32, 20, 18), + }, +}); From 546d019d2229a61435288f15b03de72dbc392410 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Wed, 16 Aug 2023 11:53:34 +0800 Subject: [PATCH 519/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20reset=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/pages/Chat/components/Chats/index.tsx | 1 + .../pages/Chat/components/InputToolbar/Emoticons/index.tsx | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index f04a8cda04..0f20b32088 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -36,6 +36,7 @@ const ChatsUI = () => { initChatInputRecorder(); return () => { dispatch(setChatText('')); + dispatch(setBottomBarStatus(undefined)); dispatch(setShowSoftInputOnFocus(true)); destroyChatInputRecorder(); }; diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx index cb2b9d5733..4bf1569a02 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/Emoticons/index.tsx @@ -4,7 +4,6 @@ import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native import GStyles from 'assets/theme/GStyles'; import { windowWidth } from '@portkey-wallet/utils/mobile/device'; import { pTd } from 'utils/unit'; -import Touchable from 'components/Touchable'; import Svg from 'components/Svg'; const NumColumns = 8; @@ -31,9 +30,9 @@ function Emoticons({ onPress, onDelete }: { onPress?: (item: EmojiItem) => void; initialNumToRender={emojiList.length} onEndReachedThreshold={0.2} /> - + - + ); } From 1aec09fe421bc506c3441a9ced74de23d4b0607c Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Wed, 16 Aug 2023 14:09:40 +0800 Subject: [PATCH 520/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20change=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputToolbar/AccessoryBar/index.tsx | 27 ++++-- .../InputToolbar/BottomBarContainer/index.tsx | 55 +----------- .../InputToolbar/ChatInput/index.tsx | 88 +++++++++++++++++++ .../js/pages/Chat/components/context/hooks.ts | 7 ++ .../js/pages/Chat/components/hooks/index.ts | 19 ++-- .../js/pages/Chat/utils/index.ts | 36 +++++++- 6 files changed, 163 insertions(+), 69 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx index bd7d13b640..e8c5cff0d6 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, useMemo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; import { pTd } from 'utils/unit'; @@ -8,15 +8,29 @@ import { BGStyles } from 'assets/theme/styles'; import { useBottomBarStatus, useChatsDispatch } from '../../context/hooks'; import { ChatBottomBarStatus } from 'store/chat/slice'; import { ToolBar } from '../ToolBar'; -import { handleInputText } from 'pages/Chat/utils'; +import { handleDeleteText, handleInputText } from 'pages/Chat/utils'; import { setChatText } from '../../context/chatsContext'; +import { EmojiItem } from '../Emoticons/config'; export const AccessoryBar = memo( function AccessoryBar() { const bottomBarStatus = useBottomBarStatus(); const dispatch = useChatsDispatch(); - const showTools = useMemo(() => !!bottomBarStatus, [bottomBarStatus]); + + const onPress = useCallback( + (item: EmojiItem) => { + const text = handleInputText(item.code); + dispatch(setChatText(text)); + }, + [dispatch], + ); + const onDelete = useCallback(() => { + const text = handleDeleteText(); + console.log(text, '=====text'); + + dispatch(setChatText(text)); + }, [dispatch]); return ( - { - const text = handleInputText(item.code); - dispatch(setChatText(text)); - }} - /> + diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx index 1d342fcc9e..06e91bb568 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx @@ -12,10 +12,10 @@ import useEffectOnce from 'hooks/useEffectOnce'; import { useKeyboardAnim } from '../../hooks'; import { useBottomBarStatus, useChatText, useChatsDispatch } from '../../context/hooks'; import { ChatBottomBarStatus } from 'store/chat/slice'; -import { setBottomBarStatus, setChatText } from '../../context/chatsContext'; +import { setBottomBarStatus } from '../../context/chatsContext'; import { BGStyles } from 'assets/theme/styles'; -import { defaultColors } from 'assets/theme'; import { SendMessageButton } from '../SendMessageButton'; +import { ChatInput } from '../ChatInput'; export const ActionsIcon = memo(function ActionsIcon({ onPress }: { onPress?: () => void }) { return ( @@ -28,17 +28,6 @@ export const ActionsIcon = memo(function ActionsIcon({ onPress }: { onPress?: () ); }); -export const EmojiIcon = memo(function EmojiIcon({ onPress }: { onPress?: () => void }) { - return ( - } - optionTintColor="#222B45" - /> - ); -}); - export function AndroidInputContainer({ children, textInputRef, @@ -117,28 +106,10 @@ export function BottomBarContainer({ children }: { children?: ReactNode; showKey onPressActionButton(ChatBottomBarStatus.tools)} /> {isIOS ? ( - - dispatch(setChatText(v))} - value={text} - ref={textInputRef as any} - /> - onPressActionButton(ChatBottomBarStatus.emoji)} /> - + ) : ( - - dispatch(setChatText(v))} - ref={textInputRef as any} - /> - onPressActionButton(ChatBottomBarStatus.emoji)} /> - + )} @@ -159,24 +130,6 @@ const styles = StyleSheet.create({ marginBottom: pTd(8), marginRight: pTd(8), }, - emojiSvg: { - marginLeft: 0, - marginBottom: 0, - position: 'absolute', - right: pTd(8), - bottom: pTd(8), - }, - inputWrapStyle: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - backgroundColor: defaultColors.bg1, - borderRadius: pTd(20), - paddingRight: pTd(40), - paddingVertical: pTd(6), - minHeight: pTd(40), - maxHeight: pTd(200), - }, input: { width: '100%', paddingLeft: pTd(16), diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx new file mode 100644 index 0000000000..7ee68a3dfd --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx @@ -0,0 +1,88 @@ +import React, { forwardRef, memo, useCallback } from 'react'; +import { NativeSyntheticEvent, TextInput, TextInputSelectionChangeEventData, View } from 'react-native'; +import { useChatText, useChatsDispatch, useIsShowEmoji } from '../../context/hooks'; +import GStyles from 'assets/theme/GStyles'; +import { setChatText } from '../../context/chatsContext'; +import Svg from 'components/Svg'; +import { StyleSheet } from 'react-native'; +import { pTd } from 'utils/unit'; +import { defaultColors } from 'assets/theme'; +import { ChatBottomBarStatus } from 'store/chat/slice'; +import Touchable from 'components/Touchable'; +import { chatInputRecorder } from 'pages/Chat/utils'; + +export const EmojiIcon = memo(function EmojiIcon({ onPress }: { onPress?: () => void }) { + return ( + + + + ); +}); +export const ChatInput = forwardRef(function InputBar( + { + onPressActionButton, + }: { + onPressActionButton: (status: ChatBottomBarStatus) => void; + }, + _ref, +) { + const dispatch = useChatsDispatch(); + const text = useChatText(); + const isShowEmoji = useIsShowEmoji(); + const onChangeText = useCallback( + (v: string) => { + dispatch(setChatText(v)); + chatInputRecorder?.setText(v); + }, + [dispatch], + ); + const onSelectionChange = useCallback( + ({ nativeEvent }: NativeSyntheticEvent) => { + if (isShowEmoji) return; + chatInputRecorder?.setSelection(nativeEvent.selection); + }, + [isShowEmoji], + ); + return ( + + + onPressActionButton(ChatBottomBarStatus.emoji)} /> + + ); +}); + +const styles = StyleSheet.create({ + emojiSvg: { + marginLeft: 0, + marginBottom: 0, + position: 'absolute', + right: pTd(8), + bottom: pTd(8), + }, + inputWrapStyle: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + backgroundColor: defaultColors.bg1, + borderRadius: pTd(20), + paddingRight: pTd(40), + paddingVertical: pTd(6), + minHeight: pTd(40), + maxHeight: pTd(200), + }, + input: { + width: '100%', + paddingLeft: pTd(16), + }, + hide: { + width: 0, + height: 0, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts b/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts index d7f8ae7fdf..0c76d84104 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts +++ b/packages/mobile-app-did/js/pages/Chat/components/context/hooks.ts @@ -1,4 +1,5 @@ import { useLatestRef } from '@portkey-wallet/hooks'; +import { ChatBottomBarStatus } from 'store/chat/slice'; import { useAppDispatch, useAppSelector } from 'store/hooks'; export function useBottomBarStatus() { @@ -9,6 +10,12 @@ export function useChatText() { return useAppSelector(state => state.chats.text); } +export function useIsShowInput() { + return useAppSelector(state => state.chats.bottomBarStatus) === ChatBottomBarStatus.input; +} +export function useIsShowEmoji() { + return useAppSelector(state => state.chats.bottomBarStatus) === ChatBottomBarStatus.emoji; +} export function useShowSoftInputOnFocus() { return useAppSelector(state => state.chats.showSoftInputOnFocus); } diff --git a/packages/mobile-app-did/js/pages/Chat/components/hooks/index.ts b/packages/mobile-app-did/js/pages/Chat/components/hooks/index.ts index c18738d8f2..d024140384 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/hooks/index.ts +++ b/packages/mobile-app-did/js/pages/Chat/components/hooks/index.ts @@ -10,6 +10,8 @@ import { setBottomBarStatus } from '../context/chatsContext'; const TopSpacing = isIOS ? bottomBarHeight : -(bottomBarHeight + 10); +const ToolsHeight = 100; + export function useKeyboardAnim({ textInputRef }: { textInputRef: React.RefObject }) { const keyboardAnim = useRef(new Animated.Value(0)).current; const bottomBarStatus = useBottomBarStatus(); @@ -19,19 +21,22 @@ export function useKeyboardAnim({ textInputRef }: { textInputRef: React.RefObjec [bottomBarStatus], ); const { keyboardHeight, isKeyboardOpened } = useKeyboard(TopSpacing); - const toValue = useMemo( - () => (showTools || isKeyboardOpened ? keyboardHeight : 0), - [isKeyboardOpened, keyboardHeight, showTools], - ); + const toValue = useMemo(() => { + if (bottomBarStatus === ChatBottomBarStatus.tools) return ToolsHeight; + return showTools || isKeyboardOpened ? keyboardHeight : 0; + }, [bottomBarStatus, isKeyboardOpened, keyboardHeight, showTools]); + // const toValue = useMemo( + // () => (showTools || isKeyboardOpened ? keyboardHeight : 0), + // [isKeyboardOpened, keyboardHeight, showTools], + // ); const preToValue = usePrevious(toValue); useEffect(() => { - if (preToValue !== toValue) { + if (preToValue !== toValue) Animated.timing(keyboardAnim, { toValue, - duration: toValue > 0 ? 150 : 200, + duration: toValue > 0 ? 250 : 300, useNativeDriver: false, }).start(); - } }, [keyboardAnim, preToValue, toValue]); useEffect(() => { diff --git a/packages/mobile-app-did/js/pages/Chat/utils/index.ts b/packages/mobile-app-did/js/pages/Chat/utils/index.ts index f4453140ea..95e79d77a1 100644 --- a/packages/mobile-app-did/js/pages/Chat/utils/index.ts +++ b/packages/mobile-app-did/js/pages/Chat/utils/index.ts @@ -44,7 +44,39 @@ function handleInputText(code: string): string { return handleInputText(code); } } else { - text = chatInputRecorder?.text + code; + text = text + code; + chatInputRecorder?.setSelection({ start: text.length, end: text.length }); + } + if (chatInputRecorder) { + chatInputRecorder.setText(text); + } + return text; +} +function isEmoji(character: string) { + return /\p{Emoji}/u.test(character); +} +function handleDeleteText(): string { + let text = chatInputRecorder?.text || ''; + if (chatInputRecorder?.selection) { + const { start, end } = chatInputRecorder?.selection; + if (start === end) { + let first = text.slice(0, start); + const last = text.slice(start); + const code = isEmoji(first.slice(-2)) ? -2 : -1; + first = first.slice(0, code); + chatInputRecorder.setSelection({ start: first.length, end: first.length }); + text = first + last; + } else { + const first = text.slice(0, start); + const last = text.slice(end); + text = first + last; + chatInputRecorder.setText(text); + chatInputRecorder.setSelection({ start: first.length, end: first.length }); + return text; + } + } else { + const code = isEmoji(text.slice(-2)) ? -2 : -1; + text = text.slice(0, code); chatInputRecorder?.setSelection({ start: text.length, end: text.length }); } if (chatInputRecorder) { @@ -53,4 +85,4 @@ function handleInputText(code: string): string { return text; } -export { chatInputRecorder, initChatInputRecorder, destroyChatInputRecorder, handleInputText }; +export { chatInputRecorder, initChatInputRecorder, destroyChatInputRecorder, handleInputText, handleDeleteText }; From e605583cc2ed91ee7ec8c649078024397fcff0d6 Mon Sep 17 00:00:00 2001 From: ykx Date: Wed, 16 Aug 2023 15:11:54 +0800 Subject: [PATCH 521/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20edit=20and?= =?UTF-8?q?=20add=20contact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/web/Popup/routes/index.tsx | 10 +- .../app/web/Prompt/routes/index.tsx | 20 +- .../app/web/hooks/useProfile.ts | 23 +- .../pages/Contacts/AddContact/Popup/index.tsx | 4 - .../Contacts/AddContact/Prompt/index.tsx | 4 - .../web/pages/Contacts/AddContact/index.tsx | 58 +---- .../pages/Contacts/ContactDetail/index.tsx | 6 +- .../Contacts/EditContact/Popup/index.tsx | 22 +- .../Contacts/EditContact/Prompt/index.tsx | 15 +- .../web/pages/Contacts/EditContact/index.tsx | 206 ++++-------------- .../app/web/pages/Contacts/Popup/index.less | 10 + .../app/web/pages/Contacts/Popup/index.tsx | 3 +- .../app/web/pages/Contacts/Prompt/index.less | 12 +- .../app/web/pages/Contacts/Prompt/index.tsx | 7 +- .../web/pages/Contacts/ViewContact/index.tsx | 16 +- .../components/AddContactForm/index.less | 21 +- .../components/AddContactForm/index.tsx | 31 +-- .../components/ContactsBody/index.less | 11 + .../components/ContactsSearchInput/index.less | 2 +- .../components/EditContactForm/index.tsx | 12 +- .../Contacts/components/NoContacts/index.tsx | 11 +- .../components/ViewContactBody/index.tsx | 2 +- .../app/web/pages/Contacts/index.tsx | 13 +- .../Send/components/RecentDetail/index.tsx | 6 +- .../app/web/pages/Wallet/Prompt/index.less | 4 + .../app/web/pages/Wallet/WalletName/index.tsx | 7 +- .../app/web/pages/Wallet/index.tsx | 2 +- .../app/web/types/Profile.ts | 4 +- 28 files changed, 206 insertions(+), 336 deletions(-) diff --git a/packages/web-extension-did/app/web/Popup/routes/index.tsx b/packages/web-extension-did/app/web/Popup/routes/index.tsx index 4821a9f6fa..e5f6a5cf31 100644 --- a/packages/web-extension-did/app/web/Popup/routes/index.tsx +++ b/packages/web-extension-did/app/web/Popup/routes/index.tsx @@ -77,6 +77,14 @@ export const PageRouter = () => path: '/setting/wallet/wallet-name', element: , }, + { + path: '/setting/wallet/:type', + element: , + }, + { + path: '/setting/wallet/:type/:extra', + element: , + }, { path: '/setting/wallet/auto-lock', element: , @@ -138,7 +146,7 @@ export const PageRouter = () => element: , }, { - path: '/setting/contacts/:type/:chat', + path: '/setting/contacts/:type/:extra', element: , }, { diff --git a/packages/web-extension-did/app/web/Prompt/routes/index.tsx b/packages/web-extension-did/app/web/Prompt/routes/index.tsx index 7dfca38d9f..9a5dd4d058 100644 --- a/packages/web-extension-did/app/web/Prompt/routes/index.tsx +++ b/packages/web-extension-did/app/web/Prompt/routes/index.tsx @@ -196,6 +196,14 @@ export const PageRouter = () => { path: '/setting/wallet/wallet-name', element: , }, + { + path: '/setting/wallet/:type', + element: , + }, + { + path: '/setting/wallet/:type/:extra', + element: , + }, { path: '/setting/wallet/auto-lock', element: , @@ -219,7 +227,7 @@ export const PageRouter = () => { element: , }, { - path: '/setting/contacts/:type/:chat', + path: '/setting/contacts/:type/:extra', element: , }, { @@ -338,6 +346,14 @@ export const PageRouter = () => { path: '/setting/wallet/wallet-name', element: , }, + { + path: '/setting/wallet/:type', + element: , + }, + { + path: '/setting/wallet/:type/:extra', + element: , + }, { path: '/setting/wallet/auto-lock', element: , @@ -359,7 +375,7 @@ export const PageRouter = () => { element: , }, { - path: '/setting/contacts/:type/:chat', + path: '/setting/contacts/:type/:extra', element: , }, { diff --git a/packages/web-extension-did/app/web/hooks/useProfile.ts b/packages/web-extension-did/app/web/hooks/useProfile.ts index 008135ba4a..b2757f426a 100644 --- a/packages/web-extension-did/app/web/hooks/useProfile.ts +++ b/packages/web-extension-did/app/web/hooks/useProfile.ts @@ -5,30 +5,45 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router'; import { useCopyToClipboard } from 'react-use'; import { useCommonState } from 'store/Provider/hooks'; -import { ChatType } from 'types/Profile'; +import { ExtraType } from 'types/Profile'; export const useProfileEdit = () => { const navigate = useNavigate(); return useCallback( - (chat: ChatType, state: any) => { + (chat: ExtraType, state: any) => { navigate(`/setting/contacts/edit/${chat}`, { state }); }, [navigate], ); }; -export const useProfileAddContact = () => { +export const useMyProfileEdit = () => { + const navigate = useNavigate(); + + return useCallback( + (chat: ExtraType, state: any) => { + navigate(`/setting/wallet/edit/${chat}`, { state }); + }, + [navigate], + ); +}; + +export const useProfileAddNewContact = () => { const navigate = useNavigate(); return useCallback( - (chat: ChatType, state: any) => { + (chat: ExtraType, state: any) => { navigate(`/setting/contacts/add/${chat}`, { state }); }, [navigate], ); }; +export const useProfileAddContact = () => { + // TODO api +}; + export const useProfileChat = () => { const navigate = useNavigate(); const { isPrompt } = useCommonState(); diff --git a/packages/web-extension-did/app/web/pages/Contacts/AddContact/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/AddContact/Popup/index.tsx index fab099c150..adb9b200ca 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/AddContact/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/AddContact/Popup/index.tsx @@ -11,13 +11,11 @@ export default function AddContactPopup({ state, addressArr, validName, - validRemark, headerTitle, isShowDrawer, goBack, onFinish, handleInputValueChange, - handleInputRemarkChange, handleSelectNetwork, handleAddressChange, closeDrawer, @@ -36,14 +34,12 @@ export default function AddContactPopup({ form={form} isDisable={isDisable} validName={validName} - validRemark={validRemark} state={state} addressArr={addressArr} onFinish={onFinish} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} handleInputValueChange={handleInputValueChange} - handleInputRemarkChange={handleInputRemarkChange} />
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx index 1910052820..0376b2a468 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo, useEffect, useState } from 'react'; import { Form, Modal, message } from 'antd'; -import { useNavigate, useLocation } from 'react-router'; +import { useNavigate, useLocation, useParams } from 'react-router'; import { useTranslation } from 'react-i18next'; import { ContactItemType, AddressItem } from '@portkey-wallet/types/types-ca/contact'; import { fetchContactListAsync } from '@portkey-wallet/store/store-ca/contact/actions'; @@ -45,13 +45,13 @@ export default function AddContact() { const { t } = useTranslation(); const navigate = useNavigate(); const { state } = useLocation(); + const { extra } = useParams(); const appDispatch = useAppDispatch(); const [disable, setDisabled] = useState(true); const [netOpen, setNetOpen] = useState(false); const [index, setIndex] = useState(-1); const [validName, setValidName] = useState({ validateStatus: '', errorMsg: '' }); const [addressArr, setAddressArr] = useState(state?.addresses); - const [validRemark, setValidRemark] = useState({ validateStatus: '', errorMsg: '' }); const addContactApi = useAddContact(); const checkExistNameApi = useCheckContactName(); const { setLoading } = useLoading(); @@ -114,18 +114,6 @@ export default function AddContact() { [handleFormValueChange], ); - const handleInputRemarkChange = useCallback( - (v: string) => { - setValidRemark({ validateStatus: '', errorMsg: '' }); - if (!v) { - setDisabled(true); - } else { - handleFormValueChange(); - } - }, - [handleFormValueChange], - ); - const handleAddressChange = useCallback( (i: number, value: string) => { value = getAelfAddress(value.trim()); @@ -175,26 +163,6 @@ export default function AddContact() { [checkExistName, form], ); - const checkExistRemark = useCallback( - async (v: string) => { - if (state.remark === v) { - return false; - } - const { existed } = await checkExistNameApi(v); // TODO remark - return existed; - }, - [checkExistNameApi, state.remark], - ); - - const handleCheckRemark = useCallback( - async (v: string) => { - const existed = await checkExistRemark(v); - - return existed; - }, - [checkExistRemark], - ); - const handleCheckAddress = useCallback( (addresses: AddressItem[]) => { let flag = 0; @@ -216,15 +184,13 @@ export default function AddContact() { const onFinish = useCallback( async (values: ContactItemType) => { - const { name, remark, addresses } = values; + const { name, addresses } = values; try { setLoading(true); const checkName = await handleCheckName(name.trim()); - const checkRemark = await handleCheckRemark(remark?.trim() || ''); const checkAddress = handleCheckAddress(addresses); - if (checkName && checkRemark && checkAddress) { - // TODO remark + if (checkName && checkAddress) { await addContactApi({ name: name.trim(), addresses }); appDispatch(fetchContactListAsync()); @@ -251,15 +217,17 @@ export default function AddContact() { setLoading(false); } }, - [addContactApi, appDispatch, handleCheckAddress, handleCheckName, handleCheckRemark, navigate, setLoading, t], + [addContactApi, appDispatch, handleCheckAddress, handleCheckName, navigate, setLoading, t], ); // go back previous page const handleGoBack = useCallback(() => { - navigate('/setting/contacts/view', { state: state }); - - // navigate('/setting/contacts'); - }, [navigate, state]); + if (extra === '3') { + navigate('/setting/contacts'); + } else { + navigate('/setting/contacts/view', { state: state }); + } + }, [extra, navigate, state]); const handleCloseDrawer = () => { setNetOpen(false); @@ -275,14 +243,12 @@ export default function AddContact() { form={form} isDisable={disable} validName={validName} - validRemark={validRemark} state={state} addressArr={addressArr} onFinish={onFinish} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} handleInputValueChange={handleInputValueChange} - handleInputRemarkChange={handleInputRemarkChange} isShowDrawer={netOpen} closeDrawer={handleCloseDrawer} handleNetworkChange={handleNetworkChange} @@ -294,14 +260,12 @@ export default function AddContact() { form={form} isDisable={disable} validName={validName} - validRemark={validRemark} state={state} addressArr={addressArr} onFinish={onFinish} handleSelectNetwork={handleSelectNetwork} handleAddressChange={handleAddressChange} handleInputValueChange={handleInputValueChange} - handleInputRemarkChange={handleInputRemarkChange} isShowDrawer={netOpen} closeDrawer={handleCloseDrawer} handleNetworkChange={handleNetworkChange} diff --git a/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx index db5b744d6e..3c42905319 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx @@ -6,14 +6,14 @@ import './index.less'; import AddContact from '../AddContact'; export default function ContactDetail() { - const { type, chat } = useParams(); + const { type, extra } = useParams(); const { isNotLessThan768 } = useCommonState(); return (
    {type === 'view' && } - {type === 'edit' && chat === '1' && } - {type === 'edit' && chat === '2' && } + {type === 'edit' && extra === '1' && } + {type === 'edit' && extra === '2' && } {type === 'add' && }
    ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx index 446cd32882..fe9e72b05d 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.tsx @@ -1,28 +1,24 @@ import './index.less'; import CustomSvg from 'components/CustomSvg'; -import NetworkDrawer from '../../NetworkDrawer'; import BackHeader from 'components/BackHeader'; -import { IAddContactProps } from '..'; +import { IEditContactProps } from '..'; import EditContactForm from 'pages/Contacts/components/EditContactForm'; export default function EditContactPopup({ form, - isNameDisable = false, - isShowRemark = true, - canSave = false, + isNameDisable, + isShowRemark, + canSave, state, validName, validRemark, headerTitle, - isShowDrawer, goBack, onFinish, handleInputValueChange, handleInputRemarkChange, - closeDrawer, - handleNetworkChange, handleCopy, -}: IAddContactProps) { +}: IEditContactProps) { return (
    @@ -45,14 +41,6 @@ export default function EditContactPopup({ handleInputRemarkChange={handleInputRemarkChange} handleCopy={handleCopy} /> -
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx index 4c1e4a2018..412215ac6c 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.tsx @@ -1,27 +1,23 @@ import './index.less'; import SecondPageHeader from 'pages/components/SecondPageHeader'; -import { IAddContactProps } from '..'; -import NetworkModal from 'pages/Contacts/NetworkModal'; +import { IEditContactProps } from '..'; import EditContactForm from 'pages/Contacts/components/EditContactForm'; export default function EditContactPrompt({ form, - isNameDisable = false, - isShowRemark = true, - canSave = false, + isNameDisable, + isShowRemark, + canSave, state, validName, validRemark, headerTitle, - isShowDrawer, goBack, onFinish, handleInputValueChange, handleInputRemarkChange, - closeDrawer, - handleNetworkChange, handleCopy, -}: IAddContactProps) { +}: IEditContactProps) { return (
    @@ -38,7 +34,6 @@ export default function EditContactPrompt({ handleInputRemarkChange={handleInputRemarkChange} handleCopy={handleCopy} /> -
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx index 2299371edd..5236910b4a 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx @@ -1,117 +1,47 @@ -import { useCallback, useMemo, useEffect, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { Form, Modal, message } from 'antd'; import { useNavigate, useLocation } from 'react-router'; import { useTranslation } from 'react-i18next'; -import { ContactItemType, AddressItem } from '@portkey-wallet/types/types-ca/contact'; +import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; import { fetchContactListAsync } from '@portkey-wallet/store/store-ca/contact/actions'; import { useAppDispatch, useLoading } from 'store/Provider/hooks'; -import { getAelfAddress, isAelfAddress } from '@portkey-wallet/utils/aelf'; import { isValidCAWalletName } from '@portkey-wallet/utils/reg'; import { useEditContact, useCheckContactName } from '@portkey-wallet/hooks/hooks-ca/contact'; -import { transNetworkText } from '@portkey-wallet/utils/activity'; -import { useIsTestnet } from 'hooks/useNetwork'; -import { IAddContactFormProps } from '../components/AddContactForm'; import EditContactPrompt from './Prompt'; import EditContactPopup from './Popup'; import { BaseHeaderProps } from 'types/UI'; import { useCommonState } from 'store/Provider/hooks'; import { useProfileCopy } from 'hooks/useProfile'; +import { IEditContactFormProps } from '../components/EditContactForm'; +import { ContactInfoError, ValidData } from '../AddContact'; -export enum ContactInfoError { - invalidAddress = 'Invalid address', - recipientAddressIsInvalid = 'Recipient address is invalid', - noName = 'Please enter contact name', - alreadyExists = 'This name already exists.', - inValidName = '3-16 characters, only a-z, A-Z, 0-9 and "_" allowed', -} - -type ValidateStatus = Parameters[0]['validateStatus']; -export type ValidData = { - validateStatus: ValidateStatus; - errorMsg: string; -}; -export interface CustomAddressItem extends AddressItem { - networkName: string; - validData: ValidData; -} - -export interface IAddContactProps extends IAddContactFormProps, BaseHeaderProps { - isShowDrawer: boolean; - isNameDisable?: boolean; - isShowRemark?: boolean; - canSave?: boolean; - closeDrawer: () => void; - handleNetworkChange: (v: any) => void; - handleCopy: (val: string) => void; -} +export type IEditContactProps = IEditContactFormProps & BaseHeaderProps; export default function EditContact() { const [form] = Form.useForm(); const { t } = useTranslation(); const navigate = useNavigate(); - const { state } = useLocation(); + const { state, pathname } = useLocation(); + const { isNotLessThan768 } = useCommonState(); const appDispatch = useAppDispatch(); - const [disable, setDisabled] = useState(true); - const [netOpen, setNetOpen] = useState(false); - const [index, setIndex] = useState(-1); + const [canSave, setCanSave] = useState(false); const [validName, setValidName] = useState({ validateStatus: '', errorMsg: '' }); - const [addressArr, setAddressArr] = useState(state?.addresses); const [validRemark, setValidRemark] = useState({ validateStatus: '', errorMsg: '' }); const editContactApi = useEditContact(); const checkExistNameApi = useCheckContactName(); const { setLoading } = useLoading(); - const isTestNet = useIsTestnet(); - - useEffect(() => { - const { addresses } = state; - const cusAddresses = addresses.map((ads: AddressItem) => ({ - ...ads, - networkName: transNetworkText(ads.chainId, isTestNet), - validData: { validateStatus: '', errorMsg: '' }, - })); - form.setFieldValue('addresses', cusAddresses); - setAddressArr(cusAddresses); - setDisabled(false); - }, [form, isTestNet, state]); - - const handleSelectNetwork = useCallback((i: number) => { - setNetOpen(true); - setIndex(i); - }, []); - - const handleNetworkChange = useCallback( - (v: any) => { - const prevAddresses = form.getFieldValue('addresses'); - prevAddresses.splice(index, 1, { - address: '', - networkName: v.networkName, - chainId: v.chainId, - validData: { validateStatus: '', errorMsg: '' }, - }); - form.setFieldValue('addresses', [...prevAddresses]); - const newAddresses = Object.assign(addressArr); - newAddresses.splice(index, 1, v); - // addressArr.splice(index, 1, v); - setAddressArr(newAddresses); - setNetOpen(false); - setDisabled(true); - }, - [addressArr, form, index], - ); - const handleFormValueChange = useCallback(() => { - const { name, addresses } = form.getFieldsValue(); - const flag = addresses.some((ads: Record) => !ads?.address); - const err = addressArr.some((ads) => ads.validData.validateStatus === 'error'); - setDisabled(!name || !addresses.length || flag || err); - }, [addressArr, form]); + const { name } = form.getFieldsValue(); + // TODO + setCanSave(name); + }, [form]); const handleInputValueChange = useCallback( (v: string) => { setValidName({ validateStatus: '', errorMsg: '' }); if (!v) { - setDisabled(true); + setCanSave(false); } else { handleFormValueChange(); } @@ -123,7 +53,7 @@ export default function EditContact() { (v: string) => { setValidRemark({ validateStatus: '', errorMsg: '' }); if (!v) { - setDisabled(true); + setCanSave(false); } else { handleFormValueChange(); } @@ -131,20 +61,6 @@ export default function EditContact() { [handleFormValueChange], ); - const handleAddressChange = useCallback( - (i: number, value: string) => { - value = getAelfAddress(value.trim()); - const { addresses } = form.getFieldsValue(); - addresses[i].address = value; - const newAddresses = Object.assign(addressArr); - newAddresses[i].validData = { validateStatus: '', errorMsg: '' }; - setAddressArr(newAddresses); - form.setFieldValue('addresses', [...addresses]); - handleFormValueChange(); - }, - [addressArr, form, handleFormValueChange], - ); - const checkExistName = useCallback( async (v: string) => { if (state.name === v) { @@ -161,19 +77,19 @@ export default function EditContact() { const existed = await checkExistName(v); if (!v) { form.setFieldValue('name', ''); + setCanSave(false); setValidName({ validateStatus: 'error', errorMsg: ContactInfoError.noName }); - setDisabled(true); return false; } else if (existed) { - setDisabled(true); + setCanSave(false); setValidName({ validateStatus: 'error', errorMsg: ContactInfoError.alreadyExists }); return false; } else if (!isValidCAWalletName(v)) { - setDisabled(true); + setCanSave(false); setValidName({ validateStatus: 'error', errorMsg: ContactInfoError.inValidName }); return false; } - setDisabled(false); + setCanSave(true); setValidName({ validateStatus: '', errorMsg: '' }); return true; }, @@ -191,43 +107,27 @@ export default function EditContact() { [checkExistNameApi, state.remark], ); - const handleCheckRemark = useCallback(async (v: string) => { - const existed = await checkExistRemark(v); - - return existed; - }, []); + const handleCheckRemark = useCallback( + async (v: string) => { + // TODO err + const existed = await checkExistRemark(v); - const handleCheckAddress = useCallback( - (addresses: AddressItem[]) => { - let flag = 0; - const newAddress = Object.assign(addressArr); - addresses.forEach((ads, i) => { - if (!isAelfAddress(ads.address)) { - flag++; - newAddress[i].validData = { validateStatus: 'error', errorMsg: ContactInfoError.invalidAddress }; - } - }); - if (!flag) { - setAddressArr(newAddress); - setDisabled(true); - } - return !flag; + return existed; }, - [addressArr], + [checkExistRemark], ); const onFinish = useCallback( async (values: ContactItemType) => { - const { name, remark, addresses } = values; + const { name, remark } = values; try { setLoading(true); const checkName = await handleCheckName(name.trim()); const checkRemark = await handleCheckRemark(remark?.trim() || ''); - const checkAddress = handleCheckAddress(addresses); - if (checkName && checkRemark && checkAddress) { + if (checkName && checkRemark) { // TODO remark - await editContactApi({ name: name.trim(), addresses, id: state.id, index: state.index }); + // await editContactApi({ name: name.trim(), remark: remark?.trim(), id: state.id, index: state.index }); appDispatch(fetchContactListAsync()); // navigate('/setting/contacts'); @@ -244,7 +144,7 @@ export default function EditContact() { onOk: () => navigate('/chat'), onCancel: () => navigate('/setting/contacts/view', { state: {} }), }); - message.success('Edit Contact Successful'); // 'Add Contact Successful' + message.success('Edit Contact Successful'); } } catch (e: any) { console.log('onFinish==contact error', e); @@ -253,54 +153,36 @@ export default function EditContact() { setLoading(false); } }, - [ - appDispatch, - editContactApi, - handleCheckAddress, - handleCheckName, - handleCheckRemark, - navigate, - setLoading, - state.id, - state.index, - t, - ], + [appDispatch, handleCheckName, handleCheckRemark, navigate, setLoading, t], ); // go back previous page const handleGoBack = useCallback(() => { - navigate('/setting/contacts/view', { state: state }); - - // navigate('/setting/contacts'); - }, [navigate, state]); - - const handleCloseDrawer = () => { - setNetOpen(false); - }; + if (pathname.includes('/setting/wallet')) { + navigate('/setting/wallet/wallet-name'); + } else { + navigate('/setting/contacts/view', { state: state }); + } + }, [navigate, pathname, state]); const handleCopy = useProfileCopy(); - const { isNotLessThan768 } = useCommonState(); + const headerTitle = useMemo(() => t('Edit Contact'), [t]); - const headerTitle = useMemo(() => t('Edit Contact'), [t]); // t('Add New Contact') return isNotLessThan768 ? ( ) : ( @@ -308,19 +190,15 @@ export default function EditContact() { headerTitle={headerTitle} goBack={handleGoBack} form={form} - isDisable={disable} validName={validName} validRemark={validRemark} state={state} - addressArr={addressArr} + isNameDisable={state.isNameDisable} + isShowRemark={state.isShowRemark} + canSave={canSave} onFinish={onFinish} - handleSelectNetwork={handleSelectNetwork} - handleAddressChange={handleAddressChange} handleInputValueChange={handleInputValueChange} handleInputRemarkChange={handleInputRemarkChange} - isShowDrawer={netOpen} - closeDrawer={handleCloseDrawer} - handleNetworkChange={handleNetworkChange} handleCopy={handleCopy} /> ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/Popup/index.less b/packages/web-extension-did/app/web/pages/Contacts/Popup/index.less index b11df95b52..3da553eccd 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/Popup/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/Popup/index.less @@ -15,5 +15,15 @@ .contacts-body { overflow-y: hidden; + + .contact-item{ + padding-right: 20px; + } + + .adm-index-bar-sidebar { + .adm-index-bar-sidebar-row { + padding-right: 6px; + } + } } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/Popup/index.tsx index 9acd509941..03f6787816 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/Popup/index.tsx @@ -8,6 +8,7 @@ import './index.less'; export default function ContactsPopup({ headerTitle, goBack, + searchPlaceholder, addText, handleAdd, isSearch, @@ -28,7 +29,7 @@ export default function ContactsPopup({ ) } /> - +
    - +
    { + console.log('add'); + }; // TODO btn show logic return isNotLessThan768 ? ( @@ -37,9 +39,8 @@ export default function ViewContact() { addContactText={addContactText} data={state} goBack={goBack} - // TODO 1 2 - handleEdit={() => handleEdit('1', state)} - handleAdd={() => handleAdd('2', state)} + handleEdit={() => handleEdit('1', state)} // TODO add or edit 1 2 + handleAdd={handleAdd} handleChat={() => handleChat(state)} handleCopy={handleCopy} /> @@ -52,9 +53,8 @@ export default function ViewContact() { addContactText={addContactText} data={state} goBack={goBack} - // TODO 1 2 - handleEdit={() => handleEdit('1', state)} - handleAdd={() => handleAdd('2', state)} + handleEdit={() => handleEdit('1', state)} // TODO add or edit 1 2 + handleAdd={handleAdd} handleChat={() => handleChat(state)} handleCopy={handleCopy} /> diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.less index 1720926cd5..1b71a1f83f 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.less @@ -88,18 +88,15 @@ width: 100%; padding: 16px; border-top: 1px solid @border-2; - .form-btn-add { - .@{app-prefix}-form-item { - margin-bottom: 0; - } - .add-btn { - color: @font-5; - font-size: 16px; - line-height: 22px; - height: 48px; - border-radius: 22px; - background-color: @bg-7; - } + margin-bottom: 0 !important; + + .add-btn { + color: @font-5; + font-size: 16px; + line-height: 22px; + height: 48px; + border-radius: 22px; + background-color: @bg-7; } } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.tsx index 0b85404156..cf3d010a90 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/AddContactForm/index.tsx @@ -1,20 +1,18 @@ import { Button, Form, Input, FormProps } from 'antd'; import { useTranslation } from 'react-i18next'; import CustomSvg from 'components/CustomSvg'; -import { CustomAddressItem, ValidData } from 'pages/Contacts/EditContact'; import { useSymbolImages } from '@portkey-wallet/hooks/hooks-ca/useToken'; import './index.less'; +import { CustomAddressItem, ValidData } from 'pages/Contacts/AddContact'; const { Item: FormItem } = Form; export interface IAddContactFormProps extends FormProps { isDisable: boolean; validName: ValidData; - validRemark: ValidData; state: any; addressArr: CustomAddressItem[]; handleInputValueChange: (v: string) => void; - handleInputRemarkChange: (v: string) => void; handleSelectNetwork: (i: number) => void; handleAddressChange: (i: number, value: string) => void; } @@ -25,10 +23,8 @@ export default function AddContactForm({ state, addressArr, validName, - validRemark, onFinish, handleInputValueChange, - handleInputRemarkChange, handleSelectNetwork, handleAddressChange, }: IAddContactFormProps) { @@ -52,17 +48,6 @@ export default function AddContactForm({ maxLength={16} /> - {/* - handleInputRemarkChange(e.target.value)} - maxLength={16} - /> - */} {(fields) => ( @@ -107,15 +92,11 @@ export default function AddContactForm({ )}
    -
    -
    - - - -
    -
    + + + ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.less index 833e70a00c..4debf77647 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsBody/index.less @@ -52,4 +52,15 @@ display: none; } } + + .adm-index-bar-anchor { + .adm-index-bar-anchor-title { + padding-left: 24px; + } + .adm-list { + .adm-list-item { + padding-left: 24px; + } + } + } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.less index ff1d9f190d..bd874aac6a 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ContactsSearchInput/index.less @@ -2,7 +2,7 @@ .contacts-search-input { margin: 0 24px 10px; - width: 312px; + width: calc(100% - 48px); height: 48px; border-radius: 6px; background-color: @bg-11; diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx index a4915ba34b..e80b574c29 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx @@ -5,12 +5,12 @@ import { useDeleteContact } from '@portkey-wallet/hooks/hooks-ca/contact'; import { useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import IdAndAddress from '../IdAndAddress'; -import { ValidData } from 'pages/Contacts/EditContact'; import './index.less'; +import { ValidData } from 'pages/Contacts/AddContact'; const { Item: FormItem } = Form; -interface IEditContactFormProps extends FormProps { +export interface IEditContactFormProps extends FormProps { state: any; validName: ValidData; validRemark?: ValidData; @@ -27,7 +27,7 @@ export default function EditContactForm({ state, validName, validRemark, - isNameDisable = false, + isNameDisable = true, isShowRemark = true, canSave = false, handleInputValueChange, @@ -56,7 +56,11 @@ export default function EditContactForm({ requiredMark={false} onFinish={onFinish}>
    - + handleInputValueChange(e.target.value)} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx index db444aa805..ad09045169 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/NoContacts/index.tsx @@ -1,25 +1,20 @@ import { Button } from 'antd'; import CustomSvg from 'components/CustomSvg'; -import { useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; import './index.less'; +import { useProfileAddNewContact } from 'hooks/useProfile'; export default function NoContacts({ initData }: { initData: Partial }) { const { t } = useTranslation(); - const navigate = useNavigate(); + const handleAdd = useProfileAddNewContact(); return (

    {t('No Contacts')}

    {t("Contacts you've added will appear here")}

    -
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx index 0768237624..3fed588c7f 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx @@ -1,6 +1,5 @@ import { Button } from 'antd'; import './index.less'; -import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import CustomSvg from 'components/CustomSvg'; import { IProfileDetailBodyProps } from 'types/Profile'; import IdAndAddress from '../IdAndAddress'; @@ -73,6 +72,7 @@ export default function ViewContactBody({ />
    + {/* TODO No edit button for strangers */}
    + )} + {typeof props?.data?.status.loading === 'number' && props?.data?.status.loading !== 0 && ( + // TODO progress + <> + )} +
    + )} */} +
    +
    + ); +}; + +export default PhotoMessage; diff --git a/packages/im-ui-web/src/PopoverMenuList/index.less b/packages/im-ui-web/src/PopoverMenuList/index.less new file mode 100644 index 0000000000..6572409e51 --- /dev/null +++ b/packages/im-ui-web/src/PopoverMenuList/index.less @@ -0,0 +1,40 @@ + +@import '../assets/theme/color.less'; +@import '../assets/theme/constants.less'; + +.popover-menu-list { + font-size: 14px; + line-height: 20px; + color: @font-11; + .menu-item { + align-items: center; + height: 40px; + padding: 10px 0; + gap: 8px; + border-bottom: none; + cursor: pointer; + + .icon-left { + svg { + width: 16px; + height: 16px; + } + } + + .menu-item-container { + flex: 1; + max-width: 78px; + word-break: break-all; + .text-overflow(1); + } + + &:hover { + color: @font-9; + svg { + path { + fill: @border-3; + } + } + } + } +} diff --git a/packages/im-ui-web/src/PopoverMenuList/index.tsx b/packages/im-ui-web/src/PopoverMenuList/index.tsx new file mode 100644 index 0000000000..779957a0f8 --- /dev/null +++ b/packages/im-ui-web/src/PopoverMenuList/index.tsx @@ -0,0 +1,37 @@ +import clsx from 'clsx'; +import { ReactNode } from 'react'; +import './index.less'; + +export interface IPopoverMenuListData { + key: number | string; + leftIcon?: ReactNode; + children?: ReactNode; + rightIcon?: ReactNode; + onClick?: (v?: any) => void; + height?: number; + className?: string; +} + +export interface IPopoverMenuListProps { + className?: string; + data?: IPopoverMenuListData[]; +} + +export default function PopoverMenuList(props: IPopoverMenuListProps) { + const { className, data = [] } = props; + return ( +
    + {data.map(item => ( +
    + {item.leftIcon &&
    {item.leftIcon}
    } + {item.children} + {item.rightIcon &&
    {item.rightIcon}
    } +
    + ))} +
    + ); +} diff --git a/packages/im-ui-web/src/StyleProvider/index.tsx b/packages/im-ui-web/src/StyleProvider/index.tsx new file mode 100644 index 0000000000..63640b302d --- /dev/null +++ b/packages/im-ui-web/src/StyleProvider/index.tsx @@ -0,0 +1,12 @@ +import { ConfigProvider } from 'antd'; +import React from 'react'; +import { useEffectOnce } from 'react-use'; + +export default function StyleProvider({ children, prefixCls }: { children: React.ReactNode; prefixCls: string }) { + useEffectOnce(() => { + ConfigProvider.config({ + prefixCls: prefixCls, + }); + }); + return {children}; +} diff --git a/packages/im-ui-web/src/SystemMessage/index.less b/packages/im-ui-web/src/SystemMessage/index.less new file mode 100644 index 0000000000..e971a7f83c --- /dev/null +++ b/packages/im-ui-web/src/SystemMessage/index.less @@ -0,0 +1,15 @@ +@import '../assets/theme/color.less'; + +.portkey-container-system { + justify-content: center; + margin-top: 8px; + font-size: 12px; + line-height: 16px; + color: @font-13; + + .portkey-system-text { + padding: 2px 8px; + background-color: @bg-10; + border-radius: 16px; + } +} diff --git a/packages/im-ui-web/src/SystemMessage/index.tsx b/packages/im-ui-web/src/SystemMessage/index.tsx new file mode 100644 index 0000000000..77d2273efc --- /dev/null +++ b/packages/im-ui-web/src/SystemMessage/index.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import './index.less'; + +import { ISystemMessageProps } from '../type'; + +const SystemMessage: React.FC = props => { + return ( +
    +
    + {props.text} +
    +
    + ); +}; + +export default SystemMessage; diff --git a/packages/im-ui-web/src/TextMessage/index.less b/packages/im-ui-web/src/TextMessage/index.less new file mode 100644 index 0000000000..3fca8ebb1a --- /dev/null +++ b/packages/im-ui-web/src/TextMessage/index.less @@ -0,0 +1,42 @@ +@import '../assets/theme/color.less'; + +.portkey-message-text { + justify-content: flex-start; + margin-top: 8px; + font-size: 16px; + line-height: 24px; + color: @font-11; + &.right { + justify-content: flex-end; + } + .text-body { + position: relative; + flex-direction: column; + padding: 8px 12px; + border-radius: 2px 20px 20px 20px; + background: @bg-22; + max-width: calc(100% - 30px); + &.right { + border-radius: 20px 2px 20px 20px; + background: @bg-27; + } + .text-text { + display: inline-block; + font-size: 15px; + .text-date-hidden { + visibility: hidden; + } + .text-link { + color: @font-9; + } + } + .text-date { + position: absolute; + bottom: 8px; + right: 12px; + font-size: 10px; + line-height: 16px; + color: @font-12; + } + } +} diff --git a/packages/im-ui-web/src/TextMessage/index.tsx b/packages/im-ui-web/src/TextMessage/index.tsx new file mode 100644 index 0000000000..67d7cd81a3 --- /dev/null +++ b/packages/im-ui-web/src/TextMessage/index.tsx @@ -0,0 +1,23 @@ +import React, { useMemo } from 'react'; +import './index.less'; + +import clsx from 'clsx'; +import { ITextMessageProps } from '../type'; +import { formatTime } from '../utils'; + +const TextMessage: React.FC = props => { + const showDate = useMemo(() => (props.dateString ? props.dateString : formatTime(props.date as any)), []); + return ( +
    +
    +
    + {props.text} + {showDate} +
    +
    {showDate}
    +
    +
    + ); +}; + +export default TextMessage; diff --git a/packages/im-ui-web/src/UnreadTip/index.less b/packages/im-ui-web/src/UnreadTip/index.less new file mode 100644 index 0000000000..77529f4c54 --- /dev/null +++ b/packages/im-ui-web/src/UnreadTip/index.less @@ -0,0 +1,17 @@ +@import '../assets/theme/color.less'; + +.portkey-unread-tip { + padding: 0 4px; + width: fit-content; + height: 16px; + min-width: 16px; + color: @font-5; + font-size: 12px; + line-height: 16px; + background-color: @bg-24; + border-radius: 8px; +} +.portkey-unread-tip.muted { + background-color: @bg-15; +} + diff --git a/packages/im-ui-web/src/UnreadTip/index.tsx b/packages/im-ui-web/src/UnreadTip/index.tsx new file mode 100644 index 0000000000..5cabbb180e --- /dev/null +++ b/packages/im-ui-web/src/UnreadTip/index.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import clsx from 'clsx'; +import { ZERO } from '../utils'; +import { IUnreadTipProps } from '../type'; +import './index.less'; + +const UnreadTip: React.FC = ({ unread, muted, bgColorString }) => { + return ( +
    + {ZERO.plus(unread).isGreaterThanOrEqualTo(100) ? '99+' : unread} +
    + ); +}; +export default UnreadTip; diff --git a/packages/im-ui-web/src/assets/emoticonData.json b/packages/im-ui-web/src/assets/emoticonData.json new file mode 100644 index 0000000000..78a3f311eb --- /dev/null +++ b/packages/im-ui-web/src/assets/emoticonData.json @@ -0,0 +1,167 @@ +{ + "grinning": "😀", + "grin": "😁", + "joy": "😂", + "smiley": "😃", + "smile": "😄", + "sweat_smile": "😅", + "laughing": "😆", + "innocent": "😇", + "smiling_imp": "😈", + "wink": "😉", + "blush": "😊", + "yum": "😋", + "relieved": "😌", + "heart_eyes": "😍", + "sunglasses": "😎", + "smirk": "😏", + "neutral_face": "😐", + "expressionless": "😑", + "unamused": "😒", + "sweat": "😓", + "pensive": "😔", + "confused": "😕", + "confounded": "😖", + "kissing": "😗", + "kissing_heart": "😘", + "kissing_smiling_eyes": "😙", + "kissing_closed_eyes": "😚", + "stuck_out_tongue": "😛", + "stuck_out_tongue_winking_eye": "😜", + "stuck_out_tongue_closed_eyes": "😝", + "disappointed": "😞", + "worried": "😟", + "angry": "😠", + "rage": "😡", + "cry": "😢", + "persevere": "😣", + "triumph": "😤", + "disappointed_relieved": "😥", + "frowning": "😦", + "anguished": "😧", + "fearful": "😨", + "weary": "😩", + "sleepy": "😪", + "tired_face": "😫", + "grimacing": "😬", + "sob": "😭", + "open_mouth": "😮", + "hushed": "😯", + "cold_sweat": "😰", + "scream": "😱", + "astonished": "😲", + "flushed": "😳", + "sleeping": "😴", + "dizzy_face": "😵", + "no_mouth": "😶", + "mask": "😷", + "see_no_evil": "🙈", + "speak_no_evil": "🙊", + "relaxed": "☺️", + "pray": "🙏", + "rocket": "🚀", + "helicopter": "🚁", + "steam_locomotive": "🚂", + "railway_car": "🚃", + "bullettrain_side": "🚄", + "heartbeat": "💓", + "broken_heart": "💔", + "crescent_moon": "🌙", + "star2": "🌟", + "sunny": "☀", + "rainbow": "🌈", + "lips": "👄", + "rose": "🌹", + "pill": "💊", + "point_right": "👉", + "punch": "👊", + "wave": "👋", + "ok_hand": "👌", + "+1": "👍", + "-1": "👎", + "clap": "👏", + "muscle": "💪", + "chestnut": "🌰", + "seedling": "🌱", + "evergreen_tree": "🌲", + "deciduous_tree": "🌳", + "palm_tree": "🌴", + "cactus": "🌵", + "tulip": "🌷", + "cherry_blossom": "🌸", + "hibiscus": "🌺", + "sunflower": "🌻", + "blossom": "🌼", + "corn": "🌽", + "ear_of_rice": "🌾", + "herb": "🌿", + "four_leaf_clover": "🍀", + "maple_leaf": "🍁", + "fallen_leaf": "🍂", + "leaves": "🍃", + "mushroom": "🍄", + "tomato": "🍅", + "eggplant": "🍆", + "grapes": "🍇", + "melon": "🍈", + "watermelon": "🍉", + "tangerine": "🍊", + "lemon": "🍋", + "banana": "🍌", + "pineapple": "🍍", + "apple": "🍎", + "green_apple": "🍏", + "pear": "🍐", + "peach": "🍑", + "cherries": "🍒", + "strawberry": "🍓", + "hamburger": "🍔", + "pizza": "🍕", + "meat_on_bone": "🍖", + "poultry_leg": "🍗", + "rice_cracker": "🍘", + "rice_ball": "🍙", + "rice": "🍚", + "curry": "🍛", + "ramen": "🍜", + "spaghetti": "🍝", + "bread": "🍞", + "fries": "🍟", + "sweet_potato": "🍠", + "dango": "🍡", + "oden": "🍢", + "sushi": "🍣", + "fried_shrimp": "🍤", + "fish_cake": "🍥", + "icecream": "🍦", + "shaved_ice": "🍧", + "ice_cream": "🍨", + "doughnut": "🍩", + "cookie": "🍪", + "chocolate_bar": "🍫", + "candy": "🍬", + "lollipop": "🍭", + "custard": "🍮", + "honey_pot": "🍯", + "cake": "🍰", + "bento": "🍱", + "stew": "🍲", + "egg": "🍳", + "fork_and_knife": "🍴", + "tea": "🍵", + "sake": "🍶", + "wine_glass": "🍷", + "cocktail": "🍸", + "tropical_drink": "🍹", + "beer": "🍺", + "beers": "🍻", + "baby_bottle": "🍼", + "ribbon": "🎀", + "gift": "🎁", + "birthday": "🎂", + "jack_o_lantern": "🎃", + "christmas_tree": "🎄", + "star": "⭐️", + "x": "❌", + "o": "⭕️" +} \ No newline at end of file diff --git a/packages/im-ui-web/src/assets/getSvg.js b/packages/im-ui-web/src/assets/getSvg.js new file mode 100644 index 0000000000..a6ddb27854 --- /dev/null +++ b/packages/im-ui-web/src/assets/getSvg.js @@ -0,0 +1,36 @@ +// getSvg.js +// eslint-disable-next-line @typescript-eslint/no-var-requires +var fs = require('fs'); +// eslint-disable-next-line @typescript-eslint/no-var-requires +var path = require('path'); +const svgDir = path.resolve(__dirname, './svgIcon'); + +function readfile(filename) { + return new Promise((resolve, reject) => { + fs.readFile(path.join(svgDir, filename), 'utf8', function (err, data) { + if (err) reject(err); + resolve({ + [filename.slice(0, filename.lastIndexOf('.'))]: data, + }); + }); + }); +} + +// read all files in dir +function readSvgs() { + return new Promise((resolve, reject) => { + fs.readdir(svgDir, function (err, files) { + if (err) reject(err); + Promise.all(files.map(filename => readfile(filename))) + .then(data => { + resolve(data); + }) + .catch(error => reject(error)); + }); + }); +} + +module.exports = { + readfile, + readSvgs, +}; diff --git a/packages/im-ui-web/src/assets/getSvg1.js b/packages/im-ui-web/src/assets/getSvg1.js new file mode 100644 index 0000000000..bacafa8c12 --- /dev/null +++ b/packages/im-ui-web/src/assets/getSvg1.js @@ -0,0 +1,59 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable @typescript-eslint/no-var-requires */ +var fs = require('fs'); +var path = require('path'); +const svgDir = path.resolve(__dirname, './svgIcon'); + +function readfile(filename) { + let nameNeedLength = filename.lastIndexOf('.'); + + return new Promise((resolve, reject) => { + fs.readFile(path.join(svgDir, filename), 'utf8', function (err, data) { + let viewBox = data.match(/viewBox="\d*\s\d*\s\d*\s\d*"/g)[0]; + let svgMatch = data.match(/[width|height]="\d*px"/g); + // data = data.replace(/^($/g, ``); + data = data.replace(/(width|height)="\d*px"/g, ''); + data = data.replace(/xmlns[^>]*/g, ''); + // data = data.replace(/stroke-width/g, 'strokeWidth'); + // data = data.replace(/fill-rule/g, 'fill-Rule'); + + console.log(data, viewBox, svgMatch); + if (err) reject(err); + resolve({ + [filename.slice(0, nameNeedLength)]: data, + }); + }); + }); +} + +function readSvgs() { + return new Promise((resolve, reject) => { + fs.readdir(svgDir, function (err, files) { + if (err) reject(err); + files = files.filter((filename) => { + let nameNeedLength = filename.lastIndexOf('.'); + let fileNameLength = filename.length; + if (filename.slice(nameNeedLength, fileNameLength) !== '.svg') { + return false; + } + return true; + }); + + Promise.all(files.map((filename) => readfile(filename))) + .then((data) => resolve(data)) + .catch((err) => reject(err)); + }); + }); +} + +readSvgs() + .then((data) => { + // console.log('data: ', data); + let svgFile = 'export default ' + JSON.stringify(Object.assign.apply(this, data)); + fs.writeFile(path.resolve(__dirname, './svgs.ts'), svgFile, function (err) { + if (err) throw new Error(err); + }); + }) + .catch((err) => { + throw new Error(err); + }); diff --git a/packages/im-ui-web/src/assets/handleSvg.js b/packages/im-ui-web/src/assets/handleSvg.js new file mode 100644 index 0000000000..ec7dec69b2 --- /dev/null +++ b/packages/im-ui-web/src/assets/handleSvg.js @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const fs = require('fs'); +const path = require('path'); +const { readSvgs } = require('./getSvg'); + +function randomCoding() { + const result = []; + // random generate 10 letters + for (let i = 0; i < 10; i++) { + // random 0 ~ 25 + const ranNum = Math.ceil(Math.random() * 25); + // a-z ASCII 97 ~ 97+25 + result.push(String.fromCharCode(97 + ranNum)); + } + return result.join(''); +} + +const chineseReg = new RegExp('[\\u4E00-\\u9FFF]+', 'g'); + +// handle svgs +readSvgs() + .then(async data => { + const obj = Object.assign.apply(this, data); + delete obj['']; + const svgList = Object.entries(obj); + for (let i = 0, length = svgList.length; i < length; i++) { + const [key, value] = svgList[i]; + const newPath = path.resolve(__dirname, `./svgIcon/${key}.svg`); + let newData = value; + if (chineseReg.test(value)) { + newData = value + // eslint-disable-next-line no-useless-escape + .replace(/<\?xml.*?\?>|<\!--.*?-->|/g, '') + .replace(/id="[^"]+"/g, () => `id="${randomCoding()}"`) + .replace(/[^<]+<\/title>/g, () => `${randomCoding()}`); + } + + await fs.writeFileSync(newPath, newData); + } + }) + .catch(err => { + throw new Error(err); + }); diff --git a/packages/im-ui-web/src/assets/index.ts b/packages/im-ui-web/src/assets/index.ts new file mode 100644 index 0000000000..3d8a1a7017 --- /dev/null +++ b/packages/im-ui-web/src/assets/index.ts @@ -0,0 +1,6 @@ +import emoticonData from './emoticonData.json'; +const emojiList: { name: string; code: string }[] = []; +for (const key in emoticonData) { + emojiList.push({ name: key, code: (emoticonData as { [key: string]: string })[key] }); +} +export { emojiList }; diff --git a/packages/im-ui-web/src/assets/svgIcon/AddContact.svg b/packages/im-ui-web/src/assets/svgIcon/AddContact.svg new file mode 100644 index 0000000000..7553cb2e69 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/AddContact.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Album.svg b/packages/im-ui-web/src/assets/svgIcon/Album.svg new file mode 100644 index 0000000000..75aceb02c2 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Album.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Bookmark.svg b/packages/im-ui-web/src/assets/svgIcon/Bookmark.svg new file mode 100644 index 0000000000..7673d32cb6 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Bookmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Close.svg b/packages/im-ui-web/src/assets/svgIcon/Close.svg new file mode 100644 index 0000000000..5cca998a3e --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Close.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/im-ui-web/src/assets/svgIcon/Copy.svg b/packages/im-ui-web/src/assets/svgIcon/Copy.svg new file mode 100644 index 0000000000..58dd8f179d --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Delete.svg b/packages/im-ui-web/src/assets/svgIcon/Delete.svg new file mode 100644 index 0000000000..b87843baa4 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Emoji.svg b/packages/im-ui-web/src/assets/svgIcon/Emoji.svg new file mode 100644 index 0000000000..009259add3 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Emoji.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/File.svg b/packages/im-ui-web/src/assets/svgIcon/File.svg new file mode 100644 index 0000000000..0ba0c0dbb1 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/File.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/ImgErr.svg b/packages/im-ui-web/src/assets/svgIcon/ImgErr.svg new file mode 100644 index 0000000000..17b519d49a --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/ImgErr.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/LeftArrow.svg b/packages/im-ui-web/src/assets/svgIcon/LeftArrow.svg new file mode 100644 index 0000000000..0b5e14841b --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/LeftArrow.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/im-ui-web/src/assets/svgIcon/More.svg b/packages/im-ui-web/src/assets/svgIcon/More.svg new file mode 100644 index 0000000000..872c38e7d5 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/More.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Mute.svg b/packages/im-ui-web/src/assets/svgIcon/Mute.svg new file mode 100644 index 0000000000..594d81f51c --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Mute.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Pin.svg b/packages/im-ui-web/src/assets/svgIcon/Pin.svg new file mode 100644 index 0000000000..79514e12d1 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Profile.svg b/packages/im-ui-web/src/assets/svgIcon/Profile.svg new file mode 100644 index 0000000000..6a792ac2a6 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Profile.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/Send.svg b/packages/im-ui-web/src/assets/svgIcon/Send.svg new file mode 100644 index 0000000000..97e7383b8d --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/Send.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/UnMute.svg b/packages/im-ui-web/src/assets/svgIcon/UnMute.svg new file mode 100644 index 0000000000..11e8ead422 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/UnMute.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/UnPin.svg b/packages/im-ui-web/src/assets/svgIcon/UnPin.svg new file mode 100644 index 0000000000..90959c88b0 --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/UnPin.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/im-ui-web/src/assets/svgs.ts b/packages/im-ui-web/src/assets/svgs.ts new file mode 100644 index 0000000000..fec24ad3ea --- /dev/null +++ b/packages/im-ui-web/src/assets/svgs.ts @@ -0,0 +1,30 @@ +export default { + AddContact: + '\n\n\n', + Album: + '\n\n\n\n\n\n\n', + Bookmark: + '\n\n\n', + Close: + '\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n', + Copy: '\n\n\n', + Delete: + '\n\n\n', + Emoji: + '\n\n\n\n\n\n', + File: '\n\n\n\n\n\n\n\n\n\n\n\n', + ImgErr: + '\n\n\n\n', + LeftArrow: + '\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n', + More: '\n\n\n', + Mute: '\n\n\n', + Pin: '\n\n\n', + Profile: + '\n\n\n', + Send: '\n\n\n', + UnMute: + '\n\n\n', + UnPin: + '\n\n\n', +}; diff --git a/packages/im-ui-web/src/assets/theme/color.less b/packages/im-ui-web/src/assets/theme/color.less new file mode 100644 index 0000000000..975224e51c --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/color.less @@ -0,0 +1,71 @@ +@primary-color: ~'var(--@{app-prefix}-primary-color)'; +@dark-color: ~'var(--@{app-prefix}-dark-color)'; +@card-bg: ~'var(--@{app-prefix}--card-bg)'; +@gray-bg: ~'var(--@{app-prefix}-gray-bg)'; +@bg1: ~'var(--@{app-prefix}-bg1)'; + +@font-1: #262626; +@font-11: #25272A; +@font-9: #5B8EF4; +@font-12: #b6babf; +@font-13: #515A62; +@font-2: #464b53; +@font-3: #8f97a6; +@font-4: #c7cbd1; +@font-5: #FFFFFF; +@font-6: #52c41a; +@font-7: #ff4d4f; +@font-8: #faad14; +@font-9: #5b8ef4; +@font-10: #252525; +@font-14: #e7383a; +@font-15: #00ab34; +@font-16: #595959; + +@border-2: #DEE2E8; +@border-3: #5B8EF4; +@border-12: #FFFFFF; +@border-1: #c5cbd5; +@border-4: #68aafd; +@border-5: #ff4d4f; +@border-6: #e3ebfa; +@border-7: #e7383a; +@border-8: #515a62; +@border-9: #00b9b0; +@border-10: #f0f2f5; +@border-11:#00AB34; + +@bg-10: #F7F7F9; +@bg-11: #FFFFFF; +@bg-22: #F0F1F4; +@bg-27: #E7F0FC; +@bg-15: #DEE2E8; +@bg-13: #F7F8F9; +@bg-7: #5B8EF4; +@bg-24: #EA4F45; +@bg-26: #B6BABF; +@bg-1: #52c41a; +@bg-2: #edf9e8; +@bg-3: #faad14; +@bg-4: #fef6e7; +@bg-5: #ff4d4f; +@bg-6: #ffeded; +@bg-8: #68aafd; +@bg-9: #eef3fd; +@bg-12: rgb(0 0 0 / 30%); +@bg-14: #2bb6af; +@bg-16: #e7383a; +@bg-17: #c2c2c2; +@bg-18: #c5cbd5; +@bg-19: #515a62; +@bg-20: #f2f4f6; +@bg-21: #00AB34; +@bg-23: #68737E; +@bg-25: #68737E; + +@hover1: @border-4; +@hover2: @border-3; + +@shadow-1: rgba(77, 78, 89, 0.13); + +@disabled1: @bg-10; diff --git a/packages/im-ui-web/src/assets/theme/common-class.less b/packages/im-ui-web/src/assets/theme/common-class.less new file mode 100644 index 0000000000..bbbee880c1 --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/common-class.less @@ -0,0 +1,295 @@ +.min-height-container { + font-family: Roboto-Regular; + min-height: calc(100% - @footer-height); +} +.full-screen-height { + height: 100vh; +} +.width-100-percent { + width: 100%; +} +.flex-row-center { + display: flex; + flex-direction: row; + align-items: center; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex { + display: flex; +} +.flex-1 { + flex: 1; +} +.flex-between-center { + display: flex; + justify-content: space-between; + align-items: center; +} +.flex-between { + display: flex; + justify-content: space-between; +} +.flex-column-center { + display: flex; + flex-direction: column; + align-items: center; +} +.flex-column-between { + display: flex; + flex-direction: column; + justify-content: space-between; +} +.flex-start-center { + display: flex; + align-items: center; + justify-content: start; +} +.cursor-pointer { + cursor: pointer; +} +.margin-auto { + margin: auto; +} +.flex-center { + display: flex; + justify-content: center; + align-items: center; +} +.font-5 { + color: @font-5; +} +.font-1 { + color: @font-1; +} + +.text-center { + text-align: center; +} +.text-right { + text-align: right; +} + +/* set list-style */ +ul, +ol { + list-style: none; +} + +/* set text-decoration */ +a, +a:hover { + text-decoration: none; +} + +/* clear margin & padding */ +body, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +p, +blockquote, +dl, +dt, +dd, +ul, +ol, +li, +pre, +fieldset, +button, +input, +textarea, +th, +td { + /* table elements */ + margin: 0; + padding: 0; + line-height: unset; +} + +.grid-span2 { + grid-column-start: span 2; +} + +.common-page { + width: @page-width; + margin: auto; +} + +.common-content { + width: @page-content; + margin: auto; +} + +.common-content1 { + width: @page-content1; + margin: auto; + padding-top: 16px; + > .title { + line-height: 47px; + font-size: 40px; + margin-bottom: 8px; + text-align: center; + color: @font-11; + } + .description { + line-height: 22px; + font-size: 16px; + margin-bottom: 32px; + text-align: center; + color: @font-11; + } +} + +.common-page1 { + width: @page-width; + margin: auto; +} + +.fix-max-content { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + // overflow-y: scroll; + overflow-y: auto; +} + +.brand-tip { + line-height: 22px; + color: @font-11; + font-size: 16px; +} + +.popup-body { + width: 360px !important; + height: 600px; +} + +.portkey-modal-wrap { + overflow: hidden; +} + +.custom-svg { + width: 16px; + height: 16px; +} + +.min-width-max-height { + min-width: 360px; + height: 100%; +} + +// ========================================================= +// ============ Large screen adaptation [start] ============ +// ========================================================= +.portkey-prompt { + width: 100%; + min-width: @min-width; + min-height: 100%; + height: 100vh; + background-color: @bg-20; + display: flex; + flex-direction: column; + align-items: center; + overflow: overlay; + + .portkey-header { + z-index: 101; + background-color: @bg-20; + margin: 0 auto; + } + + .portkey-body { + height: calc(100vh - 96px); + min-height: 593px; + margin: 0 auto; + } + .portkey-prompt-media(); +} + +.portkey-prompt-media { + // screen >= 1820, contentWidth = 1440 + @media screen and (min-width: @max-screen) { + .portkey-body, + .portkey-header { + max-width: @max-width; + width: @max-width; + + .portkey-header-body { + padding-left: 0; + padding-right: 0; + } + } + } + // 1280 < screen < 1820, maxContentWidth = 1440, marginLeftAndRight = 190 + @media screen and (max-width: 1819px) and (min-width: @mid-screen) { + .portkey-header { + width: calc(100vw - @max-margin * 2); + + .portkey-header-body { + padding-left: 0; + padding-right: 0; + } + } + .portkey-body { + width: calc(100vw - @max-margin * 2); + } + } + // 768 < screen < 1280, maxContentWidth = 1024, marginLeftAndRight = 44 + @media screen and (max-width: 1279px) and (min-width: @min-screen) { + .portkey-header { + max-width: @mid-width; + width: calc(100vw - @min-margin * 2); + .portkey-header-body { + padding-left: 0; + padding-right: 0; + } + } + .portkey-body { + max-width: @mid-width; + width: calc(100vw - @min-margin * 2); + } + } + // screen < 768, maxContentWidth = screen, minContentWidth = 360, paddingLeftAndRight = 24 + @media screen and (max-width: 767px) { + padding-bottom: 0 !important; + .portkey-header { + width: 100vw; + min-width: @min-width; + } + .portkey-body { + width: 100vw; + min-width: @min-width; + min-height: calc(100vh - 72px); + } + } +} + +// This style is suitable for detail pages, such as transaction、asset preview page... +.detail-page-prompt { + width: inherit; + max-width: @detail-width; + min-width: @min-width; + height: calc(100vh - 72px); // height - navHeight + max-height: @max-detail-height; + min-height: @min-detail-height; + + @media screen and (max-width: 767px) { + min-height: calc(100vh - 72px); + } +} +// ========================================================= +// ============= Large screen adaptation [end] ============= +// ========================================================= diff --git a/packages/im-ui-web/src/assets/theme/components.less b/packages/im-ui-web/src/assets/theme/components.less new file mode 100644 index 0000000000..4350b509b1 --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/components.less @@ -0,0 +1,173 @@ +/* button */ +.@{app-prefix}-btn-dangerous.@{app-prefix}-btn-link span { + text-decoration: underline; +} + +.@{app-prefix}-btn { + border-radius: 4px; + width: 100%; + height: auto; + padding: 0; + box-shadow: none; +} + +.@{app-prefix}-btn-default { + color: @font-9; + border-color: @border-3; +} + +.@{app-prefix}-btn-primary { + border-width: 0; + &:hover, + &:focus { + color: white; + background-color: @hover1; + border-color: @border-4; + } + + &:disabled, + &:disabled:hover, + &:disabled:focus, + &:disabled:active { + color: white; + background-color: @bg-7; + border-color: @border-3; + box-shadow: none !important; + opacity: 0.3; + } +} + +/* input */ + +.@{app-prefix}-input, +.@{app-prefix}-input-password { + padding: 12px 16px; + border-radius: 6px; + color: @font-2; + line-height: 22px; + border-color: @border-1; + &:hover { + border-color: @hover2; + } +} + +.@{app-prefix}-input { + font-size: 16px; + + &::placeholder { + font-size: 14px; + line-height: 20px; + color: @font-12; + } +} + +.@{app-prefix}-input-password-icon { + width: 16px; + height: 16px; +} + +.clear-input-border { + border: none !important; + box-shadow: none !important; + &:focus { + box-shadow: none !important; + } +} + +/* form */ +.@{app-prefix}-form { + .@{app-prefix}-form-item-explain { + line-height: 16px; + height: 16px; + margin-top: 4px; + } + .@{app-prefix}-form-item-has-error { + padding-bottom: 24px; + .@{app-prefix}-form-item-control { + & > div:nth-of-type(2) { + line-height: 20px; + height: 20px !important; + & > div:nth-of-type(2) { + height: 16px !important; + } + } + } + } + .@{app-prefix}-input-affix-wrapper-status-error { + border-color: @border-7; + } + + .@{app-prefix}-form-item-explain-error { + color: @font-14; + font-size: 12px; + } + + .@{app-prefix}-form-item { + margin-bottom: 24px; + } + .@{app-prefix}-form-item-label { + line-height: 22px; + font-size: 16px; + padding-bottom: 16px; + color: @font-2; + } + .@{app-prefix}-form-item-label > label { + color: @font-2; + line-height: 22px; + font-size: 16px; + } + .@{app-prefix}-btn-primary { + line-height: 48px; + font-size: 16px; + } +} + +// to change border color of the errored input +.@{app-prefix}-input-status-error:not(.@{app-prefix}-input-disabled):not(.@{app-prefix}-input-borderless).@{app-prefix}-input:hover, +.@{app-prefix}-input-affix-wrapper-status-error:not(.@{app-prefix}-input-affix-wrapper-disabled):not(.@{app-prefix}-input-affix-wrapper-borderless).@{app-prefix}-input-affix-wrapper, +.@{app-prefix}-input-affix-wrapper-status-error:not(.@{app-prefix}-input-affix-wrapper-disabled):not(.@{app-prefix}-input-affix-wrapper-borderless).@{app-prefix}-input-affix-wrapper:hover { + border-color: @border-3 !important; +} + +.@{app-prefix}-input-status-error:not(.@{app-prefix}-input-disabled):not(.@{app-prefix}-input-borderless).@{app-prefix}-input, +.@{app-prefix}-input-affix-wrapper-status-error:not(.@{app-prefix}-input-affix-wrapper-disabled):not(.@{app-prefix}-input-affix-wrapper-borderless).@{app-prefix}-input-affix-wrapper, +.@{app-prefix}-input-affix-wrapper-status-error:not(.@{app-prefix}-input-affix-wrapper-disabled):not(.@{app-prefix}-input-affix-wrapper-borderless).@{app-prefix}-input-affix-wrapper:focus { + border-color: #c5cbd5; +} + +.@{app-prefix}-input-status-error:not(.@{app-prefix}-input-disabled):not(.@{app-prefix}-input-borderless).@{app-prefix}-input:focus, +.@{app-prefix}-input-affix-wrapper-status-error:not(.@{app-prefix}-input-affix-wrapper-disabled):not(.@{app-prefix}-input-affix-wrapper-borderless).@{app-prefix}-input-affix-wrapper-focused { + border-color: @border-3 !important; + box-shadow: 0 0 0 2px rgb(91 142 244 / 20%); +} + +.cross-modal { + border-radius: 8px; + overflow: hidden; + .@{app-prefix}-modal-body { + padding: 0; + .@{app-prefix}-modal-confirm-content { + padding: 24px; + margin: 0; + border-bottom: 1px solid @border-2; + text-align: center; + line-height: 20px; + font-size: 14px; + color: @font-13; + } + .@{app-prefix}-modal-confirm-btns { + padding: 15px 16px 17px; + margin: 0; + display: flex; + gap: 16px; + button { + width: 136px; + height: 48px; + border-radius: 24px; + margin: 0 !important; + line-height: 22px; + font-size: 16px; + } + } + } +} diff --git a/packages/im-ui-web/src/assets/theme/constants.less b/packages/im-ui-web/src/assets/theme/constants.less new file mode 100644 index 0000000000..8eea4f4707 --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/constants.less @@ -0,0 +1,52 @@ +@import './color.less'; + +@page-width: 740px; +@page-width1: 900px; +@page-content: 560px; +@page-content1: 350px; +@footer-height: 70px; +@header-height: 72px; + +// screen +@max-screen: 1820px; +@mid-screen: 1280px; +@min-screen: 768px; +// width +@max-width: 1440px; +@mid-width: 1024px; +@detail-width: 440px; +@min-width: 360px; +// height +@max-detail-height: 884px; // height:860 + padding-bottom:24 +@min-detail-height: 624px; // height:600 + padding-bottom:24 +// margin +@max-margin: 190px; +@min-margin: 44px; + +@box-shadow1: 0px 2px 4px 0px rgba(0, 0, 0, 0.06); +@box-shadow2: 0px 2px 16px 0px rgba(0, 0, 0, 0.08), 0px 4px 8px 0px rgba(0, 0, 0, 0.08); +@box-shadow3: 0px 12px 48px 16px rgba(0, 0, 0, 0.05), 0px 9px 28px 0px rgba(0, 0, 0, 0.08), + 0px 6px 12px -8px rgba(0, 0, 0, 0.12); +@box-shadow4: 0px 9px 28px 8px rgba(0, 0, 0, 0.05), 0px 6px 16px 0px rgba(0, 0, 0, 0.08), + 0px 3px 6px -4px rgba(0, 0, 0, 0.12); +@box-shadow5: 0 3px 10px 0 rgb(77 78 89 / 24%); + +@box-shadow-style: @box-shadow1, @box-shadow2, @box-shadow3, @box-shadow4; + +.text-overflow(@n) { + text-overflow: -o-ellipsis-lastline; + overflow: hidden; + /* stylelint-disable-next-line declaration-block-no-duplicate-properties */ + text-overflow: ellipsis; + /* stylelint-disable-next-line value-no-vendor-prefix */ + display: -webkit-box; + -webkit-box-orient: vertical; + line-clamp: @n; + -webkit-line-clamp: @n; +} + +each(@box-shadow-style, { + .box-shadow-@{index} { + background: extract(@box-shadow-style, @index); + } +}); diff --git a/packages/im-ui-web/src/assets/theme/customer.theme.less b/packages/im-ui-web/src/assets/theme/customer.theme.less new file mode 100644 index 0000000000..544b016df6 --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/customer.theme.less @@ -0,0 +1,5 @@ +@import './color.less'; +@import './constants.less'; +@import './components.less'; +@import './common-class.less'; +@import './modal.less'; \ No newline at end of file diff --git a/packages/im-ui-web/src/assets/theme/customize.less b/packages/im-ui-web/src/assets/theme/customize.less new file mode 100644 index 0000000000..084226d0dc --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/customize.less @@ -0,0 +1,17 @@ +html { + --@{app-prefix}-primary-color: #5b8ef4; + --@{app-prefix}-primary-color-hover: #68aafd; + --@{app-prefix}-error-color: #ce342a; + --@{app-prefix}-dark-color: #000; + --@{app-prefix}-light-color: rgb(217 250 240) 0; + --@{app-prefix}-card-bg: #fff; + --@{app-prefix}-bg1: #fff; + --@{app-prefix}-gray-bg: #f7f7f7; +} + +html[data-theme='dark'] { + --@{app-prefix}-primary-color: red; + --@{app-prefix}-primary-color-hover: red; + --@{app-prefix}-dark-color: rgb(217 250 240) 0; + --@{app-prefix}-light-color: #000; +} diff --git a/packages/im-ui-web/src/assets/theme/modal.less b/packages/im-ui-web/src/assets/theme/modal.less new file mode 100644 index 0000000000..05c69e7f80 --- /dev/null +++ b/packages/im-ui-web/src/assets/theme/modal.less @@ -0,0 +1,126 @@ +@import './color.less'; +.utils-modal { + .@{app-prefix}-modal-content { + border-radius: 10px; + overflow: hidden; + min-width: 500px; + } + .@{app-prefix}-modal-body { + padding: 24px 40px; + .@{app-prefix}-modal-confirm-title { + text-align: center; + font-size: 22px; + padding-bottom: 15px; + } + .@{app-prefix}-modal-confirm-content { + margin: 20px 30px; + margin-top: 40px; + text-align: center; + } + } + .@{app-prefix}-modal-confirm-btns { + width: 100%; + display: flex; + } + .@{app-prefix}-modal-close-x { + line-height: 30px; + margin-top: 24px; + height: 20px; + } +} + +.dapp-modal { + max-width: 500px; + margin: auto; + display: flex; + flex-direction: column; + height: 100vh; + max-height: 100vh; + + .tips-wrap { + display: flex; + flex-direction: column; + gap: 8px; + max-width: 328px; + .primary { + text-align: center; + font-size: 24px; + font-weight: 600; + color: @font-11; + line-height: 28px; + } + .secondary { + line-height: 16px; + font-size: 12px; + text-align: center; + color: @font-13; + } + } + .chain-wrap { + width: fit-content; + margin: 0 auto 8px; + line-break: anywhere; + border-radius: 100px; + border: 1px solid @border-1; + padding: 11px 16px; + display: flex; + align-items: center; + line-height: 16px; + .status { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + margin-right: 6px; + border: 1px solid @border-9; + background-color: @border-9; + font-weight: 500; + color: @font-13; + font-size: 12px; + line-height: 16px; + } + .network-name { + color: @font-11; + } + } + .dapp-info-wrap { + display: flex; + margin: 0 auto 24px; + border-radius: 100px; + line-break: anywhere; + align-items: center; + padding: 9px 16px; + border: 1px solid @border-1; + gap: 8px; + width: fit-content; + max-width: 100%; + img, + svg, + .custom-svg { + width: 24px; + height: 24px; + } + span { + line-height: 20px; + color: @font-13; + font-size: 14px; + } + } + .btn-wrap { + display: flex; + flex-grow: 1; + flex-shrink: 0; + gap: 16px; + width: fit-content; + button { + height: 48px; + width: 156px; + padding: 12px 0; + border-radius: 100px; + span { + font-size: 16px; + line-height: 22px; + } + } + } +} diff --git a/packages/im-ui-web/src/components/CommonModal/index.tsx b/packages/im-ui-web/src/components/CommonModal/index.tsx new file mode 100644 index 0000000000..898634b3fc --- /dev/null +++ b/packages/im-ui-web/src/components/CommonModal/index.tsx @@ -0,0 +1,42 @@ +import { Col, Modal, ModalProps, Row } from 'antd'; +import clsx from 'clsx'; +import { LeftOutlined } from '@ant-design/icons'; +import { ReactNode } from 'react'; +import './styles.less'; + +export interface CommonModalProps extends ModalProps { + className?: string; + leftCallBack?: () => void; + leftElement?: ReactNode; + transitionName?: string; +} + +export default function CommonModal(props: CommonModalProps) { + const { leftCallBack, width, title, leftElement, transitionName } = props; + return ( + + {leftCallBack || leftElement ? ( + + {leftElement || } + + ) : null} + + {title} + + {leftCallBack || leftElement ? : null} + + } + /> + ); +} diff --git a/packages/im-ui-web/src/components/CommonModal/styles.less b/packages/im-ui-web/src/components/CommonModal/styles.less new file mode 100644 index 0000000000..bd89fbfac2 --- /dev/null +++ b/packages/im-ui-web/src/components/CommonModal/styles.less @@ -0,0 +1,47 @@ +@import '../../assets/theme/color.less'; + +.common-modals { + .@{app-prefix}-modal-content { + border-radius: 8px; + box-shadow: none; + } + .@{app-prefix}-modal-header { + border: none; + border-radius: 8px; + padding: 0; + .@{app-prefix}-modal-title { + line-height: 22px; + font-size: 18px; + padding: 24px 24px 16px; + color: @font-10; + font-weight: 400; + } + } + .@{app-prefix}-modal-body { + padding: 0; + line-height: 20px; + font-size: 14px; + .modal-content { + color: @font-13; + padding: 0 24px 24px; + text-align: center; + border-bottom: 1px solid @border-2; + } + .btn-wrapper { + padding: 15px 16px 17px; + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 16px; + line-height: 20px; + button { + border-radius: 24px; + line-height: 48px; + height: 48px; + font-size: 16px; + } + } + } + .@{app-prefix}-btn { + line-height: 48px; + } +} diff --git a/packages/im-ui-web/src/components/CustomSvg/index.tsx b/packages/im-ui-web/src/components/CustomSvg/index.tsx new file mode 100644 index 0000000000..e878d23be7 --- /dev/null +++ b/packages/im-ui-web/src/components/CustomSvg/index.tsx @@ -0,0 +1,20 @@ +import clsx from 'clsx'; +import { CSSProperties } from 'react'; +import svgsList from '../../assets/svgs'; +export default function ({ + type, + className, + ...props +}: { + type: keyof typeof svgsList; + className?: string; + style?: CSSProperties; + onClick?: () => void; +}) { + return ( +
    + ); +} diff --git a/packages/im-ui-web/src/components/LoadMore/index.tsx b/packages/im-ui-web/src/components/LoadMore/index.tsx new file mode 100644 index 0000000000..ba7ea7da0b --- /dev/null +++ b/packages/im-ui-web/src/components/LoadMore/index.tsx @@ -0,0 +1,34 @@ +// import React from 'react'; +import { DotLoading, InfiniteScroll } from 'antd-mobile'; +import { useTranslation } from 'react-i18next'; + +export interface ILoadingMoreProps { + hasMore?: boolean; + loadingText?: string; + noDataText?: string; + className?: string; + loadMore: () => Promise; +} + +export default function LoadMore({ + hasMore = false, + loadingText = 'Loading', + noDataText = '', + className = '', + loadMore, +}: ILoadingMoreProps) { + const { t } = useTranslation(); + + return ( + + {hasMore ? ( + <> + {t(loadingText)} + + + ) : ( + {t(noDataText)} + )} + + ); +} diff --git a/packages/im-ui-web/src/components/PhotoPreview/index.tsx b/packages/im-ui-web/src/components/PhotoPreview/index.tsx new file mode 100644 index 0000000000..6a92ff43c4 --- /dev/null +++ b/packages/im-ui-web/src/components/PhotoPreview/index.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { Image, Space } from 'antd'; + +const src = 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'; + +const App: React.FC = () => { + // or you can download flipped and rotated image + // https://codesandbox.io/s/zi-ding-yi-gong-ju-lan-antd-5-7-0-forked-c9jvmp + const onDownload = () => { + fetch(src) + .then(response => response.blob()) + .then(blob => { + const url = URL.createObjectURL(new Blob([blob])); + const link = document.createElement('a'); + link.href = url; + link.download = 'image.png'; + document.body.appendChild(link); + link.click(); + URL.revokeObjectURL(url); + link.remove(); + }); + }; + + return ( + ( + // + // + // + // + // + // + // + // + // + // ), + // }} + /> + ); +}; diff --git a/packages/im-ui-web/src/index.ts b/packages/im-ui-web/src/index.ts new file mode 100644 index 0000000000..8b38e68ccb --- /dev/null +++ b/packages/im-ui-web/src/index.ts @@ -0,0 +1,26 @@ +import ChatList from './ChatList'; +import TextMessage from './TextMessage'; +import PhotoMessage from './PhotoMessage'; +import Avatar from './Avatar'; +import PopoverMenuList from './PopoverMenuList'; +import SystemMessage from './SystemMessage'; +import MessageItem from './MessageItem'; +import MessageList from './MessageList'; +import InputBar from './InputBar'; +import UnreadTip from './UnreadTip'; +import StyleProvider from './StyleProvider'; + +// export * from './type.d'; +export { + ChatList, + TextMessage, + PhotoMessage, + Avatar, + PopoverMenuList, + SystemMessage, + MessageItem, + MessageList, + InputBar, + UnreadTip, + StyleProvider, +}; diff --git a/packages/im-ui-web/src/type.d.ts b/packages/im-ui-web/src/type.d.ts new file mode 100644 index 0000000000..1ae91f367c --- /dev/null +++ b/packages/im-ui-web/src/type.d.ts @@ -0,0 +1,1106 @@ +import React from 'react'; + +/** + * IChatItemProps Interface + * @prop id The Chat Item's id and required. + * @prop avatar The Chat Item's avatar and required. + * @prop unread The Chat Item's message unread and optional. + * @prop className The Chat Item's component className and optional. + * @prop alt The Chat Item's avatar alt and optional. + * @prop title The Chat Item's title and optional. + * @prop subtitle The Chat Item's subtitle and optional. + * @prop date The Chat Item's message date and optional. + * @prop dateString The Chat Item's message dateString and optional. + * @prop statusColor The Chat Item's statusColor and optional. + * @prop statusText The Chat Item's statusText and optional. + * @prop muted The Chat Item's muted and optional. + * @prop pin The Chat Item's pin and optional. + * @prop showMute The Chat Item's showMute icon and optional. + * @prop showVideoCall The Chat Item's showVideoCall icon and optional. + * @prop onContextMenu The Chat Item's function onContextMenu(event: React.MouseEvent) and optional. + * @prop onClick The Chat Item's function onClick(event: React.MouseEvent) and optional. + * @prop onClickMute The Chat Item's mute icon function onClickMute(event: React.MouseEvent) and optional. + * @prop onClickVideoCall The Chat Item's videoCall icon function onClickVideoCall(event: React.MouseEvent) and optional. + * @prop onDragOver The Chat Item's drag over function and optional. + * @prop onDragEnter The Chat Item's drag enter function and optional. + * @prop onDrop The Chat Item's drop function and optional. + * @prop onDragLeave The Chat Item's drop leave function and optional. + * @prop onDragComponent The Chat Item's drag component and optional. + * @prop letterItem The Chat Item's avatar letterItem and optional. + */ +export interface IChatItemProps { + id: string | number; + avatar?: string; + letterItem?: string; + unread?: number; + className?: string; + alt?: string; + title: string; + subtitle?: string; + date?: Date; + dateString?: string; + statusColor?: string; + statusText?: string; + muted?: boolean; + showMute?: boolean; + pin?: boolean; + showVideoCall?: boolean; + onContextMenu?: React.MouseEventHandler; + onClick?: React.MouseEventHandler; + onClickMute?: React.MouseEventHandler; + onClickPin?: React.MouseEventHandler; + onClickDelete?: React.MouseEventHandler; + onClickVideoCall?: React.MouseEventHandler; + onDragOver?: Function; + onDragEnter?: Function; + onDrop?: Function; + onDragLeave?: Function; + setDragStates?: Function; + onDragComponent?: any; + customStatusComponents?: React.ElementType[]; +} + +/** + * IChatListProps Interface + * @prop id The ChatList's id and required. + * @prop className The Chat List's component className and optional. + * @prop datasource The Chat List's dataSource is a IChatItemProps array and required. + * @prop cmpRef The Chat List's cmpRef and optional. + * @prop onContextMenu The Chat Item's function onContextMenu(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. + * @prop onClick The Chat Item's function onClick(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. + * @prop onClickMute The Chat Item's function onClickMute(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. + * @prop onClickVideoCall The Chat Item's function onClickVideoCall(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. + * @prop onDragOver The Chat Item's drag over function and optional. + * @prop onDragEnter The Chat Item's drag enter function and optional. + * @prop onDrop The Chat Item's drop function and optional. + * @prop onDragLeave The Chat Item's drop leave function and optional. + * @prop onDragComponent The Chat Item's drag component and optional. + */ +export interface IChatListProps { + id: string | number; + className?: string; + dataSource: IChatItemProps[]; + hasMore?: boolean; + cmpRef?: React.Ref; + onContextMenu?: ChatListEvent; + onClick?: ChatListEvent; + onClickMute?: ChatListEvent; + onClickPin?: ChatListEvent; + onClickDelete?: ChatListEvent; + onClickVideoCall?: ChatListEvent; + onDragOver?: Function; + onDragEnter?: Function; + onDrop?: Function; + onDragLeave?: Function; + onDragComponent?: Function; + loadMore: () => Promise; +} + +/** + * ChatListEvent Type + * @param item The ChatListEvent's item is a IChatItemProps. + * @param index The Chat List's index. + * @param event The Chat List's event. + */ +type ChatListEvent = (item: IChatItemProps, index: number, event: React.MouseEvent) => any; + +/** + * + */ +export interface IDefaultProps { + style: { + [key: string]: unknown; + }; + onClick: Function; +} + +/** + * IMessage Interface + * @prop id The Message's id and requried. + * @prop position The Message's position and requried. + * @prop text The Message's text and requried. + * @prop title The Message's title and requried. + * @prop focus The Message's focus and requried. + * @prop date The Message's date and requried. + * @prop dateString The Message's dateString and optional. + * @prop avatar The Message's avatar image and optional. + * @prop titleColor The Message's titleColor and required. + * @prop forwarded The Message's forwarded and required. + * @prop replyButton The Message's replyButton icon and required. + * @prop removeButton The Message's removeButton icon and required. + * @prop status The Message's status icon and required. + * @prop statusTitle The Message's statusTitle and required. + * @prop notch The Message's notch and required. + * @prop copiableDate The Message's copiableDate and optional. + * @prop retracted The Message's retracted and required. + * @prop className The Message's className and optional. + * @prop letterItem The Message's letterItem is a string and optional. + * @prop reply The Message's reply and optional. + */ +export interface IMessage { + id: string | number; + position: string; + text: string; + title: string; + focus?: boolean; + date: number | Date; + dateString?: string; + avatar?: string; + titleColor?: string; + forwarded?: boolean; + replyButton?: boolean; + removeButton?: boolean; + status?: 'waiting' | 'sent' | 'received' | 'read'; + statusTitle?: string; + notch?: boolean; + copiableDate?: boolean; + retracted?: boolean; + className?: string; + letterItem?: string; + reply?: IReplyMessage | any; + type: string; +} + +/** + * IPhotoMessage Interface + * @prop type The Photo Message's type is "photo" and required. + * @prop width The Photo Message's width and optional. + * @prop height The Photo Message's height and optional. + * @prop uri The Photo Message's uri and required. + * @prop alt The Photo Message's alt and optional. + */ +export interface IPhotoMessage extends IMessage { + data?: { + status?: IMessageDataStatus; + uri: string; + width?: number; + height?: number; + name?: string; + extension?: string; + size?: number; + id?: string; + alt?: string; + }; +} + +/** + * IPhotoMessageProps Interface + * @prop type The Photo Message's type is "photo" and required. + * @prop message The Photo Message's message is a IPhotoMessage and required. + * @prop onDownload The Photo Message's function onDownload(event: React.MouseEvent) and optional. + * @prop onOpen The Photo Message's function onOpen(event: React.MouseEvent) and optional. + * @prop onLoad The Photo Message's function onLoad(event: React.MouseEvent) and optional. + * @prop onError The Photo Message's function onError(event: React.MouseEvent) and optional. + */ +export interface IPhotoMessageProps extends IPhotoMessage { + onDownload?: React.MouseEventHandler; + onOpen?: React.MouseEventHandler; + onLoad?: React.ReactEventHandler; + onPhotoError?: React.ReactEventHandler; +} + +/** + * IPhotoPreviewMessage Interface + * @prop uri The Photo Preview's uri and required. + * @prop alt The Photo Preview's alt and optional. + */ +export interface IPhotoPreview { + uri: string; + alt?: string; +} + +/** + * IPhotoPreviewMessageProps Interface + * @prop onClose The Photo Preview's function onClose(event: React.MouseEvent) and required. + */ +export interface IPhotoPreviewProps extends IPhotoPreview { + onClose: () => void; +} + +/** + * IReplyMessage Interface extends IMessage + * @prop type The Reply Message's type is "reply" and required. + * @prop photoURL The Reply Message's photoURL and optional. + * @prop message The Reply Message's message and optional. + */ +export interface IReplyMessage extends IMessage { + message: string; + photoURL: string; +} + +/** + * IReplyMessageProps Interface + * @prop type The Reply Message's type is "reply" and required. + * @prop message The Reply Message's message is a IReplyMessage and required. + * @prop onClick The Reply Message's function onClick(event: React.MouseEvent) and optional. + */ +export interface IReplyMessageProps extends IReplyMessage { + onClick?: React.MouseEventHandler; +} + +/** + * IMeetingMessage Interface extens IMessage + * @prop type The Meeting Message's type is "meeting" and required. + * @prop message The Meeting Message's message and optional. + * @prop event The Meeting Message's event and optional. + * @prop record The Meeting Message's record and optional. + */ +export interface IMeetingMessage extends IMessage { + message?: string; + event?: { + title?: string; + avatars?: IAvatarProps[]; + avatarsLimit?: any; + }; + record?: { + avatar: string; + title?: string; + savedBy?: string; + time?: string; + }; +} + +/** + * IMeetingMessageProps Interface + * @prop type The Meeting Message's type is "meeting" and required. + * @prop subject The Meeting Message's subject and optional. + * @prop title The Meeting Message's title and optional. + * @prop date The Meeting Message's date and optional. + * @prop dateString The Meeting Message's dateString and optional. + * @prop collapseTitle The Meeting Message's collapseTitle and optional. + * @prop participants The Meeting Message's participants and optional. + * @prop more The Meeting Message's more and optional. + * @prop message The Meeting Message's message is a IMeetingMessage and required. + * @prop dataSource The Meeting Message's dataSource is a IMeetingMessage array and optional. + * @prop participantsLimit The Meeting Message's participantsLimit and optional. + * @prop onclick The Meeting Message's function onclick(event: React.MouseEvent) and optional. + * @prop onMeetingTitleClick The Meeting Message's function onMeetingTitleClick(item: IMeetingMessage, index: number, event: React.MouseEvent) and optional. + * @prop onMeetingVideoLinkClick The Meeting Message's function onMeetingVideoLinkClick(item: IMeetingMessage, index: number, event: React.MouseEvent) and optional. + * @prop onMeetingMoreSelect The Meeting Message's function onMeetingMoreSelect and optional. + */ +export interface IMeetingMessageProps extends IMeetingMessage { + subject?: string; + dateString?: string; + collapseTitle?: string; + participants?: Array<{ + id?: number | string; + title?: string; + }>; + moreItems?: Array<{ + text?: string; + icon?: { + component?: any; + float?: string; + color?: string; + size?: number; + }; + }>; + dataSource?: IMeetingMessage[]; + participantsLimit?: number; + onClick?: React.MouseEventHandler; + onMeetingTitleClick?: MeetingMessageEvent; + onMeetingVideoLinkClick?: MeetingMessageEvent; + onMeetingMoreSelect?: Function; +} + +/** + * IVideoMessage Interface + * @prop type The Video Message's type is "video" and required. + * @prop videoURL The Video Message's videoURL and required. + * @prop uri The Video Message's uri and required. + * @prop width The Video Message's width and optional. + * @prop height The Video Message's height and optional. + * @prop alt The Video Message's alt and optional. + */ +export interface IVideoMessage extends IMessage { + controlsList: string; + data: { + videoURL?: string; + thumbnailURL?: string; + width?: number; + height?: number; + name?: string; + extension?: string; + size?: string; + alt?: string; + id?: string; + uri?: string; + status?: IMessageDataStatus; + }; +} + +/** + * IVideoMessageProps Interface + * @prop type The Video Message's type is "video" and required. + * @prop message The Video Message's message is a IVideoMessage and required. + * @prop onDownload The Video Message's function onDownload(event: React.MouseEvent) and optional. + * @prop onOpen The Video Message's function onOpen(event: React.MouseEvent) and optional. + * @prop onLoad The Video Message's function onLoad(event: React.MouseEvent) and optional. + * @prop onPhotoError The Video Message's function onPhotoError(event: React.SyntheticEvent) and optional. + */ +export interface IVideoMessageProps extends IVideoMessage { + onDownload?: React.MouseEventHandler; + onOpen?: React.MouseEventHandler; + onLoad?: React.ReactEventHandler; + onPhotoError?: React.ReactEventHandler; +} + +/** + * ISystemMessage Interface extends IMessage + * @prop type The System Message's type is "system" and required. + * @prop text The System Message's text and requried. + */ +export interface ISystemMessage extends IMessage { + text: string; +} + +/** + * ISystemMessageProps Interface + * @prop type The System Message's type is "system" and required. + * @prop message The System Message's message is ISystemMessage and required. + * @prop className The System Message's className and optional. + */ +export interface ISystemMessageProps extends ISystemMessage { + className?: string; +} + +/** + * IAudioMessage Interface extends IMessage + * @prop type The Audio Message's type is "audio" and required. + * @prop audioURL The Audio Message's audio url and required. + * @prop audioType The Audio Message's audio type and optional. + * @prop controlsList The Audio Message's controls list and optional. + */ +export interface IAudioMessage extends IMessage { + data: { + audioURL?: string; + extension?: string; + name?: string; + size?: string; + duration?: number; + id?: string; + audioType?: 'audio/mp3' | string; + controlsList?: string; + }; +} + +/** + * IAudioMessageProps Interface + * @prop type The Audio Message's type is "audio" and required. + * @prop message The Audio Message's message is a IAudioMessage and required. + * @prop audioProps The Audio Message's audioProps and optional. + * @prop customStyle The Audio Message's customStyle and optional. + * @prop onOpen The Audio Message's function onOpen(event: React.MouseEvent) and optional. + * @prop onDownload The Audio Message's function onDownload(event: React.MouseEvent) and optional. + * @prop onLoad The Audio Message's function onLoad(event: React.SyntheticEvent) and optional. + */ +export interface IAudioMessageProps extends IAudioMessage { + audioProps?: { + [key: string]: unknown; + }; + customStyle?: any; + onOpen?: React.MouseEventHandler; + onDownload?: React.MouseEventHandler; + onLoad?: React.ReactEventHandler; +} + +/** + * IFileMessage Interface + * @prop type The File Message's type is "file" and required. + * @prop size The File Message's size and optional. + */ +export interface IFileMessage extends IMessage { + data: { + name?: string; + extension?: string; + size?: string; + id?: string; + uri?: string; + status?: IMessageDataStatus; + }; +} + +/** + * IFileMessageProps Interface + * @prop type The File Message's type is "file" and required. + * @prop message The File Message's message is a IFileMessage and required. + * @prop text The File Message's text and optional. + * @prop onDownload The File Message's function onDownload and optional. + * @prop onOpen The File Message's function onOpen(event: React.MouseEvent) and optional. + */ +export interface IFileMessageProps extends IFileMessage { + onDownload?: Function; + onOpen?: React.MouseEventHandler; +} + +/** + * ILocationMessage Interface + * @prop type The Location Message's type is "location" and required. + * @prop latitude The Location Message's latitude and required. + * @prop longitude The Location Message's longitude and required. + * @prop staticURL The Location Message's static url and required. + * @prop mapURL The Location Message's map url and optional. + */ +export interface ILocationMessage extends IMessage { + data: { + latitude: string; + longitude: string; + staticURL: string; + mapURL?: string; + }; +} + +/** + * ILocationMessageProps Interface + * @prop type The Location Message's type is "location" and required. + * @prop message The Location Message's message is a ILocationMessage and required. + * @prop marker The Location Message's marker and required. + * @prop zoom The Location Message's zoom and required. + * @prop apiKey The Location Message's api key and required. + * @prop className The Location Message's className and optional. + * @prop text The Location Message's text and optional. + * @prop src The Location Message's source and optional. + * @prop target The Location Message's target and optional. + * @prop href The Location Message's href and optional. + * @prop onOpen The Location Message's function onOpen(event: React.MouseEvent) and optional. + * @prop onError The Location Message's function onError(event: React.SyntheticEvent) and optional. + */ +export interface ILocationMessageProps extends ILocationMessage { + markerColor: string; + zoom: string; + apiKey: string; + className?: string; + src?: string; + target?: string; + href?: string; + onOpen?: React.MouseEventHandler; + onError?: React.ReactEventHandler; +} + +/** + * ISpotifyMessage Interface extends IMessage + * @prop type The Spotify Message's type is "spotify" and required. + * @prop uri The Spotify Message's uri and required. + * @prop theme The Spotify Message's theme and optional. + * @prop view The Spotify Message's view and optional. + * @prop width The Spotify Message's width and optional. + * @prop height The Spotify Message's height and optional. + * @prop text The Spotify Message's text and optional. + */ +export interface ISpotifyMessage extends IMessage { + uri: string; + theme?: string; + view?: string; + width?: number | string; + height?: number | string; +} + +/** + * ISpotifyMessageProps Interface + * @prop type The Spotify Message's type is "spotify" and required. + * @prop message The Spotify Message's message is a ISpotifyMessage and required. + */ +export interface ISpotifyMessageProps extends ISpotifyMessage {} + +/** + * IMessageBoxProps Interface + * @prop data The Message Box'es data is a MessageType and required. + * @prop onMessageFocused The Message Box'es onMessageFocused and optional. + * @prop renderAddCmp The Message Box'es renderAddCmp is a component and optional. + * @prop onClick The Message Box'es function onClick(event: React.MouseEvent) and optional. + * @prop onOpen The Message Box'es function onOpen(event: React.MouseEvent) and optional. + * @prop onPhotoError The Message Box'es function onPhotoError(event: React.MouseEvent) and optional. + * @prop onContextMenu The Message Box'es function onContextMenu(event: React.MouseEvent) and optional. + * @prop onForwardClick The Message Box'es function onForwardClick(event: React.MouseEvent) and optional. + * @prop onReplyClick The Message Box'es function onReplyClick(event: React.MouseEvent) and optional. + * @prop onRemoveMessageClick The Message Box'es function onRemoveMessageClick(event: React.MouseEvent) and optional. + * @prop onTitleClick The Message Box'es function onTitleClick(event: React.MouseEvent) and optional. + * @prop onMeetingMessageClick The Message Box'es function onMeetingMessageClick(event: React.MouseEvent) and optional. + * @prop onDownload The Message Box'es function onDownload(event: React.MouseEvent) and optional. + * @prop onMeetingMoreSelect The Message Box'es function onMeetingMoreSelect(event: React.MouseEvent) and optional. + * @prop onMeetingLinkClick The Message Box'es function onMeetingLinkClick(event: React.MouseEvent) and optional. + * @prop onMeetingTitleClick The Message Box'es function onMeetingTitleClick(event: React.MouseEvent) and optional. + * @prop onMeetingVideoLinkClick The Message Box'es function onMeetingVideoLinkClick(event: React.MouseEvent) and optional. + */ +export interface IMessageBoxProps { + onMessageFocused?: any; + renderAddCmp?: JSX.Element | (() => JSX.Element); + onClick?: React.MouseEventHandler; + onOpen?: React.MouseEventHandler; + onPhotoError?: React.MouseEventHandler; + onContextMenu?: React.MouseEventHandler; + onForwardClick?: React.MouseEventHandler; + onReplyClick?: React.MouseEventHandler; + onRemoveMessageClick?: React.MouseEventHandler; + onTitleClick?: React.MouseEventHandler; + onReplyMessageClick?: React.MouseEventHandler; + onMeetingMessageClick?: React.MouseEventHandler; + onDownload?: React.MouseEventHandler; + onMeetingMoreSelect?: React.MouseEventHandler; + onMeetingLinkClick?: React.MouseEventHandler; + onMeetingTitleClick?: React.MouseEventHandler; + onMeetingVideoLinkClick?: React.MouseEventHandler; + styles?: React.CSSProperties; + notchStyle?: React.CSSProperties; +} + +/** + * IMessageListProps Interface + * @prop className The Message List's className and optional. + * @prop customProps The Message List's customProps and optional. + * @prop children The Message List's children and optional. + * @prop referance The Message List's referance is a ref and required. + * @prop datasource The Message List's datasource is IMessageBoxProps and required. + * @prop lockable The Message List's lockable and required. + * @prop toBottomHeight The Message List's to bottom height and optional. + * @prop down button The Message List's down button and required. + * @prop downButtonBadge The Message List's down button badge and required. + * @prop sendMessagePreview The Message List's send message preview and required. + * @prop onScroll The Message List's function onScroll(event: React.UIEvent) and optional. + * @prop onContextMenu The Message List's function onContextMenu(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onDownButtonClick The Message List's onDownButtonClick is a ref and optional. + * @prop onOpen The Message List's function onOpen(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onDownload The Message List's function onDownload(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onPhotoError The Message List's function onPhotoError(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onMeetingMoreSelect The Message List's function onMeetingMoreSelect(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onMessageFocused The Message List's function onMessageFocused(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onClick The Message List's function onClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onForwardClick The Message List's function onForwardClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onReplyClick The Message List's function onReplyClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onReplyMessageClick The Message List's function onReplyMessageClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onTitleClick The Message List's function onTitleClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onRemoveMessageClick The Message List's function onRemoveMessageClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onMeetingMessageClick The Message List's function onMeetingMessageClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + * @prop onMeetingTitleClick The MessageList's function onMeetingTitleClick(event: React.MouseEvent) and optional. + * @prop onMeetingVideoLinkClick The MessageList's function onMeetingVideoLinkClick(event: React.MouseEvent) and optional. + * @prop onMeetingLinkClick The Message List's function onMeetingLinkClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. + */ +export interface IMessageListProps { + className?: string; + customProps?: { + [key: string]: unknown; + }; + children?: React.ReactNode; + isShowChild?: boolean; + referance: any; + dataSource: MessageType[]; + actionButtons?: MeetingLinkActionButtons[]; + lockable: boolean; + toBottomHeight?: String | number; + downButton?: boolean; + downButtonBadge?: number; + sendMessagePreview?: boolean; + onScroll?: React.UIEventHandler; + onContextMenu?: MessageListEvent; + onDownButtonClick?: React.RefObject; + onOpen?: MessageListEvent; + onDownload?: MessageListEvent; + onPhotoError?: MessageListEvent; + onMeetingMoreSelect?: MessageListEvent; + onMessageFocused?: MessageListEvent; + onClick?: MessageListEvent; + onForwardClick?: MessageListEvent; + onReplyClick?: MessageListEvent; + onReplyMessageClick?: MessageListEvent; + onTitleClick?: MessageListEvent; + onRemoveMessageClick?: MessageListEvent; + onMeetingMessageClick?: MessageListEvent; + onMeetingTitleClick?: React.MouseEventHandler; + onMeetingVideoLinkClick?: React.MouseEventHandler; + onMeetingLinkClick?: MessageListEvent; + messageBoxStyles?: React.CSSProperties; + notchStyle?: React.CSSProperties; +} + +/** + * MessageListEvent Type + * @param item The MessageListEvent's item is a IMessageBoxProps. + * @param index The MessageListEvent's index. + * @param event The MessageListEvent's event. + */ +export type MessageListEvent = (item: MessageType, index: number, event: React.MouseEvent) => any; + +/** + * IProgressOptions Interface + * @prop state The Progress Options's state is a object. + */ +export interface IProgressOptions { + state?: { + color?: string; + width?: string; + }; +} + +/** + * IMeetingLinkMessage Interface extends IMessage + * @prop meetingID The Meeting Link Message's meeting id and optional. + * @prop title The Meeting Link Message's title and optional. + */ +export interface IMeetingLinkMessage extends IMessage { + meetingID?: string; +} + +export type TActionButton = React.FunctionComponent; + +export interface MeetingLinkActionButtons { + // return meeting id + onClickButton: (id: string) => void; + Component: TActionButton; +} + +/** + * IMeetingLinkMessageProps Interface + * @prop type The Meeting Link Message's type is "meetingLink" and required. + * @prop message The Meeting Link Message's message is a IMeetingLinkMessage and required. + * @prop onMeetingLinkClick The Meeting Link Message's function onMeetingLinkClick(event: React.MouseEvent) and optional. + * @prop onMeetingMoreSelect The Meeting More Select Message's function onMeetingMoreSelect(event: React.MouseEvent) and optional. + */ +export interface IMeetingLinkMessageProps extends IMeetingLinkMessage { + actionButtons?: MeetingLinkActionButtons[]; +} + +/** + * MeetingMessageEvent Type + * @param item The MessageListEvent's item is a IMeetingMessage. + * @param index The MessageListEvent's index. + * @param event The MessageListEvent's event. + */ +type MeetingMessageEvent = (item: IMeetingMessage, index: number, event: React.MouseEvent) => any; + +/** + * ITextMessage Interface extends IMessage + * @prop type The Text Message's type is "text" and required. + */ +export interface ITextMessage extends IMessage {} + +/** + * ITextMessageProps Interface + * @prop type The Text Message's type is "text" and required. + * @prop message The Text Message's message is a ITextMessage and required. + * @prop dateString The Text Message's dateString and optional. + */ +export interface ITextMessageProps extends ITextMessage { + dateString?: string; + // copyClipboard: function; +} + +export interface IMeetingListProps { + cmpRef?: any; + className?: string; + dataSource?: IMeetingItemProps[]; + onClick?: MeetingListEvent; + onMeetingClick?: MeetingListEvent; + onShareClick?: MeetingListEvent; + onCloseClick?: MeetingListEvent; + onContextMenu?: MeetingListEvent; +} + +/** + * MeetingListEvent Type + * @param item The MessageListEvent's item is a IMeetingItemProps. + * @param index The MessageListEvent's index. + * @param event The MessageListEvent's event. + */ +type MeetingListEvent = (item: IMeetingItemProps, index: number, event: React.MouseEvent) => any; + +/** + * IMeetingItemProps Interface + * @prop id The Meeting Item's id and required. + * @prop closable The Meeting Item's closable and optional. + * @prop date The Meeting Item's date and optional. + * @prop subject The Meeting Item's subject and optional. + * @prop subjectLimit The Meeting Item's subject limit and optional. + * @prop alt The Meeting Item's alt and optional. + * @prop title The Meeting Item's title and optional. + * @prop subtitle The Meeting Item's subtitle and optional. + * @prop classname The Meeting Item's classname and optional. + * @prop dateString The Meeting Item's date string and optional. + * @prop avatarLimit The Meeting Item's avatar limit and optional. + * @prop avatars The Meeting Item's avatars array and optional. + * @prop audioMuted The Meeting Item's audio muted and optional. + * @prop audioSource The Meeting Item's audio source and optional. + * @prop onClick The Meeting Item's function onClick(event: React.MouseEvent) and optional. + * @prop onMeetingClick The Meeting Item's function onMeetingClick(event: React.MouseEvent) and optional. + * @prop onShareClick The Meeting Item's function onShareClick(event: React.MouseEvent) and optional. + * @prop onContextMenu The Meeting Item's function onContextMenu(event: React.MouseEvent) and optional. + * @prop onCloseClick The Meeting Item's function onCloseClick(event: React.MouseEvent) and optional. + */ +export interface IMeetingItemProps { + id: string | number; + closable?: boolean; + date?: any; + subject?: string; + subjectLimit?: number; + alt?: string; + title?: string; + subtitle?: string; + className?: string; + dateString?: string; + avatarLimit?: number; + avatars?: IAvatarProps[]; + audioMuted?: boolean; + audioSource?: string; + onClick?: React.MouseEventHandler; + onMeetingClick?: React.MouseEventHandler; + onShareClick?: React.MouseEventHandler; + onContextMenu?: React.MouseEventHandler; + onCloseClick?: React.MouseEventHandler; +} + +/** + * IInputProps Interface + * @prop autofocus The Input's autofocus and optional. + * @prop referance The Input's referance is a ref and optional. + * @prop clear The Input's clear and optional. + * @prop maxlength The Input's maxlength and optional. + * @prop maxHeight The Input's maxheight and optional. + * @prop onMaxLengthExceed The Input's onMaxLengthExceed function and optional. + * @prop onChange The Input's onChange function and optional. + * @prop multiline The Input's multiline and optional. + * @prop autoHeight The Input's autoHeight and optional. + * @prop minHeight The Input's minheight and optional. + * @prop className The Input's classname and optional. + * @prop leftButtons The Input's leftbuttons is a component and optional. + * @prop rightButtons The Input's rightbuttons is a component and optional. + * @prop type The Input's type and optional. + * @prop placeholder The Input's placeholder and optional. + * @prop defaultValue The Input's default value and optional. + * @prop inputStyle The Input's input style and optional. + * @prop onCopy The Input's function onCopy(event: React.ClipboardEvent) and optional. + * @prop onCut The Input's function onCut(event: React.ClipboardEvent) and optional. + * @prop onPaste The Input's function onPaste(event: React.ClipboardEvent) and optional. + * @prop onBlur The Input's function onBlur(event: React.FocusEvent) and optional. + * @prop onFocus The Input's function onFocus(event: React.FocusEvent) and optional. + * @prop onSelect The Input's function onSelect(event: React.SyntheticEvent) and optional. + * @prop onSubmit The Input's function onSubmit(event: React.FormEvent) and optional. + * @prop onReset The Input's function onReset(event: React.FormEvent) and optional. + * @prop onKeyDown The Input's function onKeyDown(event: React.KeyboardEvent) and optional. + * @prop onKeyPress The Input's function onKeyPress(event: React.KeyboardEvent) and optional. + * @prop onKeyUp The Input's function onKeyUp(event: React.KeyboardEvent) and optional. + */ +export interface IInputProps { + autofocus?: boolean; + referance?: any; // sor ve 46.satır + clear?: Function; + maxlength?: number; + maxHeight: number; + onMaxLengthExceed?: Function; + onChange?: Function; + multiline?: boolean; + autoHeight?: boolean; + minHeight?: number; + className?: string; + leftButtons?: React.ReactNode; + rightButtons?: React.ReactNode; + type?: React.HTMLInputTypeAttribute; + placeholder?: string; + defaultValue?: string; + inputStyle?: Object; + value?: string; + onCopy?: React.ClipboardEventHandler; + onCut?: React.ClipboardEventHandler; + onPaste?: React.ClipboardEventHandler; + onBlur?: React.FocusEventHandler; + onFocus?: React.FocusEventHandler; + onSelect?: React.ReactEventHandler; + onSubmit?: React.FormEventHandler; + onReset?: React.FormEventHandler; + onKeyDown?: React.KeyboardEventHandler; + onKeyPress?: React.KeyboardEventHandler; + onKeyUp?: React.KeyboardEventHandler; +} + +/** + * IMessageDataStatus Interface + * @prop error The File Message Data Status's error and optional. + * @prop download The File Message Data Status's download function and optional. + * @prop click The File Message Data Status's click function and optional. + * @prop loading The File Message Data Status's loading and optional. + */ +export interface IMessageDataStatus { + autoDownload?: boolean; + error?: boolean; + download?: Function | boolean; + click?: Function | boolean; + loading?: boolean | number; +} + +/** + * IDropdownProps Interface + * @prop className The Dropdown's className and optional. + * @prop buttonProps The Dropdown's button props and optional. + * @prop animationType The Dropdown's animation type and optional. + * @prop animationPosition The Dropdown's animation position and optional. + * @prop title The Dropdown's title and optional. + * @prop items The Dropdown's items is a IDropdownItemType array and required. + * @prop onSelect The Dropdown's onSelect function and optional. + * @prop style The Dropdown's style is an object containing color, borderColor and optional. + */ +export interface IDropdownProps { + className?: string; + buttonProps?: Object; + animationType?: string; + animationPosition?: string; + title?: string; + items: IDropdownItemType[]; + onSelect: Function; + style?: { + color?: string; + borderColor?: string; + }; +} + +/** + * ICircleProps Interface + * @prop animate The Circle's animation and required. + * @prop progressOptions The Circle's progress options and optional. + * @prop className The Circle's className and optional. + */ +export interface ICircleProps { + animate: number; + progressOptions?: Object; + className?: string; +} + +/** + * IButtonProps Interface + * @prop title The Button's title and optional. + * @prop text The Button's text and optional. + * @prop buttonRef The Button's ref and optional. + * @prop type The Button's type and optional. + * @prop className The Button's className and optional. + * @prop backgroundColor The Button's background color and optional. + * @prop color The Button's color and optional. + * @prop disabled The Button's disabled and optional. + * @prop onClick The Button's onClick function and optional. + * @prop icon The Button's icon is a IButtonIcon and optional. + */ +export interface IButtonProps { + title?: string; + text?: string; + buttonRef?: React.RefObject; + type?: string; + className?: string; + backgroundColor?: string; + color?: string; + disabled?: boolean; + onClick?: React.MouseEventHandler; + icon?: IButtonIcon; +} + +/** + * IButtonIcon Interface + * @prop float The Button Icon's float and optional. + * @prop size The Button Icon's size and optional. + * @prop components The Button Icon's components and optional. + */ +export interface IButtonIcon { + float?: any; + size?: number; + component?: React.ReactChild; +} + +/** + * IDropDownItemType Type + * @type IDropdown + * @type string + */ +type IDropdownItemType = IDropdownItem | string; + +/** + * IDropdownItem Interface + * @prop icon The Dropdown Item's icon and optional. + * @prop text The Dropdown Item's text and optional. + */ +export interface IDropdownItem { + icon?: IDropdownItemIcon; + text?: string; +} + +/** + * IDropdownItemIcon Interface + * @prop float The Dropdown Item Icon's float and optional. + * @prop color The Dropdown Item Icon's color and optional. + * @prop size The Dropdown Item Icon's size and optional. + * @prop className The Dropdown Item Icon's className and optional. + * @prop component The Dropdown Item Icon's component and optional. + */ +export interface IDropdownItemIcon { + float?: any; + color?: string; + size?: number; + className?: string; + component?: React.ReactChild; +} + +/** + * ISideBarProps Interface + * @type type The Side Bar's type and optional. + * @type data The Side Bar's data is ISideBar and optional. + */ +export interface ISideBarProps extends ISideBar { + type?: string; + data: ISideBar; +} + +/** + * ISideBar Interface + * @prop top The Side Bar's top is a component and optional. + * @prop center The Side Bar's center is a component and optional. + * @prop bottom The Side Bar's bottom is a component and optional. + * @prop className The Side Bar's className and optional. + */ +export interface ISideBar { + top?: any; + center?: any; + bottom?: any; + className?: string; +} + +/** + * IPopup Interface + * @prop show The Popup's show and optional. + * @prop header The Popup's header and optional. + * @prop text The Popup's text and optional. + * @prop footerButtons The Popup's footer buttons array and optional. + * @prop headerButtons The Popup's header buttons array and optional. + * @prop renderHeader The Popup's renderHeader function and optional. + * @prop renderContent The Popup's renderContent function and optional. + * @prop renderFooter The Popup's renderFooter function and optional. + * @prop color The Popup's color and optional. + */ +export interface IPopup { + show?: boolean; + header?: string; + text?: string; + footerButtons?: Array<{ + color?: string; + backgroundColor?: string; + text?: string; + onClick?: React.MouseEventHandler; + }>; + headerButtons?: Array<{ + type?: string; + color?: string; + icon?: { + component?: React.ReactChild; + size?: number; + }; + onClick?: React.MouseEventHandler; + }>; + renderHeader?: Function; + renderContent?: Function; + renderFooter?: Function; + color?: string; +} + +/** + * IPopupProps Interface + * @prop popup The Popup's popup is a IPopup and required. + * @prop type The Popup's type and optional. + * @prop className The Popup's className and optional. + */ +export interface IPopupProps { + popup: IPopup; + type?: string; + className?: string; +} + +/** + * IAvatarProps Interface + * @prop src The Avatar's src is an image source and required. + * @prop letterItem The Avatar's letterItem is a string and optional. + * @prop className The Avatar's className and optional. + * @prop alt The Avatar's alt and optional. + */ +export interface IAvatarProps { + src?: string; + letterItem?: string; + className?: string; + alt?: string; +} + +/** + * INavbarProps Interface + * @prop type The Navbar's type and optional. + * @prop className The Navbar's className and optional. + * @prop top The Side Bar's top is a component and optional. + * @prop center The Side Bar's center is a component and optional. + * @prop bottom The Side Bar's bottom is a component and optional. + */ +export interface INavbarProps { + type?: string; + className?: string; + left?: any; + center?: any; + right?: any; +} + +/** + * IUnreadTipProps Interface + * @prop unread The UnreadTip's unread number and required. + * @prop muted The UnreadTip's muted and optional. + * @prop bgColorString The UnreadTip's bgColorString and optional. + */ +export interface IUnreadTipProps { + unread: number; + muted?: boolean; + bgColorString?: string; +} + +/** + * MessageType Type + * @type ILocationMessageProps + * @type IPhotoMessageProps + * @type IVideoMessageProps + * @type ISpotifyMessageProps + * @type IAudioMessageProps + * @type IMeetingLinkMessageProps + * @type IFileMessageProps + * @type ITextMessageProps + * @type ISystemMessageProps + * @type IMeetingMessageProps + */ +export type MessageType = + | ({ type: 'location' } & ILocationMessageProps) + | ({ type: 'photo' } & IPhotoMessageProps) + | ({ type: 'video' } & IVideoMessageProps) + | ({ type: 'spotify' } & ISpotifyMessageProps) + | ({ type: 'audio' } & IAudioMessageProps) + | ({ type: 'meetingLink' } & IMeetingLinkMessageProps) + | ({ type: 'file' } & IFileMessageProps) + | ({ type: 'text' } & ITextMessageProps) + | ({ type: 'bookmark' } & ITextMessageProps) + | ({ type: 'system' } & ISystemMessageProps) + | ({ type: 'meeting' } & IMeetingMessageProps); + +export type MessageBoxType = MessageType & IMessageBoxProps; + +export class ChannelItem extends React.Component {} +export class ChannelList extends React.Component {} +export class MessageBox extends React.Component {} +export class LocationMessage extends React.Component {} +export class PhotoMessage extends React.Component {} +export class VideoMessage extends React.Component {} +export class SpotifyMessage extends React.Component {} +export class AudioMessage extends React.Component {} +export class MeetingLink extends React.Component {} +export class FileMessage extends React.Component {} +export class TextMessage extends React.Component {} +export class SystemMessage extends React.Component {} +export class ReplyMessage extends React.Component {} +export class MeetingMessage extends React.Component {} +export class MeetingItem extends React.Component {} +export class MeetingList extends React.Component {} +export class MessageList extends React.Component {} + +export class Popup extends React.Component {} +export class Avatar extends React.Component {} +export class Button extends React.Component {} +export class Sidebar extends React.Component {} +export class Navbar extends React.Component {} +export class Input extends React.Component {} +export class Dropdown extends React.Component {} +export class Circle extends React.Component {} +export class UnreadTip extends React.Component {} +export class PhotoPreview extends React.Component {} diff --git a/packages/im-ui-web/src/utils/index.ts b/packages/im-ui-web/src/utils/index.ts new file mode 100644 index 0000000000..3ca3e524a3 --- /dev/null +++ b/packages/im-ui-web/src/utils/index.ts @@ -0,0 +1,26 @@ +import dayjs from 'dayjs'; +import BigNumber from 'bignumber.js'; + +export const formatChatListTime = (timeStamp?: number | string): string => { + if (!timeStamp) return ''; + + const chatTime = dayjs(Number(timeStamp)); + const now = dayjs(); + const today = now.startOf('date'); + const yesterday = today.subtract(1, 'day'); + const thisYear = now.startOf('year'); + + if (chatTime.isAfter(today)) { + return chatTime.format('HH:mm'); + } else if (chatTime.isAfter(yesterday)) { + return 'yesterday'; + } else if (chatTime.isAfter(thisYear)) { + return chatTime.format('MM-DD'); + } else { + return chatTime.format('YYYY-MM-DD'); + } +}; + +export const formatTime = (time?: number | string) => dayjs(time).format('HH:mm'); + +export const ZERO = new BigNumber(0); diff --git a/packages/im/config/index.ts b/packages/im/config/index.ts new file mode 100644 index 0000000000..d0579cbf36 --- /dev/null +++ b/packages/im/config/index.ts @@ -0,0 +1,55 @@ +import { HTTPHeaders, HTTPMethod, IRequestDefaults } from '@portkey/types'; + +export interface IConfig { + requestDefaults?: IRequestDefaults; +} + +export interface IIMConfig extends IConfig { + setConfig(options: IConfig): void; +} + +export class RequestDefaultsConfig { + public headers?: HTTPHeaders; + public baseURL?: string; + public url?: string; + public method?: HTTPMethod; + public timeout?: number; + constructor(config?: IRequestDefaults) { + if (config) { + Object.entries(config).forEach(([key, value]) => { + this[key as keyof IRequestDefaults] = value; + }); + } + } + setConfig(config?: IRequestDefaults) { + if (config) { + Object.entries(config).forEach(([key, value]) => { + this[key as keyof IRequestDefaults] = value; + }); + } + } +} + +export class IMConfig implements IIMConfig { + public requestDefaults?: IRequestDefaults; + public requestConfig: RequestDefaultsConfig; + + constructor(options?: IConfig) { + this.requestConfig = new RequestDefaultsConfig(); + if (options) this.setConfig(options); + } + setConfig(options: IConfig) { + Object.entries(options).forEach(([key, value]) => { + if (!value) return; + switch (key) { + case 'requestDefaults': + this.requestConfig.setConfig(value); + this.requestDefaults = value; + break; + default: + this[key as keyof IConfig] = value; + break; + } + }); + } +} diff --git a/packages/im/constant/index.ts b/packages/im/constant/index.ts new file mode 100644 index 0000000000..503d523ee2 --- /dev/null +++ b/packages/im/constant/index.ts @@ -0,0 +1,5 @@ +export const IM_SUCCESS_CODE = '20000'; +export const IM_TOKEN_EXPIRED = '40100'; +export const IM_TOKEN_ERROR = '40101'; +export const IM_TOKEN_ISSUER = '40102'; +export const IM_TOKEN_ERROR_ARRAY = [IM_TOKEN_EXPIRED, IM_TOKEN_ERROR, IM_TOKEN_ISSUER]; diff --git a/packages/im/index.ts b/packages/im/index.ts index 5e0ec46a1f..5de1d75a54 100644 --- a/packages/im/index.ts +++ b/packages/im/index.ts @@ -1,21 +1,29 @@ -import RelationIM, { Im } from '@relationlabs/im'; +import RelationIM, { Im, config as relationConfig } from '@relationlabs/im'; import * as utils from './utils'; import { AElfWallet } from '@portkey-wallet/types/aelf'; import { IMStatusEnum, Message, MessageCount } from './types'; import { sleep } from '@portkey-wallet/utils'; +import { IIMService } from './types/service'; +import { IMConfig } from './config'; +import { FetchRequest } from '@portkey/request'; +import { IBaseRequest } from '@portkey/types'; +import { IMService } from './service'; +import { getVerifyData } from './utils'; +import { IM_TOKEN_ERROR_ARRAY } from './constant'; -class IM { +export class IM { private _imInstance?: RelationIM; - private _token?: string; - private _msgObservers: Map void>> = new Map(); + private _channelMsgObservers: Map void>> = new Map(); private _unreadMsgObservers: Map void> = new Map(); private _errorObservers: Map void> = new Map(); + private _msgCountObservers: Map void> = new Map(); private _msgCount: MessageCount = { unreadCount: 0, mentionsCount: 0, }; - private _msgCountObservers: Map void> = new Map(); + private _account?: AElfWallet; + private _caHash?: string; public status = IMStatusEnum.INIT; public userInfo?: { @@ -24,42 +32,87 @@ class IM { relationId: string; }; - constructor() {} + public config: IMConfig; + public service: IIMService; + public fetchRequest: IBaseRequest; + + constructor() { + this.config = new IMConfig({ + requestDefaults: { + headers: { + 'R-Authorization': 'invalid_authorization', + Accept: 'application/json, text/plain, */*', + }, + }, + }); + + this.fetchRequest = new FetchRequest(this.config.requestConfig); + this.service = new IMService(this.fetchRequest); + + this.rewriteFetch(); + } async init(account: AElfWallet, caHash: string) { - if (this._imInstance) return; + this._account = account; + this._caHash = caHash; + + return this.initRelationIM(); + } + async initRelationIM() { + if (!this._account || !this._caHash) { + throw new Error('no account or caHash'); + } if (this.status === IMStatusEnum.AUTHORIZING) { throw new Error('IM is authorizing'); } this.status = IMStatusEnum.AUTHORIZING; - const token = await utils.sign(`${Date.now()}`, account, caHash); - if (!token) { - throw new Error('Can not get im token'); - } - this.status = IMStatusEnum.AUTHORIZED; - this._token = token; - this.initRelationIM(); - } + try { + const caHash = this._caHash; + const verifyData = utils.getVerifyData(`${Date.now()}`, this._account, this._caHash); + const { data: verifyResult } = await this.service.verifySignatureLoop(verifyData); + const addressAuthToken = verifyResult.token; + const { data: autoResult } = await this.service.getAuthTokenLoop({ + addressAuthToken, + }); - initRelationIM() { - if (!this._token) { - throw new Error('IM token is not exist'); - } + const token = autoResult.token; + if (caHash !== this._caHash) { + console.log('caHash changed'); + return undefined; + } + this.config.setConfig({ + requestDefaults: { + headers: { + 'R-Authorization': `Bearer ${token}`, + }, + }, + }); - const APIKEY = '581c6c4fa0b54912b00088aa563342a4'; - this._imInstance = RelationIM.init({ token: this._token, apiKey: APIKEY, connect: true, refresh: true }); - this.listenRelationIM(this._imInstance); - this._imInstance.getUserInfo().then(result => { - console.log('getUserInfo', result.data); - this.userInfo = result.data; - }); + // TODO: remove test API_KEY + const API_KEY = '295edaae67724a8ba04f4f39b9221779'; + this._imInstance = RelationIM.init({ token, apiKey: API_KEY, connect: true, refresh: true }); + this.listenRelationIM(this._imInstance); + + this.status = IMStatusEnum.AUTHORIZED; + + this.refreshMessageCount(); + if (this.userInfo) { + return this.userInfo; + } + const { data: userInfo } = await this.service.getUserInfo(); + this.userInfo = userInfo; + return userInfo; + } catch (error) { + console.log('init error', error); + this.status = IMStatusEnum.INIT; + throw error; + } } listenRelationIM(imInstance: RelationIM) { imInstance.bind(Im.CONNECT_OK, () => { console.log('CONNECT_OK'); - this.status = IMStatusEnum.CONNECTED; }); imInstance.bind(Im.CONNECT_ERR, (e: any) => { @@ -68,7 +121,7 @@ class IM { }); imInstance.bind(Im.CONNECT_CLOSE, async (e: any) => { console.log('CONNECT_CLOSE msg', e); - this.status = IMStatusEnum.ERROR; + this.status = IMStatusEnum.INIT; await sleep(1000); this.updateErrorObservers(e); try { @@ -190,6 +243,79 @@ class IM { cb(e); }); } + + private rewriteFetch() { + const send = this.fetchRequest.send; + this.fetchRequest.send = async (...args) => { + const result = await send.apply(this.fetchRequest, args); + + if (IM_TOKEN_ERROR_ARRAY.includes(result.code)) { + if (!this._account || !this._caHash) { + throw new Error('no account or caHash'); + } + try { + const caHash = this._caHash; + const verifyData = getVerifyData(`${Date.now()}`, this._account, this._caHash); + const { + data: { token: addressAuthToken }, + } = await this.service.verifySignatureLoop(verifyData, 5); + const { + data: { token }, + } = await this.service.getAuthTokenLoop({ addressAuthToken }, 5); + + if (caHash !== this._caHash) { + throw new Error('account changed'); + } + this.config.setConfig({ + requestDefaults: { + headers: { + 'R-Authorization': `Bearer ${token}`, + }, + }, + }); + return await send.apply(this.fetchRequest, args); + } catch (error) { + throw error; + } + } else if (result.code?.[0] !== '2') { + throw result; + } + return result; + }; + } + + setUrl({ apiUrl, wsUrl }: { apiUrl: string; wsUrl: string }) { + this.config.setConfig({ + requestDefaults: { + baseURL: apiUrl, + }, + }); + relationConfig.setSocketURL(wsUrl); + } + + refreshMessageCount = async () => { + const { data: messageCount } = await im.service.getUnreadCount(); + + im.updateMessageCount(messageCount); + return messageCount; + }; + + destroy() { + this._imInstance && this._imInstance.destroy(); + this._imInstance = undefined; + this._msgCount = { + unreadCount: 0, + mentionsCount: 0, + }; + this._account = undefined; + this._caHash = undefined; + this.status = IMStatusEnum.INIT; + this.userInfo = undefined; + this._unreadMsgObservers.clear(); + this._channelMsgObservers.clear(); + this._errorObservers.clear(); + this._msgCountObservers.clear(); + } } const im = new IM(); diff --git a/packages/im/package.json b/packages/im/package.json index 283ff91881..8f1109c1d3 100644 --- a/packages/im/package.json +++ b/packages/im/package.json @@ -12,6 +12,6 @@ "@portkey-wallet/types": "^1.3.0", "@portkey-wallet/api": "^1.3.0", "@portkey-wallet/utils": "^1.3.0", - "@relationlabs/im": "^0.3.3" + "@relationlabs/im": "^0.4.0-beta-3" } } diff --git a/packages/im/service/index.ts b/packages/im/service/index.ts index bbd399888e..87237e0720 100644 --- a/packages/im/service/index.ts +++ b/packages/im/service/index.ts @@ -3,47 +3,176 @@ import { BaseService } from '@portkey/services'; import { CreateChannelParams, CreateChannelResult, + DeleteMessageParams, GetAuthTokenParams, GetAuthTokenResult, GetChannelInfoParams, GetChannelInfoResult, + GetChannelListParams, + GetChannelListResult, + GetMessageListParams, GetUserInfoDefaultResult, GetUserInfoParams, + HideChannelParams, IIMService, IMServiceCommon, - MessageListParams, - MessageReadParams, + ReadMessageParams, + SendMessageParams, + SendMessageResult, + TriggerMessageEvent, + UpdateChannelMuteParams, + UpdateChannelPinParams, VerifySignatureParams, VerifySignatureResult, } from '../types/service'; -import { ChannelMemberInfo, Message } from '../types'; +import { ChannelMemberInfo, Message, MessageCount } from '../types'; +import { IM_SUCCESS_CODE } from '../constant'; +import { sleep } from '@portkey-wallet/utils'; export class IMService extends BaseService implements IIMService { constructor(request: T) { super(request); } + verifySignature(params: VerifySignatureParams): IMServiceCommon { - throw new Error('Method not implemented.'); + return this._request.send({ + url: '/api/v1/users/token', + params, + method: 'POST', + headers: { + 'R-Authorization': '', + }, + }); + } + async verifySignatureLoop(params: VerifySignatureParams, times = 0): IMServiceCommon { + try { + const result = await this.verifySignature(params); + if (result.code === IM_SUCCESS_CODE) return result; + if (times === 1) throw result; + } catch (error) { + console.log('verifySignatureLoop: error', error); + } + if (times <= 0) await sleep(1000); + return this.verifySignatureLoop(params, times - 1); } + getAuthToken(params: GetAuthTokenParams): IMServiceCommon { - throw new Error('Method not implemented.'); + return this._request.send({ + url: '/api/v1/users/auth', + params, + method: 'POST', + headers: { + 'R-Authorization': '', + }, + }); + } + async getAuthTokenLoop(params: GetAuthTokenParams, times = 0): IMServiceCommon { + try { + const result = await this.getAuthToken(params); + if (result.code === IM_SUCCESS_CODE) return result; + if (times === 1) throw result; + } catch (error) { + console.log('getAuthToken: error', error); + } + if (times <= 0) await sleep(1000); + return this.getAuthTokenLoop(params, times - 1); } + getUserInfo(params: GetUserInfoParams): IMServiceCommon { - throw new Error('Method not implemented.'); + return this._request.send({ + url: '/api/v1/users/userInfo', + params, + method: 'GET', + }); } createChannel(params: CreateChannelParams): IMServiceCommon { - throw new Error('Method not implemented.'); + return this._request.send({ + url: '/api/v1/channelContacts/createChannel', + params, + method: 'POST', + }); } getChannelInfo(params: GetChannelInfoParams): IMServiceCommon { - throw new Error('Method not implemented.'); + return this._request.send({ + url: '/api/v1/channelContacts/channelDetailInfo', + params, + method: 'GET', + }); } getChannelMembers(params: GetChannelInfoParams): IMServiceCommon { - throw new Error('Method not implemented.'); + return this._request.send({ + url: '/api/v1/channelContacts/members', + params, + method: 'GET', + }); + } + sendMessage(params: SendMessageParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/message/send', + params, + method: 'POST', + }); + } + readMessage(params: ReadMessageParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/message/read', + params, + method: 'POST', + }); + } + getMessageList(params: GetMessageListParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/message/list', + params, + method: 'GET', + }); + } + deleteMessage(params: DeleteMessageParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/message/hide', + params, + method: 'POST', + }); + } + getUnreadCount(): IMServiceCommon { + return this._request.send({ + url: '/api/v1/message/unreadCount', + method: 'GET', + }); + } + triggerMessageEvent(params: TriggerMessageEvent): IMServiceCommon { + return this._request.send({ + url: '/api/v1/message/event', + params, + method: 'POST', + }); + } + getChannelList(params: GetChannelListParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/feed/list', + params, + method: 'GET', + }); + } + updateChannelPin(params: UpdateChannelPinParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/feed/pin', + params, + method: 'POST', + }); } - messageList(params: MessageListParams): IMServiceCommon { - throw new Error('Method not implemented.'); + updateChannelMute(params: UpdateChannelMuteParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/feed/mute', + params, + method: 'POST', + }); } - messageRead(params: MessageReadParams): IMServiceCommon { - throw new Error('Method not implemented.'); + hideChannel(params: HideChannelParams): IMServiceCommon { + return this._request.send({ + url: '/api/v1/feed/hide', + params, + method: 'POST', + }); } } diff --git a/packages/im/types/index.ts b/packages/im/types/index.ts index fe7a9926f7..8ff0d7bf19 100644 --- a/packages/im/types/index.ts +++ b/packages/im/types/index.ts @@ -1,5 +1,15 @@ -export type MessageType = 'SYS' | 'TEXT' | 'CARD' | 'ANNOUNCEMENT' | 'BATCH_TRANSFER'; -export type ParsedContent = string; +export type MessageType = 'SYS' | 'TEXT' | 'CARD' | 'ANNOUNCEMENT' | 'BATCH_TRANSFER' | 'IMAGE'; +export type ParsedContent = string | ParsedImage; +export type ParsedImage = { + type: string; + action: string; + imgUrl: string; + s3Key: string; + thumbImgUrl?: string; + thumbS3Key?: string; + width?: string; + height?: string; +}; export type Message = { channelUuid: string; @@ -11,21 +21,28 @@ export type Message = { fromAvatar?: string; fromName?: string; + id?: string; quote?: Message; parsedContent?: ParsedContent; unidentified?: boolean | undefined; }; +export type SocketMessage = Message & { + mute: boolean; +}; + export type ChannelMemberInfo = { relationId: string; name: string; avatar: string; isAdmin: boolean; }; + export enum ChannelTypeEnum { GROUP = 'G', P2P = 'P', } + export type ChannelInfo = { uuid: string; name: string; @@ -72,3 +89,8 @@ export type MessageCount = { unreadCount: number; mentionsCount: number; }; + +export enum TriggerMessageEventActionEnum { + ENTER_CHANNEL = 1, + EXIT_CHANNEL = 2, +} diff --git a/packages/im/types/service.ts b/packages/im/types/service.ts index a7182ddbdd..6f8a433401 100644 --- a/packages/im/types/service.ts +++ b/packages/im/types/service.ts @@ -1,4 +1,11 @@ -import { ChannelMemberInfo, ChannelTypeEnum, Message } from '.'; +import { + ChannelItem, + ChannelMemberInfo, + ChannelTypeEnum, + Message, + MessageCount, + TriggerMessageEventActionEnum, +} from '.'; export type IMServiceCommon = Promise<{ code: string; @@ -6,18 +13,6 @@ export type IMServiceCommon = Promise<{ data: T; }>; -export type MessageListParams = { - channelUuid: string; - maxCreateAt: number; - toRelationId?: string; - limit?: number; -}; - -export type MessageReadParams = { - channelUuid: string; - total: number; -}; - export type VerifySignatureParams = { message: string; signature: string; @@ -62,6 +57,7 @@ export type CreateChannelResult = { export type GetChannelInfoParams = { channelUuid: string; }; + export type GetChannelMembersParams = GetChannelInfoParams; export type GetChannelInfoResult = { @@ -77,14 +73,91 @@ export type GetChannelInfoResult = { members: ChannelMemberInfo[]; }; +export type SendMessageParams = { + channelUuid?: string; + toRelationId?: string; + type?: string; + content: string; + sendUuid: string; + quoteId?: string; + mentionedUser?: string[]; +}; + +export type SendMessageResult = { + id: string; + channelUuid: string; +}; + +export type ReadMessageParams = { + channelUuid: string; + total: number; +}; + +export type GetMessageListParams = { + channelUuid: string; + maxCreateAt: number; + toRelationId?: string; + limit?: number; +}; + +export type DeleteMessageParams = { + id: string; +}; + +export type TriggerMessageEvent = { + channelUuid?: string; + toRelationId?: string; + fromRelationId: string; + action: TriggerMessageEventActionEnum; +}; + +export type GetChannelListParams = { + keyword?: string; + cursor?: string; + skipCount?: number; + maxResultCount?: number; +}; + +export type GetChannelListResult = { + totalCount: number; + cursor: string; + list: ChannelItem[]; +}; + +export type UpdateChannelPinParams = { + channelUuid: string; + pin: boolean; +}; + +export type UpdateChannelMuteParams = { + channelUuid: string; + mute: boolean; +}; + +export type HideChannelParams = { + channelUuid: string; +}; + export interface IIMService { verifySignature(params: VerifySignatureParams): IMServiceCommon; + verifySignatureLoop(params: VerifySignatureParams, times?: number): IMServiceCommon; getAuthToken(params: GetAuthTokenParams): IMServiceCommon; - getUserInfo(params: GetUserInfoParams): IMServiceCommon; + getAuthTokenLoop(params: GetAuthTokenParams, times?: number): IMServiceCommon; + getUserInfo(params?: GetUserInfoParams): IMServiceCommon; + createChannel(params: CreateChannelParams): IMServiceCommon; getChannelInfo(params: GetChannelInfoParams): IMServiceCommon; getChannelMembers(params: GetChannelMembersParams): IMServiceCommon; - messageList(params: MessageListParams): IMServiceCommon; - messageRead(params: MessageReadParams): IMServiceCommon; + sendMessage(params: SendMessageParams): IMServiceCommon; + readMessage(params: ReadMessageParams): IMServiceCommon; + getMessageList(params: GetMessageListParams): IMServiceCommon; + deleteMessage(params: DeleteMessageParams): IMServiceCommon; + getUnreadCount(): IMServiceCommon; + triggerMessageEvent(params: TriggerMessageEvent): IMServiceCommon; + + getChannelList(params: GetChannelListParams): IMServiceCommon; + updateChannelPin(params: UpdateChannelPinParams): IMServiceCommon; + updateChannelMute(params: UpdateChannelMuteParams): IMServiceCommon; + hideChannel(params: HideChannelParams): IMServiceCommon; } diff --git a/packages/im/utils/parser.ts b/packages/im/utils/parser.ts index 86bcb9bbd2..ac2a3cac0a 100644 --- a/packages/im/utils/parser.ts +++ b/packages/im/utils/parser.ts @@ -1,6 +1,34 @@ import { messageParser as relationMessageParser, Message as RelationMessage } from '@relationlabs/im'; -import { Message } from '../types'; +import { Message, ParsedImage } from '../types'; + +const imageMessageParser = (str: string): ParsedImage => { + str = str.replaceAll(/,/g, ';'); + const result: Record = {}; + const pairs = str.split(';'); + + pairs.forEach(pair => { + const [key, value] = pair.split(':'); + result[key] = value; + }); + return { + type: result['type'] || '', + action: result['action'] || '', + imgUrl: result['p1(Text)'] || '', + s3Key: result['p2(Text)'] || '', + thumbImgUrl: result['p1(Text)'], + thumbS3Key: result['p2(Text)'], + width: result['p5(Text)'], + height: result['p6(Text)'], + }; +}; export const messageParser = (message: Message): Message => { + if (message.type === 'IMAGE') { + const { content } = message; + return { + ...message, + parsedContent: imageMessageParser(content), + }; + } return relationMessageParser(message as RelationMessage) as Message; }; diff --git a/packages/im/utils/sign.ts b/packages/im/utils/sign.ts index fe9b3346e1..6edf6f59f1 100644 --- a/packages/im/utils/sign.ts +++ b/packages/im/utils/sign.ts @@ -1,20 +1,10 @@ import AElf from 'aelf-sdk'; import { AElfWallet } from '@portkey-wallet/types/aelf'; -import RelationIM from '@relationlabs/im'; -import { FetchRequest } from '@portkey/request'; -const fetchRequest = new FetchRequest({ - baseURL: 'https://api.relationlabs.ai', - headers: { - // RelationOne api need Accept: 'application/json, text/plain, */*', - Accept: 'application/json, text/plain, */*', - }, -}); -export const sign = async (message: string, account: AElfWallet, caHash: string) => { +export const getVerifyData = (message: string, account: AElfWallet, caHash: string) => { if (!account.keyPair) { throw new Error('no keyPair'); } - const hexMsg = AElf.utils.sha256(message); const signature = account.keyPair.sign(Buffer.from(hexMsg, 'hex'), { canonical: true, @@ -28,24 +18,10 @@ export const sign = async (message: string, account: AElfWallet, caHash: string) `0${signature.recoveryParam.toString()}`, ].join(''); - const { data: verifyData } = await fetchRequest.send({ - url: '/api/v1/verify/aelf', - method: 'POST', - body: JSON.stringify({ - message, - signature: signatureStr, - address: account.address, - caHash, - }), - }); - const { token } = verifyData || {}; - - // TODO: RelationIM test APIKEY - const APIKEY = '581c6c4fa0b54912b00088aa563342a4'; - const { error, token: unifiedAuthToken } = await RelationIM.getRelationToken(token, APIKEY); - - if (error) { - throw error; - } - return unifiedAuthToken; + return { + message, + signature: signatureStr, + address: account.address, + caHash, + }; }; diff --git a/packages/mobile-app-did/js/assets/image/svgs.js b/packages/mobile-app-did/js/assets/image/svgs.js index abd57f1a14..7dfba90fc4 100644 --- a/packages/mobile-app-did/js/assets/image/svgs.js +++ b/packages/mobile-app-did/js/assets/image/svgs.js @@ -1,2 +1,2 @@ /* eslint-disable prettier/prettier */ -export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","chat-add-contact":"\n\n\n","chat-add":"\n\n\n","chat-delete-emoji":"\n\n\n\n\n","chat-delete":"\n\n\n","chat-emoji":"\n\n\n\n\n\n","chat-file":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-find-more":"\n\n\n\n\n\n","chat-keyboard":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-mute":"\n\n\n","chat-new-chat":"\n\n\n","chat-pin":"\n\n\n","chat-profile":"\n\n\n\n\n","chat-reshutter":"\n\n\n\n\n","chat-robot":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","chat-send":"\n\n\n","chat-shutter":"\n\n\n\n","chat-tab":"\n\n\n","chat-unmute":"\n\n\n\n\n","chat-unpin":"\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file +export default {"Contract":"\n\n\n\n\n","activity":"\n\n iycsjvvxme\n \n \n \n \n \n \n \n \n \n \n \n","add-blue":"\n\n\n","add-contact":"\n\n\n\n\n","add-token":"\n\n tkdtbxkcto\n \n \n \n \n \n \n \n \n \n \n \n","add1":"\n\n nkkkxfqpcl\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add2":"\n\n ymquiytxjt\n \n \n \n \n \n \n \n \n \n \n \n \n \n","add3":"\n\n wivmphxoms\n \n \n \n \n \n \n \n \n","album":"\n\n epepouhbdw\n \n \n \n \n \n \n \n \n","app-blue-logo":"\n\n Icon___Fill___Aelf\n \n \n \n \n \n \n \n \n","apple-icon":"\n\n\n\n\n\n","apple":"\n\n\n\n","bingoGame":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n","block-back":"\n\n\n","bookmark":"\n\n\n\n","bookmarked":"\n\n\n\n","buy":"\n\n\n\n\n\n\n\n\n","buy1":"\n\n\n\n\n\n\n\n\n","chat-add-contact":"\n\n\n","chat-add":"\n\n\n","chat-added":"\n\n\n","chat-album":"\n\n\n\n\n\n\n","chat-bookmark":"\n\n\n","chat-camera":"\n\n\n\n","chat-chat":"\n\n\n","chat-delete-emoji":"\n\n\n\n\n","chat-delete":"\n\n\n","chat-emoji":"\n\n\n\n\n\n","chat-file":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-find-more":"\n\n\n\n\n\n","chat-keyboard":"\n\n\n\n\n\n\n\n\n\n\n\n","chat-mute":"\n\n\n","chat-new-chat":"\n\n\n","chat-pin":"\n\n\n","chat-profile":"\n\n\n\n\n","chat-reshutter":"\n\n\n\n\n","chat-robot":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","chat-send":"\n\n\n","chat-shutter":"\n\n\n\n","chat-tab":"\n\n\n","chat-unmute":"\n\n\n\n\n","chat-unpin":"\n\n\n\n\n","check-false":"\n\n fzoykpdoyh\n \n \n \n \n \n \n \n","check-true":"\n\n mbcnnswjqh\n \n \n \n \n \n \n \n \n \n \n","clear":"\n\n qzqvrhcbqn\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","clear1":"\n\n pgqcjmcbit\n \n \n \n \n \n \n \n \n","clear2":"\n\n oinrqzcjps\n \n \n \n \n \n \n \n \n \n \n \n \n","clear3":"\n\n\n","close":"\n\n tqsruqxgrb\n \n \n \n \n \n \n \n \n","close1":"\n\n ozresgjsts\n \n \n \n \n \n \n \n \n \n \n","close2":"\n\n yyybesklsg\n \n \n \n \n \n \n \n \n \n \n","contact":"\n\n ymwllfkfjj\n \n \n \n \n \n \n \n \n \n \n \n \n \n","contact2":"\n\n mkokwgrdlj\n \n \n \n \n \n \n \n \n \n \n","copy":"\n\n cezybjxdvo\n \n \n \n \n \n \n \n \n \n","copy1":"\n\n\n\n\n","copy3":"\n\n\n\n","default_record":"\n\n\n\n\n","delete":"\n\n delete\n \n \n \n \n \n \n \n \n","desk-mac":"\n\n desk_mac\n \n \n \n \n \n \n \n \n \n \n","desk-win":"\n\n wnusftfmuc\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","discord":"\n\n\n","discover":"\n\n\n\n\n","down-arrow":"\n\n osinrlprty\n \n \n \n \n \n \n \n \n \n \n","edit":"\n\n vdesusdtjq\n \n \n \n \n \n \n \n \n \n \n \n \n","elf-icon":"\n\n\n\n\n","email":"\n\n\n\n\n","eye":"\n\n imulnepiwm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","eyeClosed":"\n\n hicffbbgsz\n \n \n \n \n \n \n \n \n \n \n","fail":"\n\n gjgrrybzwe\n \n \n \n \n \n \n","faucet":"\n\n\n\n\n\n\n\n\n\n\n\n\n","faucet1":"\n\n\n\n\n\n\n\n\n\n\n\n\n","finish":"\n\n sytkvvhtjm\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","google-icon":"\n\n\n\n\n\n\n\n","google":"\n\n\n\n\n\n","guardian":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","httpWarn":"\n\n\n\n\n","httpsLock":"\n\n\n\n\n","import":"\n\n cfqdrgpdnt\n \n \n \n \n \n \n \n \n","left-arrow":"\n\n bxsxgtucjl\n \n \n \n \n \n \n \n \n \n \n \n \n","lock":"\n\n cgioxzlxcw\n \n \n \n \n \n \n \n \n \n \n \n \n \n","logo-icon":"\n\n\n","logo-text":"\n\n zjqebghwfq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","mainnet":"\n\n vsdccgmdvr\n \n \n \n \n \n \n \n \n \n \n \n","master1":"\n\n master1\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master2":"\n\n master2\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master3":"\n\n master3\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master4":"\n\n master4\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master5":"\n\n master5\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","master6":"\n\n master6\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","more-info":"\n\n\n","more":"\n\n\n","my":"\n\n\n","no-bookmarks":"\n\n\n\n\n\n","no-message":"\n\n\n\n\n\n\n\n","no-records":"\n\n\n\n\n\n","no-result":"\n\n nsgwxrxohh\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","noData":"\n\n Img\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","phone-Android":"\n\n phone_Android\n \n \n \n \n \n \n \n \n \n \n","phone-iOS":"\n\n phone_iOS\n \n \n \n \n \n \n \n \n \n \n","phone":"\n\n\n\n\n\n\n","question-mark":"\n\n uhxdjnzjyz\n \n \n \n \n \n \n \n \n","receive":"\n\n\n idbsiskeqx\n \n \n \n \n \n \n \n \n \n \n \n \n \n","receive1":"\n\n kwpdvdpdep\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","red-delete":"\n\n\n","refresh":"\n\n wxwmniiwhr\n \n \n \n \n \n \n \n \n \n \n","refresh1":"\n\n\n\n\n","reload":"\n\n glmbgniwte\n \n \n \n \n \n \n \n \n \n \n \n","right-arrow":"\n\n\n","right-arrow2":"\n\n right02\n \n \n \n \n \n \n \n \n","scan-frame":"\n\n vzbmmmilfr\n \n \n \n \n \n \n \n \n \n \n","scan-square":"\n\n\n\n\n\n\n\n","scan":"\n\n ihxupooxxr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","search":"\n\n dhwysojdsr\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected":"\n\n rgvfghecdq\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","selected2":"\n\n sdcfxhevmq\n \n \n \n \n \n \n \n \n \n \n","selected3":"\n\n a a a\n \n \n \n \n \n \n \n \n \n \n \n \n","send":"\n\n twqjriweez\n \n \n \n \n \n \n \n \n \n \n \n \n \n","send1":"\n\n umoyfdfszl\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","setting":"\n\n rokdjfskko\n \n \n \n \n \n \n \n \n \n","setting2":"\n\n jjtrxyphxg\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAboutUs":"\n\n zqlgqdoeve\n \n \n \n \n \n \n \n \n \n \n \n \n","settingsAddressBook":"\n\n qgmvghoedy\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsHelp":"\n\n zqoxrydqmd\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsNetworks":"\n\n otxgxofwcv\n \n \n \n \n \n \n \n \n \n \n \n \n \n","settingsSecurity":"\n\n hqqhptctxw\n \n \n \n \n \n \n \n \n \n \n","settingsSetting":"\n\n vuuzuuyxty\n \n \n \n \n \n \n \n \n \n \n \n \n","share":"\n\n\n\n\n\n","share2":"\n\n\n\n\n","social-recovery":"\n\n cjrewsnkts\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","success":"\n\n gxszmwdsui\n \n \n \n \n \n \n","switch":"\n\n\n\n\n\n\n","telegram":"\n\n\n\n\n\n\n\n\n\n","terms":"\n\n\n\n\n\n","testnet":"\n\n\n\n\n","time":"\n\n\n\n","touch-id":"\n\n\n\n","transfer-receive":"\n\n iyvgjvxgxm\n \n \n \n \n \n \n \n \n \n \n \n \n","transfer-send":"\n\n uonrsgnbug\n \n \n \n \n \n \n \n \n \n \n \n","transfer":"\n\n xpkknuehvy\n \n \n \n \n \n \n \n \n \n \n \n \n \n","twitter":"\n\n\n","unselected":"\n\n\n","up-arrow":"\n\n up\n \n \n \n \n \n \n \n \n","visa":"\n\n\n","wallet-security":"\n\n\n\n\n\n","wallet-white":"\n\n\n\n\n\n\n\n\n\n\n\n","wallet":"\n\n bfzjdifnru\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n","warning":"\n\n\n"} \ No newline at end of file diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-added.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-added.svg new file mode 100644 index 0000000000..943828f939 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-added.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-album.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-album.svg new file mode 100644 index 0000000000..5e4f4879e2 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-album.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-bookmark.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-bookmark.svg new file mode 100644 index 0000000000..7b1b6e6fe8 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-bookmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-camera.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-camera.svg new file mode 100644 index 0000000000..edf1701d08 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-camera.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-chat.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-chat.svg new file mode 100644 index 0000000000..3883e0c030 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg b/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg index 153541ae66..be2b58db35 100644 --- a/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg +++ b/packages/mobile-app-did/js/assets/image/svgs/chat-mute.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/mobile-app-did/js/assets/image/svgs/no-message.svg b/packages/mobile-app-did/js/assets/image/svgs/no-message.svg new file mode 100644 index 0000000000..6f69fdcc06 --- /dev/null +++ b/packages/mobile-app-did/js/assets/image/svgs/no-message.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/mobile-app-did/js/components/CacheImage/index.tsx b/packages/mobile-app-did/js/components/CacheImage/index.tsx index 3b9e02bcbd..fc66db6f0a 100644 --- a/packages/mobile-app-did/js/components/CacheImage/index.tsx +++ b/packages/mobile-app-did/js/components/CacheImage/index.tsx @@ -1,27 +1,61 @@ +import GStyles from 'assets/theme/GStyles'; import { FontStyles } from 'assets/theme/styles'; import useEffectOnce from 'hooks/useEffectOnce'; -import React, { useCallback, useState } from 'react'; -import { ActivityIndicator, Image, ImageProps, StyleSheet } from 'react-native'; +import React, { useCallback, useMemo, useState } from 'react'; +import { ActivityIndicator, Image, ImageProps, StyleSheet, View } from 'react-native'; import { getCacheImage, isURISource } from 'utils/fs/img'; -import { pTd } from 'utils/unit'; +import Default_Image from 'assets/image/pngs/default_record.png'; + +export interface CacheImageProps extends ImageProps { + thumb?: ImageProps['source']; +} + +export default function CacheImage(props: CacheImageProps) { + const [source, setSource] = useState(); + const [thumb, setThumb] = useState(); -export default function CacheImage(props: ImageProps) { - const [source, setSource] = useState(); const init = useCallback(async () => { // is ImageRequireSource if (!isURISource(props.source)) return setSource(props.source); const localSource = await getCacheImage(props.source); localSource && setSource(localSource); - }, [props.source]); + if (props.thumb) { + if (!isURISource(props.thumb)) return setThumb(props.thumb); + const localThumb = await getCacheImage(props.thumb); + localThumb && setThumb(localThumb); + } + }, [props.source, props.thumb]); useEffectOnce(() => { init(); }); - if (!source) return ; - return ; + + const indicator = useMemo( + () => ( + + + + ), + [], + ); + + return ( + + {thumb && !source && } + {!source ? indicator : } + + ); } const styles = StyleSheet.create({ backgroundStyle: { overflow: 'hidden' }, - indicatorStyle: { marginHorizontal: pTd(5) }, + indicatorBox: { + position: 'absolute', + left: 0, + right: 0, + top: 0, + bottom: 0, + ...GStyles.center, + backgroundColor: 'transparent', + }, }); diff --git a/packages/mobile-app-did/js/components/CommonAvatar/index.tsx b/packages/mobile-app-did/js/components/CommonAvatar/index.tsx index 2fe3e0087f..64084763e5 100644 --- a/packages/mobile-app-did/js/components/CommonAvatar/index.tsx +++ b/packages/mobile-app-did/js/components/CommonAvatar/index.tsx @@ -28,7 +28,7 @@ export default function CommonAvatar(props: CommonAvatarProps) { shapeType = 'circular', hasBorder, } = props; - const initialsTitle = title?.[0]; + const initialsTitle = String(title?.[0] || '').toUpperCase(); const sizeStyle = { width: Number(avatarSize), diff --git a/packages/mobile-app-did/js/components/ContactList/index.tsx b/packages/mobile-app-did/js/components/ContactList/index.tsx index fe3d86ba5e..58f52e07ab 100644 --- a/packages/mobile-app-did/js/components/ContactList/index.tsx +++ b/packages/mobile-app-did/js/components/ContactList/index.tsx @@ -117,7 +117,7 @@ const ContactsList: React.FC = ({ key={item.id} contact={item} onPress={() => { - navigationService.navigate('ContactDetail', { contact: item }); + navigationService.navigate('NoChatContactProfile', { contact: item }); }} /> ); @@ -163,7 +163,7 @@ const ContactsList: React.FC = ({ type="solid" containerStyle={contactListStyles.addButtonWrap} buttonStyle={[contactListStyles.addButton]} - onPress={() => navigationService.navigate('ContactEdit')}> + onPress={() => navigationService.navigate('NoChatContactProfileEdit')}> {t('Add New Contacts')} diff --git a/packages/mobile-app-did/js/components/NoData/index.tsx b/packages/mobile-app-did/js/components/NoData/index.tsx index aa6c17cdd3..c823dd06c5 100644 --- a/packages/mobile-app-did/js/components/NoData/index.tsx +++ b/packages/mobile-app-did/js/components/NoData/index.tsx @@ -3,10 +3,11 @@ import { StyleSheet, View, ViewStyle } from 'react-native'; import { pTd } from 'utils/unit'; import { TextL } from 'components/CommonText'; import { defaultColors } from 'assets/theme'; -import Svg from 'components/Svg'; +import Svg, { IconName } from 'components/Svg'; export type NoDataPropsType = { noPic?: boolean; + icon?: IconName; message?: string; type?: 'center' | 'top'; topDistance?: number | string; @@ -15,6 +16,7 @@ export type NoDataPropsType = { const NoData: React.FC = props => { const { + icon = 'noData', message = 'You have no transactions!', type = 'top', topDistance = pTd(89), @@ -33,7 +35,7 @@ const NoData: React.FC = props => { return ( - {!noPic && } + {!noPic && } {message} ); diff --git a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx index d3473c5a69..0ff8c64bf8 100644 --- a/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx +++ b/packages/mobile-app-did/js/components/TabsDrawer/components/WalletInfoOverlay/index.tsx @@ -46,7 +46,7 @@ const MyWalletModal = ({ tabInfo }: MyWalletModalType) => { const info = value as CAInfo; return info?.caAddress ? { - chaiId: key, + chainId: key, caAddress: info.caAddress, ...accountTokenList.find(token => token.chainId === key && token.symbol === defaultToken.symbol), } @@ -73,11 +73,11 @@ const MyWalletModal = ({ tabInfo }: MyWalletModalType) => { {walletName} {caInfoList?.map((item, index) => ( - + - {formatStr2EllipsisStr(addressFormat(item?.caAddress, item?.chaiId as ChainId), 10)} + {formatStr2EllipsisStr(addressFormat(item?.caAddress, item?.chainId as ChainId), 10)} - {formatChainInfoToShow(item?.chaiId as ChainId, currentNetwork)} + {formatChainInfoToShow(item?.chainId as ChainId, currentNetwork)} diff --git a/packages/mobile-app-did/js/components/Updater/index.tsx b/packages/mobile-app-did/js/components/Updater/index.tsx index 893db56ea6..da683b99e1 100644 --- a/packages/mobile-app-did/js/components/Updater/index.tsx +++ b/packages/mobile-app-did/js/components/Updater/index.tsx @@ -26,6 +26,9 @@ import { exceptionManager } from 'utils/errorHandler/ExceptionHandler'; import EntryScriptWeb3 from 'utils/EntryScriptWeb3'; import { useFetchTxFee } from '@portkey-wallet/hooks/hooks-ca/useTxFee'; import { useCheckAndInitNetworkDiscoverMap } from 'hooks/discover'; +import im from '@portkey-wallet/im'; +import s3Instance from '@portkey-wallet/utils/s3'; +import Config from 'react-native-config'; request.setExceptionManager(exceptionManager); export default function Updater() { @@ -35,7 +38,7 @@ export default function Updater() { changeLanguage('en'); }); useChainListFetch(); - const { apiUrl } = useCurrentNetworkInfo(); + const { apiUrl, imApiUrl, imWsUrl, imS3Bucket } = useCurrentNetworkInfo(); const pin = usePin(); const onLocking = useLocking(); const checkManagerOnLogout = useCheckManagerOnLogout(); @@ -53,6 +56,18 @@ export default function Updater() { service.defaults.baseURL = apiUrl; } }, [apiUrl]); + useMemo(() => { + im.setUrl({ + apiUrl: imApiUrl || '', + wsUrl: imWsUrl || '', + }); + }, [imApiUrl, imWsUrl]); + useMemo(() => { + s3Instance.setConfig({ + bucket: imS3Bucket || '', + key: Config.IM_S3_KEY || '', + }); + }, [imS3Bucket]); useMemo(() => { request.setLockCallBack(onLocking); diff --git a/packages/mobile-app-did/js/hooks/useInitData.ts b/packages/mobile-app-did/js/hooks/useInitData.ts index c1efa34191..8434d5829c 100644 --- a/packages/mobile-app-did/js/hooks/useInitData.ts +++ b/packages/mobile-app-did/js/hooks/useInitData.ts @@ -12,6 +12,7 @@ import { useBookmarkList, useCheckAndInitNetworkDiscoverMap } from './discover'; import { usePin } from './store'; import { getManagerAccount } from 'utils/redux'; import im from '@portkey-wallet/im'; +import { useInitIm } from '@portkey-wallet/hooks/hooks-ca/im'; // const getCurrentCAContract = useGetCurrentCAContract(); @@ -34,6 +35,7 @@ export default function useInitData() { useCheckAndInitNetworkDiscoverMap(); const { refresh: loadBookmarkList } = useBookmarkList(); + const initIm = useInitIm(); const initIM = useCallback(async () => { if (!pin) return; @@ -41,11 +43,11 @@ export default function useInitData() { if (!account || !wallet.caHash) return; try { - await im.init(account, wallet.caHash); + await initIm(account, wallet.caHash); } catch (error) { console.log('im init error', error); } - }, [pin, wallet.caHash]); + }, [initIm, pin, wallet.caHash]); const init = useCallback(async () => { try { diff --git a/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts b/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts index 7ee265d524..15c47e3b10 100644 --- a/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts +++ b/packages/mobile-app-did/js/hooks/useKeyboardHeight.ts @@ -2,9 +2,8 @@ import { useLatestRef } from '@portkey-wallet/hooks'; import { windowHeight } from '@portkey-wallet/utils/mobile/device'; import { isIOS } from '@rneui/base'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { LayoutAnimation, KeyboardEvent } from 'react-native'; +import { KeyboardEvent } from 'react-native'; import { Keyboard } from 'react-native'; -import { nextAnimation } from 'utils/animation'; import useEffectOnce from './useEffectOnce'; import useAsyncStorageState from './useAsyncStorageState'; @@ -42,43 +41,23 @@ export default function useKeyboardHeight() { return keyboardHeight; } -export function useKeyboard(topSpacing = 0, isAnimation = false) { +export function useKeyboard(topSpacing = 0) { const [keyboardHeight, setKeyboardHeight] = useAsyncStorageState('KeyboardHeight', DefaultKeyboardHeight); const [isKeyboardOpened, setIsKeyboardOpened] = useState(); + const KeyboardOpenedRef = useLatestRef(isKeyboardOpened); const show: KeyboardEventListener = useCallback( event => { - if (isAnimation) - nextAnimation({ - duration: event.duration, - create: { - duration: event.duration, - type: LayoutAnimation.Types[event.easing], - property: LayoutAnimation.Properties.opacity, - }, - }); - - setKeyboardHeight(isIOS ? event.endCoordinates.height : windowHeight - event.endCoordinates.screenY); + if (!KeyboardOpenedRef.current || isIOS) { + setKeyboardHeight(isIOS ? event.endCoordinates.height : windowHeight - event.endCoordinates.screenY); + } setIsKeyboardOpened(true); }, - [isAnimation, setKeyboardHeight], + [KeyboardOpenedRef, setKeyboardHeight], ); - const hide: KeyboardEventListener = useCallback( - event => { - if (isAnimation) - nextAnimation({ - duration: event.duration, - create: { - duration: event.duration, - type: LayoutAnimation.Types[event.easing], - property: LayoutAnimation.Properties.opacity, - }, - }); - - setIsKeyboardOpened(false); - }, - [isAnimation], - ); + const hide: KeyboardEventListener = useCallback(() => { + setIsKeyboardOpened(false); + }, []); useKeyboardListener({ show, hide }); return useMemo( () => ({ keyboardHeight: (keyboardHeight || DefaultKeyboardHeight) - topSpacing, isKeyboardOpened }), diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index 8ff87856fb..ba794c50d1 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -112,10 +112,21 @@ export default function TabRoot() { header: () => null, tabBarIcon: ({ focused }) => { const tabMenu = tabMenuList.find(tab => tab.name === route.name); - if (tabMenu?.name === TabRouteNameEnum.CHAT && unreadCount > 0) { + if (tabMenu?.name === TabRouteNameEnum.CHAT) { return ( - {formatMessageCountToStr(unreadCount)} + {unreadCount > 0 && {formatMessageCountToStr(unreadCount)}} + + + ); + } else if (tabMenu?.name === TabRouteNameEnum.SETTINGS) { + return ( + + ; export default function NavigationRoot() { diff --git a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx index 755f6c62f1..d437e2464e 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { StyleSheet, View, Image } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; @@ -15,26 +15,78 @@ import { ChatOperationsEnum } from '@portkey-wallet/constants/constants-ca/chat' import CommonAvatar from 'components/CommonAvatar'; import { FontStyles } from 'assets/theme/styles'; import AddContactButton from '../components/AddContactButton'; +import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; +import { ChannelItem } from '@portkey-wallet/im/types'; +import { useChannel, useMuteChannel, usePinChannel, useHideChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import ActionSheet from 'components/ActionSheet'; + +type RouterParams = { + channelInfo?: ChannelItem; +}; const ChatDetails = () => { - const onPressMore = useCallback((event: { nativeEvent: { pageX: any; pageY: any } }) => { - const { pageX, pageY } = event.nativeEvent; - ChatOverlay.showChatPopover({ - list: [ - { - title: ChatOperationsEnum.PROFILE, - iconName: 'chat-profile', - onPress: () => navigationService.navigate('Profile'), - }, - { title: ChatOperationsEnum.PIN, iconName: 'chat-unpin' }, - { title: ChatOperationsEnum.MUTE, iconName: 'chat-unmute' }, - { title: ChatOperationsEnum.DELETE_CHAT, iconName: 'chat-delete' }, - ], - px: pageX, - py: pageY, - position: 'left', - }); - }, []); + const { channelInfo } = useRouterParams(); + const { mute, pin, displayName } = channelInfo || {}; + + const pinChannel = usePinChannel(); + const muteChannel = useMuteChannel(); + const hideChannel = useHideChannel(); + const { sendMessage } = useChannel(channelInfo?.channelUuid || ''); + + const onPressMore = useCallback( + (event: { nativeEvent: { pageX: any; pageY: any } }) => { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showChatPopover({ + list: [ + { + title: ChatOperationsEnum.PROFILE, + iconName: 'chat-profile', + onPress: () => navigationService.navigate('Profile'), + }, + { + title: pin ? ChatOperationsEnum.UNPIN : ChatOperationsEnum.PIN, + iconName: pin ? 'chat-unpin' : 'chat-pin', + onPress: () => { + pinChannel(channelInfo?.channelUuid || '', !pin); + }, + }, + { + title: mute ? ChatOperationsEnum.UNMUTE : ChatOperationsEnum.MUTE, + iconName: mute ? 'chat-unmute' : 'chat-mute', + onPress: () => { + muteChannel(channelInfo?.channelUuid || '', !channelInfo?.channelUuid); + }, + }, + { + title: ChatOperationsEnum.DELETE_CHAT, + iconName: 'chat-delete', + onPress: () => { + ActionSheet.alert({ + title: 'Delete chat?', + buttons: [ + { + title: 'Cancel', + type: 'outline', + }, + { + title: 'Confirm', + type: 'primary', + onPress: () => { + hideChannel(channelInfo?.channelUuid || ''); + }, + }, + ], + }); + }, + }, + ], + px: pageX, + py: pageY, + position: 'left', + }); + }, + [channelInfo?.channelUuid, hideChannel, mute, muteChannel, pin, pinChannel], + ); return ( { - - Name - + + + {displayName} + + {mute && } } rightDom={ diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index 99b5e1184e..3aa18e8819 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -16,6 +16,9 @@ import { useChannelList, useCreateP2pChannel } from '@portkey-wallet/hooks/hooks import { pTd } from 'utils/unit'; import im from '@portkey-wallet/im'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; +import { v4 } from 'uuid'; +import useEffectOnce from 'hooks/useEffectOnce'; +import { formatChatListTime } from '@portkey-wallet/utils/chat'; export default function DiscoverHome() { const createChannel = useCreateP2pChannel(); @@ -63,14 +66,11 @@ export default function DiscoverHome() { useEffect(() => { console.log('channelList', channelList); - const imInstance = im.getInstance(); - if (!imInstance) return; - imInstance.getUserInfo().then(e => console.log(e)); }, [channelList]); const createCha = useCallback(async () => { try { - const result = await createChannel('nutbk-6aaaa-aaaaj-7hatq-cai'); + const result = await createChannel('5h7d6-liaaa-aaaaj-vgmya-cai'); console.log('result', result); } catch (error) { console.log('createChannel: error', error); @@ -82,15 +82,24 @@ export default function DiscoverHome() { }, [initChannelList]); const sendMess = useCallback(async () => { - console.log('=== c7b961729aaa46e6ab73f70fa0ee6055'); + im.service.sendMessage({ + toRelationId: 'e7i7y-giaaa-aaaaj-2ooma-cai', + type: 'TEXT', + sendUuid: v4(), + content: ` hello tho--- ${formatChatListTime(Date.now())} `, + }); + console.log('sendMess', v4()); }, []); + useEffectOnce(() => { + initChannelList(); + }); + return ( - diff --git a/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx b/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx index ffa4779865..b5087e5073 100644 --- a/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/FindMorePeople/index.tsx @@ -1,45 +1,35 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { FlatList, StyleSheet, View, Image } from 'react-native'; +import { FlatList, StyleSheet, View } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; import { TextM } from 'components/CommonText'; import CommonInput from 'components/CommonInput'; -import navigationService from 'utils/navigationService'; import Touchable from 'components/Touchable'; import NoData from 'components/NoData'; import useDebounce from 'hooks/useDebounce'; import RecommendSection from '../components/RecommendSection'; import { BGStyles } from 'assets/theme/styles'; import Svg from 'components/Svg'; +import ContactItem from 'components/ContactItem'; -const mock_data = [{ id: 1 }]; +const mock_data = [{ id: 1, index: 1, name: 'Dav' }]; const FindMorePeople = () => { const [keyword, setKeyword] = useState(''); - const [loading, setLoading] = useState(false); + const [, setLoading] = useState(false); const debounceWord = useDebounce(keyword, 500); const [list, setList] = useState(mock_data); useEffect(() => { setLoading(true); - setList([{ id: 1 }]); + setList(mock_data); setLoading(false); }, [debounceWord]); - const renderItem = useCallback((item: any) => { - console.log(item); - return ( - navigationService.navigate('Profile')}> - - Sally - stranger - navigationService.navigate('ChatDetails')}> - - - - ); + const renderItem = useCallback(({ item }: any) => { + return ; }, []); return ( @@ -49,7 +39,18 @@ const FindMorePeople = () => { scrollViewProps={{ disabled: true }} containerStyles={styles.container}> - setKeyword(v)} /> + setKeyword(v)} + rightIcon={ + keyword ? ( + setKeyword('')}> + + + ) : undefined + } + rightIconContainerStyle={styles.rightIconContainerStyle} + /> {!keyword && ( @@ -69,7 +70,7 @@ export default FindMorePeople; const styles = StyleSheet.create({ container: { - backgroundColor: defaultColors.bg4, + backgroundColor: defaultColors.bg1, flex: 1, ...GStyles.paddingArg(0), }, @@ -84,4 +85,7 @@ const styles = StyleSheet.create({ borderBottomColor: defaultColors.border6, borderBottomWidth: StyleSheet.hairlineWidth, }, + rightIconContainerStyle: { + marginRight: pTd(10), + }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx index 1392f078f5..4e7fadfe05 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx @@ -2,7 +2,7 @@ import GStyles from 'assets/theme/GStyles'; import { TextL, TextM, TextS } from 'components/CommonText'; import Touchable from 'components/Touchable'; import React, { memo, useCallback, useRef, useState } from 'react'; -import { StyleSheet, View, Image, GestureResponderEvent } from 'react-native'; +import { StyleSheet, View, GestureResponderEvent } from 'react-native'; import SwipeableItem, { OpenDirection, SwipeableItemImperativeRef } from 'react-native-swipeable-item'; import { BGStyles, FontStyles } from 'assets/theme/styles'; import Svg from 'components/Svg'; @@ -88,18 +88,17 @@ export default memo(function ChatHomeListItemSwiped(props: ChatHomeListItemSwipe {/* TODO: Remark */} {item.displayName} - + {item.mute && } {formatChatListTime(item.lastPostAt)} - - {/* TODO: Image */} - - {item.lastMessageContent ? item.lastMessageContent : '[Image]'} + + + {item.lastMessageType === 'TEXT' ? item.lastMessageContent : '[Image]'} {item.pin ? ( - + ) : ( @@ -152,10 +151,14 @@ const styles = StyleSheet.create({ width: '100%', height: pTd(2), }, + message: { + maxWidth: pTd(270), + }, messageNum: { borderRadius: pTd(8), backgroundColor: 'red', minWidth: pTd(16), + marginRight: pTd(0), paddingHorizontal: pTd(4), textAlign: 'center', overflow: 'hidden', diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx index ac7b6c08a8..5c0f847012 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx @@ -1,14 +1,14 @@ -import React from 'react'; -import { FlatList, StyleSheet } from 'react-native'; -import GStyles from 'assets/theme/GStyles'; -import { defaultColors } from 'assets/theme'; -import { pTd } from 'utils/unit'; +import React, { useCallback } from 'react'; +import { FlatList, GestureResponderEvent } from 'react-native'; import navigationService from 'utils/navigationService'; import { BGStyles } from 'assets/theme/styles'; import ChatOverlay from '../ChatOverlay'; import ChatHomeListItemSwiped from '../ChatHomeListItemSwiper'; import { ChannelItem } from '@portkey-wallet/im/types'; import NoData from 'components/NoData'; +import { useHideChannel, useMuteChannel, usePinChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import CommonToast from 'components/CommonToast'; +import { handleErrorMessage } from '@portkey-wallet/utils'; type ChatListType = { chatList: ChannelItem[]; @@ -16,43 +16,73 @@ type ChatListType = { export default function ChatList(props: ChatListType) { const { chatList = [] } = props; + const pinChannel = usePinChannel(); + const muteChannel = useMuteChannel(); + const hideChannel = useHideChannel(); + + const onHideChannel = useCallback( + async (item: ChannelItem) => { + try { + await hideChannel(item.channelUuid); + } catch (error) { + CommonToast.fail(handleErrorMessage(error)); + } + }, + [hideChannel], + ); + + const longPress = useCallback( + (event: GestureResponderEvent, item: ChannelItem) => { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showChatPopover({ + list: [ + { + title: item.pin ? 'Unpin' : 'Pin', + iconName: 'chat-pin', + onPress: async () => { + try { + await pinChannel(item.channelUuid, !item.pin); + } catch (error) { + CommonToast.fail(handleErrorMessage(error)); + } + }, + }, + { + title: item.mute ? 'Unmute' : 'Mute', + iconName: 'chat-mute', + onPress: async () => { + try { + await muteChannel(item.channelUuid, !item.mute, true); + } catch (error) { + CommonToast.fail(handleErrorMessage(error)); + } + }, + }, + { + title: 'Delete', + iconName: 'chat-delete', + onPress: () => onHideChannel(item), + }, + ], + px: pageX, + py: pageY, + formatType: 'dynamicWidth', + }); + }, + [muteChannel, onHideChannel, pinChannel], + ); return ( } - renderItem={item => ( + ListEmptyComponent={} + renderItem={({ item }) => ( navigationService.navigate('ChatDetails')} - onLongPress={event => { - const { pageX, pageY } = event.nativeEvent; - - ChatOverlay.showChatPopover({ - list: [ - { - title: 'pin', - iconName: 'chat-pin', - onPress: () => navigationService.navigate('NewChatHome', { item }), - }, - { - title: 'mute', - iconName: 'chat-mute', - onPress: () => navigationService.navigate('NewChatHome', { item }), - }, - { - title: 'delete', - iconName: 'chat-delete', - onPress: () => navigationService.navigate('NewChatHome', { item }), - }, - ], - px: pageX, - py: pageY, - formatType: 'dynamicWidth', - }); - }} - {...item} - onDelete={() => console.log('delete')} + item={item} + onDelete={() => onHideChannel(item)} + onPress={() => navigationService.navigate('ChatDetails', { channelInfo: item })} + onLongPress={event => longPress(event, item)} /> )} /> diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/chatPopover.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/chatPopover.tsx new file mode 100644 index 0000000000..d865e6282c --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/chatPopover.tsx @@ -0,0 +1,154 @@ +import React from 'react'; +import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; +import OverlayModal, { CustomBounds } from 'components/OverlayModal'; +import Touchable from 'components/Touchable'; +import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; +import Svg, { IconName } from 'components/Svg'; +import { pTd } from 'utils/unit'; +import { defaultColors } from 'assets/theme'; + +const vertical = 20; +const horizontal = 20; +const itemHeight = pTd(48); +const BoxWidth = 100; +const horizontalSpacing = 120; +const verticalSpacing = 300; + +export type ListItemType = { onPress?: () => void; title: string; iconName: IconName }; + +type ShowChatPopoverParams = { + list: ListItemType[]; + px?: number; + py?: number; + position?: 'left' | 'right'; + customPosition?: { left?: number; right?: number; top?: number; bottom?: number }; + customBounds?: CustomBounds; + formatType?: 'fixedWidth' | 'dynamicWidth'; +}; + +function formatPositionTop(px: number, py: number, length: number) { + let top = py + horizontal; + if (py > verticalSpacing) { + top = py - length * itemHeight - horizontal; + } + return top; +} + +function formatPosition(px: number, py: number, length: number, position: 'left' | 'right') { + let left = px + vertical; + const top = formatPositionTop(px, py, length); + if ( + (position === 'right' && px > horizontalSpacing) || + (position === 'left' && px > screenWidth - horizontalSpacing) + ) { + left = px - BoxWidth - vertical; + } + return { top, left }; +} + +function formatPositionByDynamicWidth(px: number, py: number, length: number) { + let left = px + vertical; + const top = formatPositionTop(px, py, length); + if (px > screenWidth / 2) { + left = px - screenWidth / 2; + } + return { top, left }; +} + +function ChatPopover({ + list, + customPosition, + formatType, +}: { + formatType: ShowChatPopoverParams['formatType']; + list: ListItemType[]; + customPosition: ShowChatPopoverParams['customPosition']; +}) { + return ( + + + {list.map((item, index) => { + return ( + { + item.onPress?.(); + OverlayModal.hide(); + }} + style={formatType === 'fixedWidth' ? styles.itemStyles : styles.dynamicWidthItemStyles}> + + {item.title} + + ); + })} + + + ); +} + +export function showChatPopover({ + list, + px, + py, + position = 'left', + customPosition, + customBounds, + formatType = 'fixedWidth', +}: ShowChatPopoverParams) { + if (!customPosition) { + customPosition = + formatType === 'fixedWidth' + ? formatPosition(px || 0, py || 0, list.length, position) + : formatPositionByDynamicWidth(px || 0, py || 0, list.length); + } + OverlayModal.show(, { + customBounds: customBounds || { + x: px || customPosition.left || 0, + y: py || customPosition.top || 0, + width: 0, + height: 0, + }, + overlayOpacity: 0, + containerStyle: {}, + style: { backgroundColor: 'transparent' }, + animated: true, + }); +} + +const itemStyle = StyleSheet.create({ + item: { + height: itemHeight, + paddingHorizontal: pTd(16), + flexDirection: 'row', + alignItems: 'center', + }, +}); + +const styles = StyleSheet.create({ + dynamicWidthItemStyles: { + minWidth: 100, + ...itemStyle.item, + }, + itemStyles: { + width: BoxWidth, + ...itemStyle.item, + }, + container: { + paddingVertical: pTd(4), + position: 'absolute', + borderRadius: pTd(6), + zIndex: 100, + minWidth: pTd(136), + shadowOffset: { width: 2, height: 5 }, + backgroundColor: defaultColors.bg1, + shadowColor: defaultColors.shadow1, + shadowOpacity: 0.15, + shadowRadius: 10, + elevation: 2, + }, + textStyles: { + marginLeft: pTd(16), + color: defaultColors.font5, + }, + backgroundBox: { height: screenHeight, width: screenWidth, backgroundColor: 'transparent' }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx index ac4f3edea2..98891dcd1b 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/index.tsx @@ -1,158 +1,6 @@ -import React from 'react'; -import { screenHeight, screenWidth } from '@portkey-wallet/utils/mobile/device'; -import OverlayModal, { CustomBounds } from 'components/OverlayModal'; -import Touchable from 'components/Touchable'; -import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; -import Svg, { IconName, SvgProps } from 'components/Svg'; -import { pTd } from 'utils/unit'; -import { defaultColors } from 'assets/theme'; -import { TextL } from 'components/CommonText'; - -const vertical = 20; -const horizontal = 20; -const itemHeight = pTd(48); -const BoxWidth = 100; -const horizontalSpacing = 120; -const verticalSpacing = 300; - -export type ListItemType = { onPress?: () => void; title: string; iconName: IconName }; - -type ShowChatPopoverParams = { - list: ListItemType[]; - px?: number; - py?: number; - position?: 'left' | 'right'; - customPosition?: { left?: number; right?: number; top?: number; bottom?: number }; - customBounds?: CustomBounds; - formatType?: 'fixedWidth' | 'dynamicWidth'; -}; - -function formatPositionTop(px: number, py: number, length: number) { - let top = py + horizontal; - if (py > verticalSpacing) { - top = py - length * itemHeight - horizontal; - } - return top; -} - -function formatPosition(px: number, py: number, length: number, position: 'left' | 'right') { - let left = px + vertical; - const top = formatPositionTop(px, py, length); - if ( - (position === 'right' && px > horizontalSpacing) || - (position === 'left' && px > screenWidth - horizontalSpacing) - ) { - left = px - BoxWidth - vertical; - } - return { top, left }; -} - -function formatPositionByDynamicWidth(px: number, py: number, length: number) { - let left = px + vertical; - const top = formatPositionTop(px, py, length); - if (px > screenWidth / 2) { - left = px - screenWidth / 2; - } - return { top, left }; -} - -function ChatPopover({ - list, - customPosition, - formatType, -}: { - formatType: ShowChatPopoverParams['formatType']; - list: ListItemType[]; - customPosition: ShowChatPopoverParams['customPosition']; -}) { - return ( - - - {list.map((item, index) => { - return ( - { - item.onPress?.(); - OverlayModal.hide(); - }} - style={formatType === 'fixedWidth' ? styles.itemStyles : styles.dynamicWidthItemStyles}> - - {item.title} - - ); - })} - - - ); -} - -function showChatPopover({ - list, - px, - py, - position = 'left', - customPosition, - customBounds, - formatType = 'fixedWidth', -}: ShowChatPopoverParams) { - if (!customPosition) { - customPosition = - formatType === 'fixedWidth' - ? formatPosition(px || 0, py || 0, list.length, position) - : formatPositionByDynamicWidth(px || 0, py || 0, list.length); - } - OverlayModal.show(, { - customBounds: customBounds || { - x: px || customPosition.left || 0, - y: py || customPosition.top || 0, - width: 0, - height: 0, - }, - overlayOpacity: 0, - containerStyle: {}, - style: { backgroundColor: 'transparent' }, - animated: true, - }); -} +import { showChatPopover } from './chatPopover'; +import { showPreviewImage } from './previewImage'; export default { showChatPopover, + showPreviewImage, }; - -const itemStyle = StyleSheet.create({ - item: { - height: itemHeight, - paddingHorizontal: pTd(16), - flexDirection: 'row', - alignItems: 'center', - }, -}); - -const styles = StyleSheet.create({ - dynamicWidthItemStyles: { - minWidth: 100, - ...itemStyle.item, - }, - itemStyles: { - width: BoxWidth, - ...itemStyle.item, - }, - container: { - paddingVertical: pTd(4), - position: 'absolute', - borderRadius: pTd(6), - zIndex: 100, - minWidth: pTd(136), - shadowOffset: { width: 2, height: 5 }, - backgroundColor: defaultColors.bg1, - shadowColor: defaultColors.shadow1, - shadowOpacity: 0.15, - shadowRadius: 10, - elevation: 2, - }, - textStyles: { - marginLeft: pTd(16), - color: defaultColors.font5, - }, - backgroundBox: { height: screenHeight, width: screenWidth, backgroundColor: 'transparent' }, -}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/previewImage.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/previewImage.tsx new file mode 100644 index 0000000000..8c36c00cbd --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatOverlay/previewImage.tsx @@ -0,0 +1,56 @@ +import React, { useRef } from 'react'; +import { screenWidth, windowHeight, windowWidth } from '@portkey-wallet/utils/mobile/device'; +import OverlayModal, { CustomBounds } from 'components/OverlayModal'; +import { ImageProps, StyleSheet } from 'react-native'; +import CacheImage from 'components/CacheImage'; +import GStyles from 'assets/theme/GStyles'; +import TransformView from 'rn-teaset/components/TransformView/TransformView'; +type PreviewImageProps = { source: ImageProps['source']; thumb?: ImageProps['source'] }; + +function PreviewImage({ source, thumb }: PreviewImageProps) { + const scaleRef = useRef(1); + // const [init, setInit] = useState(true); + // useEffectOnce(() => { + // const timer = setTimeout(() => { + // setInit(false); + // }, 200); + // return () => { + // timer && clearTimeout(timer); + // }; + // }); + // if (init) return null; + + return ( + { + if (scaleRef.current !== 1) return; + if (Math.abs(translateY) > 50) OverlayModal.hide(); + }} + onTransforming={(translateX: number, translateY: number, scale: number) => { + scaleRef.current = scale; + }} + style={GStyles.flex1} + containerStyle={styles.containerStyle}> + + + ); +} + +export function showPreviewImage({ + customBounds, + source, + thumb, +}: { + customBounds: CustomBounds; +} & PreviewImageProps) { + OverlayModal.show(, { + customBounds: customBounds, + position: 'center', + containerStyle: { ...GStyles.flex1, width: screenWidth }, + }); +} + +const styles = StyleSheet.create({ + containerStyle: { height: windowHeight * 0.8, width: windowWidth }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index 183759af32..2d3fb1f543 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -1,12 +1,17 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { GiftedChat, GiftedChatProps, IMessage, MessageImageProps, MessageTextProps } from 'react-native-gifted-chat'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import { + GiftedChat, + GiftedChatProps, + IMessage, + Message, + MessageImageProps, + MessageProps, + MessageTextProps, +} from 'react-native-gifted-chat'; import initialMessages from '../messages'; import { AccessoryBar, BottomBarContainer } from '../InputToolbar'; -import { renderSystemMessage, renderMessage } from '../MessageContainer'; import { randomId } from '@portkey-wallet/utils'; import { Keyboard } from 'react-native'; -import GStyles from 'assets/theme/GStyles'; -import Touchable from 'components/Touchable'; import { useChatsDispatch } from '../context/hooks'; import CustomBubble from '../CustomBubble'; import { setBottomBarStatus, setChatText, setShowSoftInputOnFocus } from '../context/chatsContext'; @@ -15,7 +20,10 @@ import MessageText from '../Message/MessageText'; import { destroyChatInputRecorder, initChatInputRecorder } from 'pages/Chat/utils'; import MessageImage from '../Message/MessageImage'; -import { BGStyles } from 'assets/theme/styles'; +import { useThrottleCallback } from '@portkey-wallet/hooks'; +import { StyleSheet } from 'react-native'; +import { pTd } from 'utils/unit'; +import Touchable from 'components/Touchable'; const user = { _id: 1, @@ -25,6 +33,13 @@ const user = { const Empty = () => null; +const ListViewProps = { + windowSize: 1, + maxToRenderPerBatch: 5, + removeClippedSubviews: false, + legacyImplementation: true, +}; + const ChatsUI = () => { const [messages, setMessages] = useState([]); const dispatch = useChatsDispatch(); @@ -46,7 +61,7 @@ const ChatsUI = () => { }; }); - const onDismiss = useCallback(() => { + const onDismiss = useThrottleCallback(() => { Keyboard.dismiss(); dispatch(setBottomBarStatus(undefined)); }, [dispatch]); @@ -64,33 +79,53 @@ const ChatsUI = () => { const renderBubble = useCallback((data: any) => { return ; }, []); + const listViewProps: GiftedChatProps['listViewProps'] = useMemo(() => { + return { + ...ListViewProps, + onScrollBeginDrag: onDismiss, + }; + }, [onDismiss]); + const renderMessage = useCallback( + (props: MessageProps) => { + return ( + + + + ); + }, + [onDismiss], + ); return ( <> - - - + @@ -101,3 +136,14 @@ const ChatsUI = () => { export default function Chats() { return ; } + +const styles = StyleSheet.create({ + leftMessageContainer: { + marginLeft: pTd(16), + marginRight: 0, + }, + rightMessageContainer: { + marginLeft: 0, + marginRight: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx index 2cc711c771..bd042a30b1 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/CustomBubble/index.tsx @@ -2,19 +2,16 @@ import React from 'react'; import { StyleSheet } from 'react-native'; import { BubbleProps, IMessage } from 'react-native-gifted-chat'; import { Bubble } from 'react-native-gifted-chat'; -import Touchable from 'components/Touchable'; import { defaultColors } from 'assets/theme'; import { pTd } from 'utils/unit'; export default function CustomBubble(props: BubbleProps) { return ( - - - + ); } diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx index e8c5cff0d6..db0e90e521 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx @@ -1,6 +1,5 @@ import React, { memo, useCallback, useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; -import { pTd } from 'utils/unit'; import GStyles from 'assets/theme/GStyles'; import Emoticons from '../Emoticons'; @@ -17,7 +16,6 @@ export const AccessoryBar = memo( const bottomBarStatus = useBottomBarStatus(); const dispatch = useChatsDispatch(); const showTools = useMemo(() => !!bottomBarStatus, [bottomBarStatus]); - const onPress = useCallback( (item: EmojiItem) => { const text = handleInputText(item.code); @@ -27,8 +25,6 @@ export const AccessoryBar = memo( ); const onDelete = useCallback(() => { const text = handleDeleteText(); - console.log(text, '=====text'); - dispatch(setChatText(text)); }, [dispatch]); return ( @@ -41,7 +37,7 @@ export const AccessoryBar = memo( ]}> - + ); }, @@ -49,24 +45,6 @@ export const AccessoryBar = memo( ); const styles = StyleSheet.create({ - toolsItem: { - width: '25%', - margin: pTd(2), - backgroundColor: 'skyblue', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - hideInput: { - position: 'absolute', - top: -1000, - opacity: 0, - ...GStyles.flex1, - }, - inputContainerStyle: { - height: 80, - ...GStyles.flex1, - }, hide: { width: 0, height: 0, diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx index 06e91bb568..0fa2000fd4 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/BottomBarContainer/index.tsx @@ -1,12 +1,10 @@ import Svg from 'components/Svg'; -import React, { ReactNode, memo, useCallback, useRef, useState } from 'react'; +import React, { ReactNode, memo, useCallback, useRef } from 'react'; import { StyleSheet, View } from 'react-native'; import { Actions } from 'react-native-gifted-chat'; import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; -import { isIOS } from '@portkey-wallet/utils/mobile/device'; import { Animated } from 'react-native'; -import { TextInput } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import useEffectOnce from 'hooks/useEffectOnce'; import { useKeyboardAnim } from '../../hooks'; @@ -15,7 +13,8 @@ import { ChatBottomBarStatus } from 'store/chat/slice'; import { setBottomBarStatus } from '../../context/chatsContext'; import { BGStyles } from 'assets/theme/styles'; import { SendMessageButton } from '../SendMessageButton'; -import { ChatInput } from '../ChatInput'; +import { ChatInput, ChatInputBar } from '../ChatInput'; +import { isIOS } from '@portkey-wallet/utils/mobile/device'; export const ActionsIcon = memo(function ActionsIcon({ onPress }: { onPress?: () => void }) { return ( @@ -28,65 +27,29 @@ export const ActionsIcon = memo(function ActionsIcon({ onPress }: { onPress?: () ); }); -export function AndroidInputContainer({ - children, - textInputRef, -}: { - children?: ReactNode; - textInputRef: React.RefObject; -}) { - const timer = useRef(); - - const [hide, setHide] = useState(); - const bottomBarStatus = useBottomBarStatus(); - - const onPressIn = useCallback(() => { - if (bottomBarStatus === undefined) { - setHide(true); - timer.current = setTimeout(() => { - setHide(false); - }, 300); - } - textInputRef.current?.focus(); - }, [bottomBarStatus, textInputRef]); - useEffectOnce(() => { - return () => { - timer.current && clearTimeout(timer.current); - }; - }); - return ( - - - {children} - - - ); -} - export function BottomBarContainer({ children }: { children?: ReactNode; showKeyboard?: () => void }) { const bottomBarStatus = useBottomBarStatus(); const dispatch = useChatsDispatch(); const text = useChatText(); - const textInputRef = useRef(null); + const textInputRef = useRef(null); const keyboardAnim = useKeyboardAnim({ textInputRef }); const timer = useRef(); - const inputFocus = useCallback(() => { - textInputRef.current?.focus(); - timer.current && clearTimeout(timer.current); - timer.current = setTimeout(() => { - dispatch(setBottomBarStatus(ChatBottomBarStatus.input)); - }, 300); - }, [dispatch]); + const inputFocus = useCallback( + (autoHide?: boolean) => { + textInputRef.current?.focus(autoHide); + timer.current && clearTimeout(timer.current); + timer.current = setTimeout(() => { + dispatch(setBottomBarStatus(ChatBottomBarStatus.input)); + }, 300); + }, + [dispatch], + ); const onPressActionButton = useCallback( (status: ChatBottomBarStatus) => { if (bottomBarStatus === status) { - inputFocus(); + inputFocus(bottomBarStatus === ChatBottomBarStatus.tools); } else { dispatch(setBottomBarStatus(status)); textInputRef.current?.blur(); @@ -102,72 +65,36 @@ export function BottomBarContainer({ children }: { children?: ReactNode; showKey }); return ( - <> - + + onPressActionButton(ChatBottomBarStatus.tools)} /> - {isIOS ? ( - - ) : ( - - - - )} + {children} - + ); } const styles = StyleSheet.create({ wrap: { + overflow: 'hidden', + }, + barWrap: { position: 'relative', paddingHorizontal: pTd(16), paddingVertical: pTd(10), + marginBottom: isIOS ? 0 : 5, }, fileSvg: { marginLeft: 0, marginBottom: pTd(8), marginRight: pTd(8), }, - input: { - width: '100%', - paddingLeft: pTd(16), - }, sendStyle: { marginBottom: pTd(8), marginLeft: pTd(8), width: pTd(24), height: pTd(24), }, - toolsItem: { - width: '25%', - margin: pTd(2), - backgroundColor: 'skyblue', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }, - hideInput: { - position: 'absolute', - top: -1000, - opacity: 0, - ...GStyles.flex1, - }, - inputContainerStyle: { - height: 80, - ...GStyles.flex1, - }, - hide: { - width: 0, - height: 0, - }, - absolute: { - top: 0, - bottom: 0, - left: 0, - right: 0, - position: 'absolute', - zIndex: 999, - }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx index 7ee68a3dfd..6975223906 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ChatInput/index.tsx @@ -1,15 +1,27 @@ -import React, { forwardRef, memo, useCallback } from 'react'; -import { NativeSyntheticEvent, TextInput, TextInputSelectionChangeEventData, View } from 'react-native'; -import { useChatText, useChatsDispatch, useIsShowEmoji } from '../../context/hooks'; +import React, { forwardRef, memo, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; +import { + View, + TextInput, + TextInputProps, + NativeSyntheticEvent, + TextInputContentSizeChangeEventData, + TextInputSelectionChangeEventData, +} from 'react-native'; +import { useBottomBarStatus, useChatText, useChatsDispatch } from '../../context/hooks'; import GStyles from 'assets/theme/GStyles'; import { setChatText } from '../../context/chatsContext'; import Svg from 'components/Svg'; import { StyleSheet } from 'react-native'; import { pTd } from 'utils/unit'; -import { defaultColors } from 'assets/theme'; import { ChatBottomBarStatus } from 'store/chat/slice'; import Touchable from 'components/Touchable'; import { chatInputRecorder } from 'pages/Chat/utils'; +import useEffectOnce from 'hooks/useEffectOnce'; +import { defaultColors } from 'assets/theme'; +import { isIOS } from '@portkey-wallet/utils/mobile/device'; +export interface ChatInput extends TextInput { + focus: (autoHide?: boolean) => void; +} export const EmojiIcon = memo(function EmojiIcon({ onPress }: { onPress?: () => void }) { return ( @@ -18,17 +30,20 @@ export const EmojiIcon = memo(function EmojiIcon({ onPress }: { onPress?: () => ); }); -export const ChatInput = forwardRef(function InputBar( - { - onPressActionButton, - }: { - onPressActionButton: (status: ChatBottomBarStatus) => void; - }, - _ref, -) { + +const ChatInput = forwardRef(function Input(props: TextInputProps, _ref) { + const bottomBarStatus = useBottomBarStatus(); const dispatch = useChatsDispatch(); const text = useChatText(); - const isShowEmoji = useIsShowEmoji(); + + const onSelectionChange = useCallback( + ({ nativeEvent }: NativeSyntheticEvent) => { + if (bottomBarStatus === ChatBottomBarStatus.emoji) return; + chatInputRecorder?.setSelection(nativeEvent.selection); + }, + [bottomBarStatus], + ); + const onChangeText = useCallback( (v: string) => { dispatch(setChatText(v)); @@ -36,27 +51,121 @@ export const ChatInput = forwardRef(function InputBar( }, [dispatch], ); - const onSelectionChange = useCallback( - ({ nativeEvent }: NativeSyntheticEvent) => { - if (isShowEmoji) return; - chatInputRecorder?.setSelection(nativeEvent.selection); - }, - [isShowEmoji], + return ( + + ); +}); + +export const AndroidChatInputBar = forwardRef(function InputBar( + { + onPressActionButton, + }: { + onPressActionButton: (status: ChatBottomBarStatus) => void; + }, + _ref, +) { + const timer = useRef(); + const textInputRef = useRef(); + const [hide, setHide] = useState(); + const bottomBarStatus = useBottomBarStatus(); + const [inputBlur, setInputBlur] = useState(true); + const dimensionsRef = useRef<{ width: number; height: number }>(); + + const onFocus = useCallback((autoHide?: boolean) => { + if (autoHide) { + setHide(true); + timer.current = setTimeout(() => { + setHide(false); + }, 400); + } + textInputRef.current?.focus(); + }, []); + + useImperativeHandle(_ref, () => ({ + focus: (autoHide?: boolean) => onFocus(autoHide), + blur: () => textInputRef.current?.blur(), + setNativeProps: (nativeProps: object) => textInputRef.current?.setNativeProps(nativeProps), + })); + + const onPressIn = useCallback(() => { + if (!bottomBarStatus || bottomBarStatus !== ChatBottomBarStatus.emoji) { + onFocus(true); + } else { + textInputRef.current?.focus(); + } + }, [bottomBarStatus, onFocus]); + + useEffectOnce(() => { + return () => { + timer.current && clearTimeout(timer.current); + }; + }); + + const determineInputSizeChange = useCallback((dimensions: { width: number; height: number }) => { + // Support earlier versions of React Native on Android. + if (!dimensions) return; + if ( + !dimensionsRef || + !dimensionsRef.current || + (dimensionsRef.current && + (dimensionsRef.current.width !== dimensions.width || dimensionsRef.current.height !== dimensions.height)) + ) { + dimensionsRef.current = dimensions; + } + }, []); + + const handleContentSizeChange = useCallback( + ({ nativeEvent: { contentSize } }: NativeSyntheticEvent) => + determineInputSizeChange(contentSize), + [determineInputSizeChange], + ); + const inputDisabled = useMemo(() => !inputBlur || hide, [hide, inputBlur]); + return ( + + + + setInputBlur(true)} + onFocus={() => setInputBlur(false)} + onContentSizeChange={handleContentSizeChange} + /> + + + onPressActionButton(ChatBottomBarStatus.emoji)} /> + ); +}); + +export const IOSChatInputBar = forwardRef(function InputBar( + { + onPressActionButton, + }: { + onPressActionButton: (status: ChatBottomBarStatus) => void; + }, + _ref, +) { return ( - - + + onPressActionButton(ChatBottomBarStatus.emoji)} /> ); }); +// Android interacts differently than iOS +export const ChatInputBar = isIOS ? IOSChatInputBar : AndroidChatInputBar; const styles = StyleSheet.create({ emojiSvg: { @@ -76,13 +185,25 @@ const styles = StyleSheet.create({ paddingVertical: pTd(6), minHeight: pTd(40), maxHeight: pTd(200), + ...GStyles.flex1, + }, + androidInputWrapStyle: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + minHeight: pTd(28), + maxHeight: pTd(188), + ...GStyles.flex1, }, input: { width: '100%', + padding: 0, paddingLeft: pTd(16), }, - hide: { - width: 0, - height: 0, + hideInput: { + position: 'absolute', + top: -1000, + opacity: 0, + ...GStyles.flex1, }, }); diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx index 74eccecf38..6c36821673 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, useCallback } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; @@ -9,15 +9,21 @@ import * as ImagePicker from 'expo-image-picker'; import GStyles from 'assets/theme/GStyles'; import SendPicModal from '../SendPicModal'; import BookmarkOverlay from '../../BookmarkOverlay'; +import Svg, { IconName } from 'components/Svg'; +import { defaultColors } from 'assets/theme'; +import { FontStyles } from 'assets/theme/styles'; +import { ViewStyleType } from 'types/styles'; +import { bindUriToLocalImage } from 'utils/fs/img'; -export const ToolBar = memo(function ToolBar() { +export const ToolBar = memo(function ToolBar({ style }: { style?: ViewStyleType }) { const selectPhoto = useCallback(async () => { - const result = (await ImagePicker.launchImageLibraryAsync({ + const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: false, allowsMultipleSelection: false, - })) as unknown as { uri: string }; - + }); + if (result.cancelled) return; + await bindUriToLocalImage(result.uri, 'https://google.com'); if (result.uri) { SendPicModal.showSendPic({ uri: result.uri, @@ -35,37 +41,61 @@ export const ToolBar = memo(function ToolBar() { } }, []); - return ( - - navigationService.navigate('ChatCamera')}> - camera - - - photo - - + const toolList = useMemo((): { label: string; icon: IconName; onPress: () => void }[] => { + return [ + { + label: 'Camera', + icon: 'chat-camera', + onPress: () => navigationService.navigate('ChatCamera'), + }, + { + label: 'Album', + icon: 'chat-album', + onPress: selectPhoto, + }, + + { + label: 'Bookmarks', + icon: 'chat-bookmark', + onPress: () => BookmarkOverlay.showBookmarkList({ onPressCallBack: item => { console.log(item); }, - }) - }> - Bookmark - + }), + }, + ]; + }, [selectPhoto]); + + return ( + + {toolList.map(ele => ( + + + + + {ele.label} + + ))} ); }); const styles = StyleSheet.create({ + wrap: { + padding: pTd(16), + }, toolsItem: { - width: '25%', - margin: pTd(2), - backgroundColor: 'skyblue', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + width: pTd(77), + height: pTd(76), + marginRight: pTd(8), + }, + toolsItemIconWrap: { + backgroundColor: defaultColors.bg1, + marginBottom: pTd(8), + width: pTd(52), + height: pTd(52), + borderRadius: pTd(6), }, hideInput: { position: 'absolute', diff --git a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx index b373185fa4..43c39f6ebf 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageImage/index.tsx @@ -7,11 +7,23 @@ import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; import ChatOverlay from '../../ChatOverlay'; +const MockImgSource = { + uri: 'https://img0.baidu.com/it/u=925843206,3288141497&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=769', +}; + function MessageImage(props: MessageImageProps) { const { currentMessage } = props; return ( { + const { pageX, pageY } = event.nativeEvent; + ChatOverlay.showPreviewImage({ + source: MockImgSource, + thumb: MockImgSource, + customBounds: { x: pageX, y: pageY, width: 0, height: 0 }, + }); + }} onLongPress={event => { const { pageX, pageY } = event.nativeEvent; ChatOverlay.showChatPopover({ @@ -24,13 +36,7 @@ function MessageImage(props: MessageImageProps) { formatType: 'dynamicWidth', }); }}> - +
    + +
    +
    + {discover.name} + +
    +
    {discover.url}
    +
    +
    + ), + click: () => onClick(discover), + })), + [mockData, onClick], + ); + + return ( +
    +
    +

    {`Bookmarks`}

    + +
    +
    + {mockData.length === 0 ? ( +
    +

    No Bookmarks

    +
    + ) : ( + + )} +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/components/BookmarkListDrawer/index.less b/packages/web-extension-did/app/web/pages/ChatBox/components/BookmarkListDrawer/index.less new file mode 100644 index 0000000000..d5398eeb6d --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/components/BookmarkListDrawer/index.less @@ -0,0 +1,15 @@ +.bookmark-list-drawer { + overflow-x: auto; + .@{app-prefix}-drawer-mask{ + min-width: 360px; + } + .@{app-prefix}-drawer-content-wrapper{ + min-width: 360px; + } + .@{app-prefix}-drawer-content { + border-radius: 8px 8px 0 0; + .@{app-prefix}-drawer-body { + padding: 0; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/components/BookmarkListDrawer/index.tsx b/packages/web-extension-did/app/web/pages/ChatBox/components/BookmarkListDrawer/index.tsx new file mode 100644 index 0000000000..482c2d707d --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/components/BookmarkListDrawer/index.tsx @@ -0,0 +1,25 @@ +import { DrawerProps } from 'antd'; +import BaseDrawer from 'components/BaseDrawer'; +import BookmarkList from '../BookmarkList'; +import './index.less'; + +interface CustomSelectProps extends DrawerProps { + open: boolean; + onClose?: () => void; + onClick: (url: string) => void; +} + +export default function BookmarkListDrawer({ open, onClose, onClick, ...props }: CustomSelectProps) { + return ( + + + + ); +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.less b/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.less new file mode 100644 index 0000000000..a4f90d1a1b --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.less @@ -0,0 +1,44 @@ +@import '../../../../assets/theme/color.less'; + +.portkey-photo-send-modal { + .portkey-modal-header { + display: none; + } + + .portkey-modal-body { + .modal-content { + padding: 24px 20px; + border-bottom: 1px solid @border-2; + img { + width: 100%; + height: 100%; + } + } + } + + .portkey-modal-footer { + border-top: none; + padding: 16px; + + .modal-footer { + gap: 16px; + } + + .portkey-btn { + height: 48px; + border-radius: 24px; + font-size: 16px; + line-height: 22px; + } + .portkey-btn-primary { + border: 1px solid @border-3; + color: @font-5; + background-color: @bg-7; + } + .portkey-btn-default { + color: @font-9; + background-color: @bg-11; + border: 1px solid @border-3; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.tsx b/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.tsx new file mode 100644 index 0000000000..bded0876e2 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.tsx @@ -0,0 +1,44 @@ +import { Button } from 'antd'; +import { useTranslation } from 'react-i18next'; +import './index.less'; +import CommonModal from 'components/CommonModal'; +import { useState } from 'react'; + +interface PhotoSendModalProps { + open: boolean; + url: string; + onConfirm: () => void; + onCancel: () => void; +} + +export default function PhotoSendModal({ open, url, onConfirm, onCancel }: PhotoSendModalProps) { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + + const handleConfirm = () => { + setLoading(true); + onConfirm(); + }; + + return ( + + + + + }> +
    + image-send +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/index.less b/packages/web-extension-did/app/web/pages/ChatBox/index.less new file mode 100644 index 0000000000..436a76594b --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/index.less @@ -0,0 +1,97 @@ +@import '../../assets/theme/color.less'; + +.chat-box-page { + height: 600px; + background-color: @bg-11;; + .chat-box-top { + flex-shrink: 0; + padding-top: 16px; + background-color: @bg-13; + border-bottom: 1px solid @border-2; + .title-element { + align-items: center; + .name-text { + margin-left: 8px; + max-width: 150px; + font-size: 24px; + line-height: 28px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + color: @font-11; + } + .custom-svg.mute-icon { + margin-left: 4px; + width: 12px; + height: 12px; + display: flex; + align-items: center; + svg { + path { + fill: @border-13; + } + } + } + } + .setting-header-wrapper { + border: none; + background-color: @bg-13; + .right-element { + gap: 16px; + } + } + } + .add-contact-tip { + position: relative; + flex-shrink: 0; + height: 48px; + border-bottom: 1px solid @border-2; + font-size: 16px; + line-height: 22px; + color: @font-9; + .content { + position: relative; + height: 100%; + margin: auto; + gap: 16px; + .text { + cursor: pointer; + } + .addcontact-icon { + cursor: pointer; + width: 20px; + height: 20px; + } + } + .close2-icon { + position: absolute; + top: 16px; + right: 16px; + width: 12px; + height: 12px; + cursor: pointer; + } + } + .chat-box-content { + width: 100%; + flex: 1; + overflow: auto; + ::-webkit-scrollbar { + display: none; + } + } + .chat-box-footer { + flex-shrink: 0; + } +} + +.chat-box-popover { + top: 60px !important; + left: 220px !important; + .portkey-popover-inner { + border-radius: 6px; + .portkey-popover-inner-content { + padding: 0 12px; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/index.tsx b/packages/web-extension-did/app/web/pages/ChatBox/index.tsx new file mode 100644 index 0000000000..54e3440a81 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/index.tsx @@ -0,0 +1,197 @@ +import { useMemo, useState } from 'react'; +import { useLocation, useNavigate, useParams } from 'react-router'; +import SettingHeader from 'pages/components/SettingHeader'; +import CustomSvg from 'components/CustomSvg'; +import { Popover, Upload, UploadFile } from 'antd'; +import { PopoverMenuList, MessageList, InputBar, StyleProvider } from '@portkey-wallet/im-ui-web'; +import { Avatar } from '@portkey-wallet/im-ui-web'; +import { RcFile } from 'antd/lib/upload/interface'; +import PhotoSendModal from './components/PhotoSendModal'; +import { useChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { useEffectOnce } from 'react-use'; +import { formatChatListTime } from '@portkey-wallet/utils/chat'; +import BookmarkListDrawer from './components/BookmarkListDrawer'; +import im from '@portkey-wallet/im'; +import './index.less'; + +enum MessageTypeWeb { + 'SYS' = 'system', + 'TEXT' = 'text', + 'CARD' = '', + 'IMAGE' = 'photo', + 'ANNOUNCEMENT' = '', + 'BATCH_TRANSFER' = '', +} + +export default function Session() { + const { channelUuid } = useParams(); + const { state } = useLocation(); + const navigate = useNavigate(); + const [file, setFile] = useState(); + const [previewImage, setPreviewImage] = useState(); + const [showBookmark, setShowBookmark] = useState(false); + // TODO + const isStranger = true; + const [showStrangerTip, setShowStrangerTip] = useState(true); + const { list, init, sendMessage, pin, mute, exit } = useChannel(`${channelUuid}`); + console.log(file); + useEffectOnce(() => { + init(); + }); + + // TODO photo + + const messageList: any = useMemo(() => { + return list.map((item) => { + return { + id: item.id, + // sendUuid: item.sendUuid, // TODO + title: item.fromName, + position: item.from === im.userInfo?.relationId ? 'right' : 'left', // TODO '5h7d6-liaaa-aaaaj-vgmya-cai' + text: item.parsedContent, + type: MessageTypeWeb[item.type], + dateString: formatChatListTime(item.createAt), + }; + }); + }, [list]); + const chatPopList = useMemo( + () => [ + { + key: 'profile', + leftIcon: , + children: 'Profile', + // TODO + onClick: () => navigate('/profile'), + }, + { + key: state?.pin ? 'un-pin' : 'pin', + leftIcon: , + children: state?.pin ? 'Unpin' : 'Pin', + onClick: () => pin(!state.pin), + }, + { + key: state?.muted ? 'un-mute' : 'mute', + leftIcon: , + children: state?.muted ? 'Unmute' : 'Mute', + onClick: () => mute(!state?.muted), + }, + { + key: 'delete', + leftIcon: , + children: 'Delete', + onClick: exit, + }, + isStranger && { + key: 'add-contact', + leftIcon: , + children: 'Add Contact', + // TODO + onClick: () => navigate('/add-contact'), + }, + ], + [exit, isStranger, mute, navigate, pin, state?.muted, state.pin], + ); + const uploadProps = { + className: 'chat-input-upload', + showUploadList: false, + accept: 'image/*', + beforeUpload: async (paramFile: RcFile) => { + setFile(paramFile); + const src = await new Promise((resolve) => { + const reader = new FileReader(); + reader.readAsDataURL(paramFile); + reader.onload = () => { + resolve(reader.result); + }; + }); + setPreviewImage(src as string); + return false; + }, + }; + + const inputMorePopList = [ + { + key: 'album', + leftIcon: , + children: ( + + Picture + + ), + }, + { + key: 'bookmark', + leftIcon: , + children: 'Bookmark', + onClick: () => setShowBookmark(true), + }, + ]; + + // TODO + const handleUpload = () => { + // sendImage(file) + }; + + return ( +
    +
    + + +
    {state?.title}
    + {state?.muted && } +
    + } + leftCallBack={() => navigate('/chat-list')} + rightElement={ +
    + }> + + + navigate('/chat-list')} /> +
    + } + /> +
    + {isStranger && showStrangerTip && ( +
    +
    + + Add Contact +
    + setShowStrangerTip(false)} /> +
    + )} +
    + + + +
    +
    + + + +
    + { + setPreviewImage(''); + setFile(undefined); + }} + /> + setShowBookmark(false)} + onClick={sendMessage} + /> + + ); +} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/mock.ts b/packages/web-extension-did/app/web/pages/ChatBox/mock.ts new file mode 100644 index 0000000000..ff5eabb009 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/mock.ts @@ -0,0 +1,158 @@ +export const mockTemp: any = [ + { + id: 'id', + title: '', + position: 'right', + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'text', + date: new Date(), + }, + { + id: 'id', // id + sendUuid: '', // sendUuid + title: '', // fromName + position: 'left', // fromName duibi + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', // parsedContent + type: 'text', // type + date: new Date(), // createAt + }, + { + id: 'id', // + title: '', + position: 'right', + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'bookmark', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'left', + data: { + id: 'img1', + uri: 'https://avatars.githubusercontent.com/u/80540635?v=4', + alt: 'alt', + status: { + download: true, + }, + }, + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'photo', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'right', + data: { + id: 'img2', + uri: 'https://avatars.githubusercontent.com/u/80540635?v=4', + alt: 'alt', + status: { + download: true, + }, + }, + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'photo', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'right', + text: '07-30', + type: 'system', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'right', + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'text', + date: new Date(), + // focus: false, + // titleColor: '', + // retracted: false, + }, + { + id: 'id', + title: '', + position: 'left', + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'text', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'right', + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'bookmark', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'left', + data: { + id: 'img3', + uri: 'https://avatars.githubusercontent.com/u/80540635?v=4', + alt: 'alt', + status: { + download: true, + }, + }, + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'photo', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, + { + id: 'id', + title: '', + position: 'right', + data: { + id: 'img4', + uri: 'https://avatars.githubusercontent.com/u/80540635?v=4', + alt: 'alt', + status: { + download: true, + }, + }, + text: 'Hi, Do you want to buy or sell some tokens? Buy or sell some tokens?', + type: 'photo', + date: new Date(), + focus: false, + titleColor: '', + notch: false, + retracted: false, + }, +]; diff --git a/packages/web-extension-did/app/web/pages/ChatBox/utils/index.ts b/packages/web-extension-did/app/web/pages/ChatBox/utils/index.ts new file mode 100644 index 0000000000..5b839ba97b --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatBox/utils/index.ts @@ -0,0 +1,7 @@ +export const getPixel = (url: string) => { + const img = new Image(); + img.src = url; + img.onload = () => { + return [img.width, img.height]; + }; +}; diff --git a/packages/web-extension-did/app/web/pages/ChatEntry/index.less b/packages/web-extension-did/app/web/pages/ChatEntry/index.less new file mode 100644 index 0000000000..caff1edaee --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatEntry/index.less @@ -0,0 +1,33 @@ +@import '../../assets/theme/color.less'; + +.chat-entry { + position: relative; + width: 48px; + height: 48px; + border-radius: 50%; + background-color: @bg-7; + cursor: pointer; + + .chat-entry-container { + width: 100%; + height: 100%; + .custom-svg.chatentry-icon { + width: 24px; + height: 24px; + svg { + path { + fill: @bg-11; + } + } + } + } + + .chat-entry-unread { + position: absolute; + top: -1px; + left: 32px; + .portkey-unread-tip { + border: 1px solid @border-12; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ChatEntry/index.tsx b/packages/web-extension-did/app/web/pages/ChatEntry/index.tsx new file mode 100644 index 0000000000..d363eeacc2 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatEntry/index.tsx @@ -0,0 +1,25 @@ +import { UnreadTip } from '@portkey-wallet/im-ui-web'; +import { useNavigate } from 'react-router'; +import CustomSvg from 'components/CustomSvg'; +import './index.less'; + +interface IChatEntry { + unread?: number; +} + +export default function ChatEntry(props: IChatEntry) { + const { unread = 0 } = props; + const navigate = useNavigate(); + return ( +
    navigate('/chat-list')}> +
    + +
    + {unread > 0 && ( +
    + +
    + )} +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/ChatList/index.less b/packages/web-extension-did/app/web/pages/ChatList/index.less new file mode 100644 index 0000000000..468e043051 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatList/index.less @@ -0,0 +1,43 @@ +@import '../../assets/theme/color.less'; + +.chat-list-page { + height: 100%; + background-color: @bg-11; + ::-webkit-scrollbar { + display: none; + } + .chat-list-top { + overflow: hidden; + padding-top: 16px; + flex-shrink: 0; + border-bottom: 1px solid @border-2;; + background-color: @bg-13; + z-index: 100; + .setting-header-wrapper { + border: none; + .right-element { + gap: 16px; + .custom-svg { + width: 20px; + height: 20px; + } + } + } + } + .chat-list-content { + overflow-y: auto; + height: calc(100% - 96px); + .no-message { + .message-icon { + width: 64px; + height: 64px; + } + + margin-top: 100px; + gap: 24px; + font-size: 14px; + line-height: 20px; + color: @font-13; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ChatList/index.tsx b/packages/web-extension-did/app/web/pages/ChatList/index.tsx new file mode 100644 index 0000000000..c7ee317d44 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatList/index.tsx @@ -0,0 +1,110 @@ +import { Popover } from 'antd'; +import { useMemo } from 'react'; +import { useNavigate } from 'react-router'; +import { useTranslation } from 'react-i18next'; +import { ChatList as ChannelList, PopoverMenuList } from '@portkey-wallet/im-ui-web'; + +import CustomSvg from 'components/CustomSvg'; +import SettingHeader from 'pages/components/SettingHeader'; +import { useChannelList, usePinChannel, useMuteChannel, useHideChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { useEffectOnce } from 'react-use'; +import { formatChatListTime } from '@portkey-wallet/utils/chat'; +import './index.less'; + +export default function ChatList() { + const navigate = useNavigate(); + const { t } = useTranslation(); + const pinChannel = usePinChannel(); + const muteChannel = useMuteChannel(); + const hideChannel = useHideChannel(); + const { + list: chatList, + init: initChannelList, + next: nextChannelList, + hasNext: hasNextChannelList, + } = useChannelList(); + + const onConfirm = () => { + // TODO + }; + const popList = useMemo( + () => [ + { + key: 'newChat', + leftIcon: , + children: 'New Chat', + onClick: () => navigate('/new-chat'), + }, + { + key: 'add-contact', + leftIcon: , + children: 'Add Contact', + onClick: onConfirm, + }, + ], + [navigate], + ); + const rightElement = useMemo( + () => ( +
    + navigate('/chat-list-search')} /> + }> + + + navigate('/')} /> +
    + ), + [navigate, popList], + ); + + const transChatList = useMemo(() => { + return chatList.map((item) => { + return { + id: item.channelUuid, + letterItem: item.displayName.substring(0, 1).toUpperCase(), + title: item.displayName, + subtitle: item.lastMessageContent, + dateString: formatChatListTime(item.lastPostAt), + muted: item.mute, + pin: item.pin, + unread: item.unreadMessageCount, + }; + }); + }, [chatList]); + + useEffectOnce(() => { + initChannelList(); + }); + + return ( +
    +
    + navigate('/')} rightElement={rightElement} /> +
    +
    + {chatList.length === 0 ? ( +
    + +
    No Message
    +
    + ) : ( + pinChannel(`${chatItem.id}`, !chatItem.pin)} + onClickMute={(chatItem) => muteChannel(`${chatItem.id}`, !chatItem.muted)} + onClickDelete={(chatItem) => hideChannel(`${chatItem.id}`)} + onClick={(chatItem) => navigate(`/chat-box/${chatItem.id}`, { state: chatItem })} + hasMore={hasNextChannelList} + loadMore={nextChannelList} + /> + )} +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/ChatList/mock.ts b/packages/web-extension-did/app/web/pages/ChatList/mock.ts new file mode 100644 index 0000000000..18274b180d --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatList/mock.ts @@ -0,0 +1,158 @@ +export const mockChatList: any[] = [ + { + id: 'id1', + avatar: '', + letterItem: 'E', + alt: 'p', + title: 'EmreEmreEmreEmreEmreEmreEmreEmreEmre', + subtitle: 'What are you doing ?', + date: new Date(), + showMute: true, + muted: true, + pin: true, + unread: 0, + // customStatusComponents: [() => UnreadTip({ unread: 10 })], + }, + { + id: 'id2', + avatar: '', + letterItem: 'B', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + date: new Date(), + showMute: true, + muted: true, + unread: 1, + // customStatusComponents: [() => UnreadTip({ unread: 10 })], + }, + { + id: 'id3', + avatar: '', + letterItem: 'C', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + date: new Date(), + muted: true, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '99+', + unread: 99, + }, + { + id: 'id4', + avatar: 'p', + letterItem: 'P', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + date: new Date(), + muted: true, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '9', + unread: 100, + }, + { + id: 'id5', + avatar: 'p', + letterItem: 'E', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + date: new Date(), + muted: false, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '99', + unread: 1, + }, + { + id: 'id6', + avatar: 'p', + letterItem: '2', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + date: new Date(), + muted: false, + showMute: true, + // customStatusComponents: [() => UnreadTip({ unread: 101 })], + unread: 99, + }, + { + id: 'id7', + avatar: 'p', + letterItem: 'd', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + // date: new Date(), + date: undefined, + muted: false, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '99+', + unread: 100, + }, + { + id: 'id8', + avatar: 'p', + letterItem: 'd', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + // date: new Date(), + date: undefined, + muted: false, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '99+', + unread: 100, + }, + { + id: 'id9', + avatar: 'p', + letterItem: 'd', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + // date: new Date(), + date: undefined, + muted: false, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '99+', + unread: 100, + }, + { + id: 'id10', + avatar: 'p', + letterItem: 'd', + alt: 'p', + title: 'Emre', + subtitle: 'What are you doing ?', + // date: new Date(), + date: undefined, + muted: false, + showMute: true, + showVideoCall: true, + statusColor: 'red', + statusColorType: 'badge', + statusText: '99+', + unread: 100, + }, +]; diff --git a/packages/web-extension-did/app/web/pages/ChatListSearch/index.less b/packages/web-extension-did/app/web/pages/ChatListSearch/index.less new file mode 100644 index 0000000000..36b98c8f5b --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatListSearch/index.less @@ -0,0 +1,45 @@ +@import '../../assets/theme/color.less'; + +.chat-list-search-page { + width: 360px; + height: 100%; + background-color: @bg-11; + font-size: 16px; + line-height: 22px; + .chat-list-search { + padding-top: 16px; + flex-shrink: 0; + border-bottom: 1px solid @border-2; + background-color: @bg-13; + .close2-icon { + width: 18px; + height: 18px; + } + .setting-header-wrapper { + height: 62px; + border: none; + background-color: @bg-13; + } + .dropdown-search-wrapper { + padding: 0 24px 10px; + } + } + .find-more { + padding: 14px 24px; + gap: 16px; + color: @font-9; + border-bottom: 1px solid @border-2; + .addcontact-icon { + width: 20px; + height: 20px; + } + } + .chat-list-search-content { + .search-empty { + margin-top: 60px; + font-size: 16px; + line-height: 22px; + color: @font-12; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/ChatListSearch/index.tsx b/packages/web-extension-did/app/web/pages/ChatListSearch/index.tsx new file mode 100644 index 0000000000..82f0670c55 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/ChatListSearch/index.tsx @@ -0,0 +1,73 @@ +import { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router'; +import { useTranslation } from 'react-i18next'; +import { useDebounceCallback } from '@portkey-wallet/hooks'; +import SettingHeader from 'pages/components/SettingHeader'; +import CustomSvg from 'components/CustomSvg'; +import { useLoading } from 'store/Provider/hooks'; +import DropdownSearch from 'components/DropdownSearch'; +import './index.less'; + +export default function ChatListSearch() { + const { t } = useTranslation(); + const [filterWord, setFilterWord] = useState(''); + const navigate = useNavigate(); + const { setLoading } = useLoading(); + const [chatList, setChatList] = useState<[]>([]); + + const handleSearch = useCallback(async (keyword: string) => { + if (!keyword) { + setChatList([]); + } else { + setChatList([]); + } + }, []); + + const searchDebounce = useDebounceCallback( + async (params) => { + setLoading(true); + await handleSearch(params); + setLoading(false); + }, + [filterWord], + 500, + ); + + return ( +
    +
    + navigate('/chat-list')} + rightElement={ navigate('/chat-list')} />} + /> + } + value={filterWord} + inputProps={{ + onChange: (e) => { + const _value = e.target.value.replaceAll(' ', ''); + setFilterWord(_value); + searchDebounce(_value); + }, + placeholder: 'Search chats', + }} + /> +
    +
    + + Find More +
    +
    + {chatList.length === 0 ? ( +
    {filterWord ? 'No search result' : ''}
    + ) : ( +
    +
    Chats
    + {/* TODO contact list */} +
    + )} +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less index f3e161bdac..f0e7e4c9ee 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.less @@ -3,6 +3,13 @@ .balance { overflow-y: overlay; + .chat-body { + position: absolute; + right: 20px; + bottom: 20px; + z-index: 9999; + } + .wallet-name { position: relative; line-height: 22px; diff --git a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx index 90c91ea96d..4cfcfa55e4 100644 --- a/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx +++ b/packages/web-extension-did/app/web/pages/Home/components/MyBalance/index.tsx @@ -28,10 +28,18 @@ import useGuardianList from 'hooks/useGuardianList'; import { FAUCET_URL } from '@portkey-wallet/constants/constants-ca/payment'; import { BalanceTab } from '@portkey-wallet/constants/constants-ca/assets'; import PromptEmptyElement from 'pages/components/PromptEmptyElement'; -import { useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; +import { useCurrentNetworkInfo, useIsMainnet } from '@portkey-wallet/hooks/hooks-ca/network'; import AccountConnect from 'pages/components/AccountConnect'; -import './index.less'; import { useBuyButtonShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import ChatEntry from 'pages/ChatEntry'; +import './index.less'; +import im from '@portkey-wallet/im'; +import { getWallet } from '@portkey-wallet/utils/aelf'; +import InternalMessage from 'messages/InternalMessage'; +import InternalMessageTypes from 'messages/InternalMessageTypes'; +import aes from '@portkey-wallet/utils/aes'; +import { useUnreadCount } from '@portkey-wallet/hooks/hooks-ca/im'; +import { request } from '@portkey-wallet/api/api-did'; export interface TransactionResult { total: number; @@ -82,6 +90,42 @@ export default function MyBalance() { useFreshTokenPrice(); useVerifierList(); const { isBuyButtonShow } = useBuyButtonShow(); + // TODO + const isShowChat = true; + // const isShowChat = useIsChatShow(); + const unreadCount = useUnreadCount(); + + // IM START + const { apiUrl, imApiUrl, imWsUrl } = useCurrentNetworkInfo(); + useMemo(() => { + request.set('baseURL', apiUrl); + if (request.defaultConfig.baseURL !== apiUrl) { + request.defaultConfig.baseURL = apiUrl; + } + }, [apiUrl]); + useMemo(() => { + im.setUrl({ + apiUrl: imApiUrl || '', + wsUrl: imWsUrl || '', + }); + }, [imApiUrl, imWsUrl]); + + const initIM = useCallback(async () => { + const getSeedResult = await InternalMessage.payload(InternalMessageTypes.GET_SEED).send(); + const pin = getSeedResult.data.privateKey; + if (!pin) return; + + const privateKey = aes.decrypt(walletInfo.AESEncryptPrivateKey, pin); + const account = getWallet(privateKey || ''); + if (!account || !walletInfo.caHash) return; + + try { + await im.init(account, walletInfo.caHash); + } catch (error) { + console.log('im init error', error); + } + }, [walletInfo.AESEncryptPrivateKey, walletInfo.caHash]); + // IM END useEffect(() => { if (state?.key) { @@ -94,6 +138,10 @@ export default function MyBalance() { appDispatch(getSymbolImagesAsync()); }, [passwordSeed, appDispatch, caAddresses, chainIdArray, caAddressInfos, isMainNet, state?.key]); + useEffect(() => { + isShowChat && initIM(); + }, [initIM, isShowChat]); + useEffect(() => { getGuardianList({ caHash: walletInfo?.caHash }); isMainNet && appDispatch(fetchBuyFiatListAsync()); @@ -170,6 +218,12 @@ export default function MyBalance() { return (
    + {/* TODO isPrompt */} + {isShowChat && ( +
    + +
    + )}
    {!isPrompt && } {walletName} diff --git a/packages/web-extension-did/app/web/pages/NewChat/index.less b/packages/web-extension-did/app/web/pages/NewChat/index.less new file mode 100644 index 0000000000..4597f81236 --- /dev/null +++ b/packages/web-extension-did/app/web/pages/NewChat/index.less @@ -0,0 +1,36 @@ +@import '../../assets/theme/color.less'; + +.new-chat-page { + width: 360px; + height: 100%; + background-color: @bg-11; + font-size: 16px; + line-height: 22px; + + .new-chat-top { + flex-shrink: 0; + padding-top: 16px; + background-color: @bg-13; + border-bottom: 1px solid @border-2; + .close2-icon { + width: 18px; + height: 18px; + } + .setting-header-wrapper { + border: none; + background-color: @bg-13; + height: 62px; + } + .dropdown-search-wrapper { + padding: 0 24px 10px; + } + } + .new-chat-content { + .empty { + margin-top: 60px; + font-size: 16px; + line-height: 22px; + color: @font-12; + } + } +} diff --git a/packages/web-extension-did/app/web/pages/NewChat/index.tsx b/packages/web-extension-did/app/web/pages/NewChat/index.tsx new file mode 100644 index 0000000000..f8b596bc7c --- /dev/null +++ b/packages/web-extension-did/app/web/pages/NewChat/index.tsx @@ -0,0 +1,69 @@ +import { useCallback, useState } from 'react'; +import { useNavigate } from 'react-router'; +import { useTranslation } from 'react-i18next'; +import { useDebounceCallback } from '@portkey-wallet/hooks'; +import SettingHeader from 'pages/components/SettingHeader'; +import CustomSvg from 'components/CustomSvg'; +import { useLoading } from 'store/Provider/hooks'; +import DropdownSearch from 'components/DropdownSearch'; +import './index.less'; + +export default function ChatListSearch() { + const { t } = useTranslation(); + const [filterWord, setFilterWord] = useState(''); + const navigate = useNavigate(); + const { setLoading } = useLoading(); + const [chatList, setChatList] = useState<[]>([]); + + const handleSearch = useCallback(async (keyword: string) => { + if (!keyword) { + setChatList([]); + } else { + setChatList([]); + } + }, []); + + const searchDebounce = useDebounceCallback( + async (params) => { + setLoading(true); + await handleSearch(params); + setLoading(false); + }, + [filterWord], + 500, + ); + + return ( +
    +
    + navigate('/chat-list')} + rightElement={ navigate('/chat-list')} />} + /> + } + value={filterWord} + inputProps={{ + onChange: (e) => { + const _value = e.target.value.replaceAll(' ', ''); + setFilterWord(_value); + searchDebounce(_value); + }, + placeholder: 'Search', + }} + /> +
    +
    + {chatList.length === 0 ? ( +
    {filterWord ? `No search result` : `No contact found`}
    + ) : ( +
    +
    Chats
    + {/* TODO */} +
    + )} +
    +
    + ); +} diff --git a/packages/web-extension-did/app/web/pages/components/MenuList/index.tsx b/packages/web-extension-did/app/web/pages/components/MenuList/index.tsx index 400db1fe30..30672ef79d 100644 --- a/packages/web-extension-did/app/web/pages/components/MenuList/index.tsx +++ b/packages/web-extension-did/app/web/pages/components/MenuList/index.tsx @@ -14,6 +14,7 @@ export interface IMenuItemProps { className?: string; isShowSelectedColor?: boolean; selected?: number | string; + showEnterIcon?: boolean; } export default function MenuList({ @@ -22,11 +23,13 @@ export default function MenuList({ className, isShowSelectedColor = false, selected, + showEnterIcon = true, }: IMenuItemProps) { return (
    {list.map((item) => ( { extraArgument: E; @@ -66,6 +68,11 @@ export const dappPersistConfig = { storage: localStorage, }; +export const discoverPersistConfig = { + key: discoverSlice.name, + storage: localStorage, +}; + export const txFeePersistConfig = { key: txFeeSlice.name, storage: localStorage, @@ -104,6 +111,12 @@ export const cmsPersistConfig = { storage: localStorage, }; +export const imPersistConfig = { + key: imSlice.name, + storage: localStorage, + blacklist: ['channelMessageListNetMap'], +}; + const reduxPersistConfig = { key: reduxStorageRoot, storage: localStorage, @@ -125,7 +138,9 @@ const reduxPersistConfig = { contactSlice.name, guardiansSlice.name, dappSlice.name, + discoverSlice.name, txFeeSlice.name, + imSlice.name, ], // More info here: https://shift.infinite.red/shipping-persistant-reducers-7341691232b1 // transforms: [SetTokenTransform], diff --git a/packages/web-extension-did/app/web/store/Provider/hooks.ts b/packages/web-extension-did/app/web/store/Provider/hooks.ts index 83bf1659f4..8873e44c69 100644 --- a/packages/web-extension-did/app/web/store/Provider/hooks.ts +++ b/packages/web-extension-did/app/web/store/Provider/hooks.ts @@ -22,6 +22,7 @@ export const useMiscState = () => useAppSelector((state) => state.misc); export const useCommonState = () => useAppSelector((state) => state.common); export const usePayment = () => useAppSelector((state) => state.payment); export const useDapp = () => useAppSelector((state) => state.dapp); +export const useDiscover = () => useAppSelector((state) => state.discover); export const useLoading = () => { const _setLoading = useCallback( (isLoading: boolean | OpacityType, loadingInfo?: LoadingInfoType) => setLoading(isLoading, loadingInfo), diff --git a/packages/web-extension-did/app/web/store/Provider/rootReducer.ts b/packages/web-extension-did/app/web/store/Provider/rootReducer.ts index 29e9da2c5e..c43d96e07b 100644 --- a/packages/web-extension-did/app/web/store/Provider/rootReducer.ts +++ b/packages/web-extension-did/app/web/store/Provider/rootReducer.ts @@ -26,13 +26,16 @@ import { cmsPersistConfig, dappPersistConfig, txFeePersistConfig, + imPersistConfig, } from './config'; import { miscSlice } from '@portkey-wallet/store/store-ca/misc/slice'; import { guardiansSlice } from '@portkey-wallet/store/store-ca/guardians/slice'; import { paymentSlice } from '@portkey-wallet/store/store-ca/payment/slice'; import { cmsSlice } from '@portkey-wallet/store/store-ca/cms/slice'; import { dappSlice } from '@portkey-wallet/store/store-ca/dapp/slice'; +import { discoverSlice } from '@portkey-wallet/store/store-ca/discover/slice'; import { txFeeSlice } from '@portkey-wallet/store/store-ca/txFee/slice'; +import { imSlice } from '@portkey-wallet/store/store-ca/im/slice'; export const tokenReducer = persistReducer(tokenPersistConfig, tokenSlice.reducer); export const assetReducer = persistReducer(assetPersistConfig, assetsSlice.reducer); @@ -46,7 +49,9 @@ export const miscReducer = persistReducer(miscPersistConfig, miscSlice.reducer); export const paymentReducer = persistReducer(paymentPersistConfig, paymentSlice.reducer); export const cmsReducer = persistReducer(cmsPersistConfig, cmsSlice.reducer); export const dappReducer = persistReducer(dappPersistConfig, dappSlice.reducer); +export const discoverReducer = persistReducer(dappPersistConfig, discoverSlice.reducer); export const txFeeReducer = persistReducer(txFeePersistConfig, txFeeSlice.reducer); +export const imReducer = persistReducer(imPersistConfig, imSlice.reducer); const rootReducer = customCombineReducers({ [walletSlice.name]: walletReducer, @@ -66,7 +71,9 @@ const rootReducer = customCombineReducers({ [paymentSlice.name]: paymentReducer, [cmsSlice.name]: cmsReducer, [dappSlice.name]: dappReducer, + [discoverSlice.name]: discoverReducer, [txFeeSlice.name]: txFeeReducer, + [imSlice.name]: imReducer, }); export default rootReducer; diff --git a/packages/web-extension-did/package.json b/packages/web-extension-did/package.json index a3857f3eed..1128066612 100644 --- a/packages/web-extension-did/package.json +++ b/packages/web-extension-did/package.json @@ -16,6 +16,7 @@ "@portkey-wallet/store": "^1.3.0", "@portkey-wallet/types": "^1.3.0", "@portkey-wallet/utils": "^1.3.0", + "@portkey-wallet/im-ui-web": "^1.3.0", "@portkey/extension-provider": "1.0.1-alpha.0", "@portkey/provider-utils": "1.0.1-alpha.0", "@portkey/provider-types": "1.0.1-alpha.0", diff --git a/yarn.lock b/yarn.lock index 62e04f3239..cfc2af0795 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6175,10 +6175,10 @@ redux-thunk "^2.4.1" reselect "^4.1.5" -"@relationlabs/im@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@relationlabs/im/-/im-0.3.3.tgz#b22a66ac064f72cefc579e8b23222d16c1bc02d5" - integrity sha512-O3cK1qrupf7VE7w/dPOv+vdgTwUBEsGRvWt+vCHZBTUwN+io5hFS07LnZ/WWDXyjxXVlIGU2vMXchXOCUAD3og== +"@relationlabs/im@^0.4.0-beta-3": + version "0.4.0-beta-3" + resolved "https://registry.yarnpkg.com/@relationlabs/im/-/im-0.4.0-beta-3.tgz#3f84d2ef8a2408c248a27fe761646b9c24a8919c" + integrity sha512-cRk8KKMwbnFcskesjHgCRmWvEZKIRQs55dlB5iDLtciJzFvo1Fs3GqDA9ZcOkiyq5Huexa+zgiySqIRJ2nCMFA== dependencies: axios "^0.27.2" @@ -9250,6 +9250,22 @@ available-typed-arrays@^1.0.5: resolved "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +aws-sdk@^2.1438.0: + version "2.1438.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1438.0.tgz#6bc9b3ef716b47ed78d94da5234692ec2dc73ff5" + integrity sha512-xessrWtoMD2icLPnm4KlTl3tiudJlYh3RtqBx+BggBc3ie9l98za1tSB4NunabTEbBUj4YMmzN1lpMsNEZ0NGw== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + util "^0.12.4" + uuid "8.0.0" + xml2js "0.5.0" + aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.npmmirror.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" @@ -10853,15 +10869,7 @@ buffer-xor@^1.0.3: resolved "https://registry.npmmirror.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" - integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@^4.9.1: +buffer@4.9.2, buffer@^4.9.1: version "4.9.2" resolved "https://registry.npmmirror.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== @@ -10870,6 +10878,14 @@ buffer@^4.9.1: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" + integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buffer@^5.0.5, buffer@^5.2.1, buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -14744,6 +14760,11 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== + events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -17157,6 +17178,11 @@ idna-uts46-hx@^2.3.1: dependencies: punycode "2.1.0" +ieee754@1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -18738,6 +18764,11 @@ jju@~1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== + joi@^17.2.1: version "17.6.2" resolved "https://registry.npmmirror.com/joi/-/joi-17.6.2.tgz#00ac55ce6495596545cce45309f38738cfbd7cd3" @@ -19559,6 +19590,11 @@ loader-utils@^3.2.0: resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== +loaders.css@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/loaders.css/-/loaders.css-0.1.2.tgz#3a9fb43726c73334a38142af9d0629019b658743" + integrity sha512-Rhowlq24ey1VOeor+3wYOt9+MjaxBOJm1u4KlQgNC3+0xJ0LS4wq4iG57D/BPzvuD/7HHDGQOWJ+81oR2EI9bQ== + localforage@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" @@ -22789,6 +22825,13 @@ progress@2.0.3, progress@^2.0.0, progress@^2.0.3: resolved "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +progressbar.js@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/progressbar.js/-/progressbar.js-1.1.0.tgz#300f99be40ba93fb0867f91dff01771236cade70" + integrity sha512-K68/xcyXKo2I6T3PfIkXrRaycxROmWeU4bugb49iulWR25cU94PM0cfZ47S0jDhG5K3vPhZwCOy1fgb5Pgh6UQ== + dependencies: + shifty "^2.1.2" + promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.npmmirror.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" @@ -22990,6 +23033,11 @@ pumpify@^1.3.5: inherits "^2.0.3" pump "^2.0.0" +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + punycode@2.1.0: version "2.1.0" resolved "https://registry.npmmirror.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" @@ -23132,6 +23180,11 @@ query-string@^7.0.0, query-string@^7.1.1: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -23823,6 +23876,11 @@ react-i18next@^11.16.9, react-i18next@^11.18.0, react-i18next@^11.18.6: "@babel/runtime" "^7.14.5" html-parse-stringify "^3.0.1" +react-icons@^4.3.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.10.1.tgz#3f3b5eec1f63c1796f6a26174a1091ca6437a500" + integrity sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw== + react-is@^16.12.0, react-is@^16.13.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -24285,6 +24343,16 @@ react-shallow-renderer@16.15.0, react-shallow-renderer@^16.13.1: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" +react-spinkit@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-spinkit/-/react-spinkit-3.0.0.tgz#31fdaf4e18177766c57d1b1f3330290f8492a85a" + integrity sha512-RrfGRPjqxHQiy7quPqhjPynTu0zobgQaZu1QYBMpJJ6pCSRRRK16EZMaxdE6fLVYFRJWpX/eGATWLMoVFFT5uQ== + dependencies: + classnames "^2.2.3" + loaders.css "^0.1.2" + object-assign "^4.1.0" + prop-types "^15.5.8" + react-test-renderer@18.0.0: version "18.0.0" resolved "https://registry.npmmirror.com/react-test-renderer/-/react-test-renderer-18.0.0.tgz#fa403d625ea9478a70ace43db88833f6c3a5bb4c" @@ -25332,6 +25400,11 @@ sass-loader@^13.0.2: klona "^2.0.4" neo-async "^2.6.2" +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== + sax@>=0.6.0, sax@^1.2.4: version "1.2.4" resolved "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -25701,6 +25774,13 @@ shell-quote@^1.6.1, shell-quote@^1.7.3: resolved "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== +shifty@^2.1.2: + version "2.20.4" + resolved "https://registry.yarnpkg.com/shifty/-/shifty-2.20.4.tgz#fb2ec81697b808b250024fa9548b5b93fadd78cf" + integrity sha512-4Y0qRkg8ME5XN8yGNAwmFOmsIURGFKT9UQfNL6DDJQErYtN5HsjyoBuJn41ZQfTkuu2rIbRMn9qazjKsDpO2TA== + optionalDependencies: + fsevents "^2.3.2" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -27183,6 +27263,11 @@ thunky@^1.0.2: resolved "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +timeago.js@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-4.0.2.tgz#724e8c8833e3490676c7bb0a75f5daf20e558028" + integrity sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w== + timed-out@^4.0.1: version "4.0.1" resolved "https://registry.npmmirror.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -27911,6 +27996,14 @@ url-set-query@^1.0.0: resolved "https://registry.npmmirror.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg== +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + urllib@^2.25.1: version "2.39.1" resolved "https://registry.npmmirror.com/urllib/-/urllib-2.39.1.tgz#bfb4a6b2b67b07780baec7ca3130a269355d804c" @@ -28047,6 +28140,11 @@ uuid@7.0.2: resolved "https://registry.npmmirror.com/uuid/-/uuid-7.0.2.tgz#7ff5c203467e91f5e0d85cfcbaaf7d2ebbca9be6" integrity sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw== +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== + uuid@^2.0.2: version "2.0.3" resolved "https://registry.npmmirror.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" @@ -29371,6 +29469,14 @@ xml2js@0.4.23: sax ">=0.6.0" xmlbuilder "~11.0.0" +xml2js@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + xmlbuilder@^14.0.0: version "14.0.0" resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-14.0.0.tgz#876b5aec4f05ffd5feb97b0a871c855d16fbeb8c" From 7d52bb94d7d8562c3615582f1a7302f360c05232 Mon Sep 17 00:00:00 2001 From: ykx Date: Sat, 19 Aug 2023 10:26:21 +0800 Subject: [PATCH 527/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20contact=20?= =?UTF-8?q?name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/pages/Contacts/EditContact/index.tsx | 1 + .../components/EditContactForm/index.less | 2 +- .../components/EditContactForm/index.tsx | 10 +++++++-- .../components/IdAndAddress/index.less | 6 +++++- .../components/IdAndAddress/index.tsx | 21 +++++++++++-------- .../components/ViewContactBody/index.tsx | 2 +- .../app/web/pages/Wallet/WalletName/index.tsx | 2 +- .../app/web/types/Profile.ts | 2 +- 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx index d2cb3014db..4f41f17350 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx @@ -15,6 +15,7 @@ import { useGoProfile, useProfileCopy } from 'hooks/useProfile'; import { IEditContactFormProps } from '../components/EditContactForm'; import { ContactInfoError, ValidData } from '../AddContact'; import CustomModal from 'pages/components/CustomModal'; +import { useEffectOnce } from 'react-use'; export type IEditContactProps = IEditContactFormProps & BaseHeaderProps; diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less index 94326782d4..8199cfddde 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.less @@ -2,7 +2,7 @@ .edit-contact-form { .form-content { - padding: 12px 24px 8px; + padding: 12px 24px 0; .@{app-prefix}-form-item .@{app-prefix}-form-item-label { padding-bottom: 8px; diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx index e260c8ccb8..590e924fa3 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next'; import IdAndAddress from '../IdAndAddress'; import './index.less'; import { ValidData } from 'pages/Contacts/AddContact'; +import { useEffectOnce } from 'react-use'; const { Item: FormItem } = Form; @@ -77,7 +78,7 @@ export default function EditContactForm({ validateStatus={validRemark?.validateStatus} help={validRemark?.errorMsg}> handleInputRemarkChange(e.target.value)} maxLength={16} /> @@ -85,7 +86,12 @@ export default function EditContactForm({ )}
    - +
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less index 094d10dadf..896ee3166e 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.less @@ -2,10 +2,11 @@ @import '../../../../assets/theme/constants.less'; .id-and-address { + padding: 0 24px; .info-section { display: flex; flex-direction: column; - padding: 24px; + padding-bottom: 24px; color: @font-11; width: 100%; @@ -15,6 +16,9 @@ color: #262626; margin-bottom: 16px; } + .title-did { + padding-top: 24px; + } .info-content { .info-desc { font-size: 14px; diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx index 7d3313b4df..b55ec10c43 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/IdAndAddress/index.tsx @@ -2,15 +2,16 @@ import './index.less'; import ContactAddressList from 'pages/Contacts/components/ContactAddressList'; import CustomSvg from 'components/CustomSvg'; import { AddressItem } from '@portkey-wallet/types/types-ca/contact'; +import clsx from 'clsx'; interface IIdAndAddressProps { portkeyId: string; - relationOneId: string; + relationId: string; addresses: AddressItem[]; handleCopy: (val: string) => void; } -export default function IdAndAddress({ portkeyId, relationOneId, addresses, handleCopy }: IIdAndAddressProps) { +export default function IdAndAddress({ portkeyId, relationId, addresses, handleCopy }: IIdAndAddressProps) { return (
    {/* Section - ID */} @@ -24,21 +25,23 @@ export default function IdAndAddress({ portkeyId, relationOneId, addresses, hand
    )} - {!portkeyId && relationOneId && ( + {!portkeyId && relationId && (
    {`ID (relation one)`}
    -
    {relationOneId}
    - handleCopy(relationOneId)} type="Copy" className="id-copy-icon" /> +
    {relationId}
    + handleCopy(relationId)} type="Copy" className="id-copy-icon" />
    )} {/* Section - Address */} -
    -
    {`DID`}
    - -
    + {addresses?.length > 0 && ( +
    +
    {`DID`}
    + +
    + )}
    ); } diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx index 3fed588c7f..b6998e7cdf 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx @@ -66,7 +66,7 @@ export default function ViewContactBody({ diff --git a/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx b/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx index 65b5f426c1..87a68aa88a 100644 --- a/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx +++ b/packages/web-extension-did/app/web/pages/Wallet/WalletName/index.tsx @@ -23,7 +23,7 @@ export default function WalletName() { name: 'by', addresses: [{ chainId: 'AELF', address: 'H8CXvfy8hm', chainName: 'aelf' }], portkeyId: '111111', - relationOneId: '111', + relationId: '111', isNameDisable: false, isShowRemark: false, }; diff --git a/packages/web-extension-did/app/web/types/Profile.ts b/packages/web-extension-did/app/web/types/Profile.ts index 54c8d3b04a..25a2e8e809 100644 --- a/packages/web-extension-did/app/web/types/Profile.ts +++ b/packages/web-extension-did/app/web/types/Profile.ts @@ -7,7 +7,7 @@ export interface IProfileDetailDataProps { name: string; remark?: string; portkeyId: string; - relationOneId: string; + relationId: string; index: string; addresses: AddressItem[]; isNameDisable?: boolean; From b57f31d59c13a0508fd53ce212c1d73cb6dcdd88 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Sat, 19 Aug 2023 10:37:07 +0800 Subject: [PATCH 528/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20security=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/SecurityLock/index.tsx | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/mobile-app-did/js/pages/SecurityLock/index.tsx b/packages/mobile-app-did/js/pages/SecurityLock/index.tsx index 6098f216a2..e123d960da 100644 --- a/packages/mobile-app-did/js/pages/SecurityLock/index.tsx +++ b/packages/mobile-app-did/js/pages/SecurityLock/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { AppState, AppStateStatus, StyleSheet } from 'react-native'; +import { AppState, AppStateStatus } from 'react-native'; import { useAppDispatch } from 'store/hooks'; import { setCredentials } from 'store/user/actions'; import { useUser } from 'hooks/store'; @@ -24,25 +24,24 @@ import { getSecureStoreItem } from '@portkey-wallet/utils/mobile/biometric'; import { useThrottleCallback } from '@portkey-wallet/hooks'; import ActionSheet from 'components/ActionSheet'; import { isUserBiometricsError } from 'utils/biometrics'; -let appState: AppStateStatus, verifyTime: number; +import GStyles from 'assets/theme/GStyles'; export default function SecurityLock() { const { biometrics } = useUser(); const biometricsReady = useBiometricsReady(); const [caInfo, setStateCAInfo] = useState(); + const appStateRef = useRef(); + const verifyTimeRef = useRef(); usePreventHardwareBack(); const timer = useRef(); const onResultFail = useOnResultFail(); - const digitInput = useRef(); const [errorMessage, setErrorMessage] = useState(); - const { managerInfo, address, caHash } = useCurrentWalletInfo(); const dispatch = useAppDispatch(); const isSyncCAInfo = useMemo(() => address && managerInfo && !caHash, [address, caHash, managerInfo]); const navigation = useNavigation(); const onIntervalGetResult = useIntervalGetResult(); const originChainId = useOriginChainId(); - useEffect(() => { if (isSyncCAInfo) { setTimeout(() => { @@ -75,7 +74,6 @@ export default function SecurityLock() { }, [biometrics, biometricsReady, managerInfo, navigation], ); - const handlePassword = useCallback( (pwd: string) => { dispatch(setCredentials({ pin: pwd })); @@ -126,10 +124,9 @@ export default function SecurityLock() { originChainId, ], ); - const verifyBiometrics = useThrottleCallback( async () => { - if (!biometrics || (verifyTime && verifyTime + 1000 > Date.now())) return; + if (!biometrics || (verifyTimeRef.current && verifyTimeRef.current + 1000 > Date.now())) return; try { const securePassword = await getSecureStoreItem('Pin'); if (!securePassword) throw new Error('No password'); @@ -143,16 +140,16 @@ export default function SecurityLock() { }); } } - verifyTime = Date.now(); + verifyTimeRef.current = Date.now(); }, [biometrics, handlePassword], 1000, ); const handleAppStateChange = useCallback( (nextAppState: AppStateStatus) => { - if (nextAppState === 'active' && appState !== 'active') { + if (nextAppState === 'active' && appStateRef.current !== 'active') { verifyBiometrics(); - appState = nextAppState; + appStateRef.current = nextAppState; } }, [verifyBiometrics], @@ -165,7 +162,6 @@ export default function SecurityLock() { return () => { timer.current?.remove(); listener.remove(); - appState = 'background'; }; }, [handleAppStateChange]); const onChangeText = useCallback( @@ -184,7 +180,7 @@ export default function SecurityLock() { [errorMessage, handlePassword], ); return ( - + ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); From 5de36b4760d6f2f18816d9c7f4e8eb407f752299 Mon Sep 17 00:00:00 2001 From: portkey-yellow Date: Sat, 19 Aug 2023 10:51:09 +0800 Subject: [PATCH 529/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20tool=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputToolbar/AccessoryBar/index.tsx | 1 + .../components/InputToolbar/ToolBar/index.tsx | 22 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx index db0e90e521..27b260f9d1 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/AccessoryBar/index.tsx @@ -48,6 +48,7 @@ const styles = StyleSheet.create({ hide: { width: 0, height: 0, + overflow: 'hidden', }, absolute: { top: 0, diff --git a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx index 6c36821673..6192e5d4ab 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/InputToolbar/ToolBar/index.tsx @@ -97,26 +97,4 @@ const styles = StyleSheet.create({ height: pTd(52), borderRadius: pTd(6), }, - hideInput: { - position: 'absolute', - top: -1000, - opacity: 0, - ...GStyles.flex1, - }, - inputContainerStyle: { - height: 80, - ...GStyles.flex1, - }, - hide: { - width: 0, - height: 0, - }, - absolute: { - top: 0, - bottom: 0, - left: 0, - right: 0, - position: 'absolute', - zIndex: 999, - }, }); From 554879a97c2c33210ba1c10e514ec4f8b4a54c4c Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Sat, 19 Aug 2023 10:55:08 +0800 Subject: [PATCH 530/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20isImputation=20u?= =?UTF-8?q?i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mobile-app-did/js/assets/theme/GStyles.ts | 3 ++ .../mobile-app-did/js/assets/theme/index.ts | 1 + .../js/components/ContactItem/index.tsx | 42 ++++++++++++++-- .../pages/My/Contacts/ContactsHome/index.tsx | 2 + .../Contacts/NoChatContactProfile/index.tsx | 14 ++++++ .../components/ContactUpdateWarning/index.tsx | 42 ++++++++++++++++ .../js/pages/My/components/DIDItem/index.tsx | 50 ------------------- .../js/pages/My/components/MenuItem/index.tsx | 13 ++++- packages/mobile-app-did/js/pages/My/index.tsx | 1 + 9 files changed, 112 insertions(+), 56 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/My/components/ContactUpdateWarning/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/My/components/DIDItem/index.tsx diff --git a/packages/mobile-app-did/js/assets/theme/GStyles.ts b/packages/mobile-app-did/js/assets/theme/GStyles.ts index 747cb007d3..7dc13338f8 100644 --- a/packages/mobile-app-did/js/assets/theme/GStyles.ts +++ b/packages/mobile-app-did/js/assets/theme/GStyles.ts @@ -23,6 +23,9 @@ export default { flexDirection: 'column', flexWrap: 'wrap', }, + itemStart: { + alignItems: 'flex-start', + }, itemCenter: { alignItems: 'center', }, diff --git a/packages/mobile-app-did/js/assets/theme/index.ts b/packages/mobile-app-did/js/assets/theme/index.ts index b50ddd2e2b..94b08cf4f4 100644 --- a/packages/mobile-app-did/js/assets/theme/index.ts +++ b/packages/mobile-app-did/js/assets/theme/index.ts @@ -27,6 +27,7 @@ export const defaultColors = { bg18: '#F0F1F4', bg19: '#000000', bg20: '#515A62', + bg21: '#FDEDEC', font1: '#464B53', font2: 'white', diff --git a/packages/mobile-app-did/js/components/ContactItem/index.tsx b/packages/mobile-app-did/js/components/ContactItem/index.tsx index 0bca4a54ba..16dd6421a7 100644 --- a/packages/mobile-app-did/js/components/ContactItem/index.tsx +++ b/packages/mobile-app-did/js/components/ContactItem/index.tsx @@ -2,7 +2,8 @@ import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { BGStyles, BorderStyles, FontStyles } from 'assets/theme/styles'; -import { TextM, TextS, TextXXL } from 'components/CommonText'; +import { TextL, TextS, TextXXL } from 'components/CommonText'; +import Svg from 'components/Svg'; import Touchable from 'components/Touchable'; import React, { memo } from 'react'; import { View, TouchableOpacity, StyleSheet } from 'react-native'; @@ -10,25 +11,34 @@ import { View, TouchableOpacity, StyleSheet } from 'react-native'; import { pTd } from 'utils/unit'; export interface ItemType { + isShowWarning?: boolean; isShowChat?: boolean; + isShowContactIcon?: boolean; contact: ContactItemType; onPress?: (item: any) => void; onPressChat?: (item: any) => void; } const ContactItem: React.FC = props => { - const { isShowChat = false, contact, onPress, onPressChat } = props; + const { isShowChat = false, isShowWarning = false, isShowContactIcon = false, contact, onPress, onPressChat } = props; return ( onPress?.(contact)}> - + + {!isShowWarning && } {contact.name[0].toUpperCase()} - + {contact.name} - + + {isShowContactIcon && ( + + + Contact + + )} {isShowChat && ( onPressChat?.(contact)}> @@ -74,4 +84,26 @@ export const styles = StyleSheet.create({ paddingHorizontal: pTd(12), paddingVertical: pTd(4), }, + avatarWrap: { + position: 'relative', + }, + warningCycle: { + position: 'absolute', + zIndex: 1000, + right: 0, + top: 0, + width: pTd(8), + height: pTd(8), + borderRadius: pTd(4), + backgroundColor: defaultColors.bg17, + borderWidth: pTd(1), + borderColor: defaultColors.bg1, + }, + contactIconWrap: { + width: pTd(76), + paddingHorizontal: pTd(8), + paddingVertical: pTd(2), + borderRadius: pTd(4), + backgroundColor: defaultColors.bg9, + }, }); diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx index 79e5ce64d5..421d86491a 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx @@ -16,6 +16,7 @@ import SearchContactListSection from '../SearchContactListSection'; import { StyleSheet } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import FindMoreButton from 'pages/Chat/components/FindMoreButton'; +import ContactUpdateWarning from 'pages/My/components/ContactUpdateWarning'; const ContactsHome: React.FC = () => { const { t } = useLanguage(); @@ -67,6 +68,7 @@ const ContactsHome: React.FC = () => { onChangeText={value => setKeyword(value.trim())} /> + {debounceKeyword ? : } ); diff --git a/packages/mobile-app-did/js/pages/My/Contacts/NoChatContactProfile/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/NoChatContactProfile/index.tsx index f6e8efc443..124624c4f1 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/NoChatContactProfile/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/NoChatContactProfile/index.tsx @@ -10,6 +10,8 @@ import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; import { defaultColors } from 'assets/theme'; import ProfileHeaderSection from 'pages/My/components/ProfileHeaderSection'; import ProfileAddressSection from 'pages/My/components/ProfileAddressSection'; +import useEffectOnce from 'hooks/useEffectOnce'; +import ActionSheet from 'components/ActionSheet'; type RouterParams = { contact?: ContactItemType; @@ -19,6 +21,18 @@ const NoChatContactProfile: React.FC = () => { const { contact } = useRouterParams(); const { t } = useLanguage(); + useEffectOnce(() => { + ActionSheet.alert({ + message: + 'Portkey has grouped contacts with the same Portkey ID together and removed duplicate contacts with the same address.', + buttons: [ + { + title: 'OK', + }, + ], + }); + }); + return ( { + const [show, setShow] = useState(true); + + // TODO: should fetch api + if (!show) return null; + + return ( + + + + Portkeys will automatic group contacts with matching Portkey IDs and seamlessly update in real-time.{' '} + + setShow(false)}> + + + + ); +}; + +export default memo(ContactUpdateWarning); + +const styles = StyleSheet.create({ + wrap: { + width: '100%', + backgroundColor: defaultColors.bg21, + paddingHorizontal: pTd(16), + paddingVertical: pTd(12), + }, + tips: { + marginLeft: pTd(8), + marginRight: pTd(16), + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/components/DIDItem/index.tsx b/packages/mobile-app-did/js/pages/My/components/DIDItem/index.tsx deleted file mode 100644 index 6a4604c28d..0000000000 --- a/packages/mobile-app-did/js/pages/My/components/DIDItem/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import GStyles from 'assets/theme/GStyles'; -import { BGStyles } from 'assets/theme/styles'; -import { TextM } from 'components/CommonText'; -import FormItem from 'components/FormItem'; -import Svg from 'components/Svg'; -import Touchable from 'components/Touchable'; - -import React, { memo, useCallback } from 'react'; -import { StyleSheet, View } from 'react-native'; -import { copyText } from 'utils'; -import { pTd } from 'utils/unit'; - -type PortkeyIDItemPropsType = { - disable?: boolean; - portkeyID: string; -}; - -const PortkeyIDItem: React.FC = props => { - const { disable, portkeyID = 'portkeyID' } = props; - - const copyId = useCallback(() => copyText(portkeyID), [portkeyID]); - - return ( - - - {portkeyID} - - - - - - ); -}; - -export default memo(PortkeyIDItem); - -const styles = StyleSheet.create({ - content: { - height: pTd(56), - borderRadius: pTd(6), - paddingHorizontal: pTd(16), - }, -}); diff --git a/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx b/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx index de39fb1cf7..dfd213dae3 100644 --- a/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx +++ b/packages/mobile-app-did/js/pages/My/components/MenuItem/index.tsx @@ -83,5 +83,16 @@ const styles = StyleSheet.create({ svgWrap: { position: 'relative', }, - warningCycle: {}, + warningCycle: { + position: 'absolute', + zIndex: 1000, + right: pTd(13), + top: -pTd(3), + width: pTd(8), + height: pTd(8), + borderRadius: pTd(4), + backgroundColor: defaultColors.bg17, + borderWidth: pTd(1), + borderColor: defaultColors.bg1, + }, }); diff --git a/packages/mobile-app-did/js/pages/My/index.tsx b/packages/mobile-app-did/js/pages/My/index.tsx index 599611fe0c..abfefd51ff 100644 --- a/packages/mobile-app-did/js/pages/My/index.tsx +++ b/packages/mobile-app-did/js/pages/My/index.tsx @@ -60,6 +60,7 @@ export default function MyMenu() { {MenuList.map(ele => { return ( Date: Sat, 19 Aug 2023 10:55:44 +0800 Subject: [PATCH 531/893] =?UTF-8?q?fix:=20=F0=9F=90=9B=20test=20chat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example-gifted-chat.png | Bin 994954 -> 0 bytes .../TestChat/example-gifted-chat/src/Chats.js | 88 ------ .../example-gifted-chat/src/InputToolbar.tsx | 111 ------- .../src/MessageContainer.tsx | 125 -------- .../example-gifted-chat/src/Photo.tsx | 193 ------------ .../example-gifted-chat/src/messages.js | 109 ------- .../TestChat/example-slack-message/README.md | 9 - .../example-default-style.png | Bin 241375 -> 0 bytes .../example-slack-style.png | Bin 254804 -> 0 bytes .../TestChat/example-slack-message/src/App.js | 68 ----- .../example-slack-message/src/SlackBubble.js | 274 ------------------ .../example-slack-message/src/SlackMessage.js | 117 -------- .../js/pages/Test/TestChat/index.tsx | 6 - .../js/pages/Test/TestChat/route.tsx | 15 - 14 files changed, 1115 deletions(-) delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/example-gifted-chat.png delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/src/Chats.js delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/src/InputToolbar.tsx delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/src/MessageContainer.tsx delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/src/Photo.tsx delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/src/messages.js delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-slack-message/README.md delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-slack-message/example-default-style.png delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-slack-message/example-slack-style.png delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-slack-message/src/App.js delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-slack-message/src/SlackBubble.js delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/example-slack-message/src/SlackMessage.js delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/index.tsx delete mode 100644 packages/mobile-app-did/js/pages/Test/TestChat/route.tsx diff --git a/packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/example-gifted-chat.png b/packages/mobile-app-did/js/pages/Test/TestChat/example-gifted-chat/example-gifted-chat.png deleted file mode 100644 index c4b94670c9ddb52fbe0da46a11d2d26270161ce3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 994954 zcmeFYc|26_`#-J}6 zbBu+B<+!nto;eH4p;Q)@BPmDNfFAqu<~kM@&in4Vx@N|@x?*O2J`dbIU0GO+;$K>^ zTU+$-=i8baKa4zLbhIU2;_{OhJa-QrD1G|w+1XSxryEBu1O?n`t4)-?6JV(~^z#&t zkVKF`=ac})h2BQ%s)CL4XR9h&H(E9${0W2E%1oK;hF~_9JeAfP8Hq~=Q>DITVxrkU zb`=%X7$Fa^v9T)MV^wXvB0k&EWpF^6PKVA<20nR5l~uS<0m3lL7CH2f9bhp!(;;{@ zT8tZSz(SOWyphk%qIhWOgA}3fwl!yUq%hkNQoyMJ^Vzo-atpd_X`UzWpLxbuuN0GZ zH{@78@e|QZjIN1ad7GK#msi1$q8jGYwfw7@V@dEPGNf%kF8DT0+NuDsuP?jgk|LBAQ!sE@`! zbBQZuxi@mq*!)t*1doukA71jON|t3ome8b+q*YfbzCJQb{&@_0*>3fx8==;@T@D!~ zi-Q8P{MSQ<&e-LHP2$YP*s(IxJDomPW6s6GF`u6=0eMhc=&?eESCqr?j)`F?6eN2qz3+c2+LZmVVfI|r?;BW&L8BqPxx29tFklVGiM5t zAA3A4GGf@v6DkB>j*4~@894?4MjYRp6U?uw(sI{s#dC2EVpqd$sS9`Kd#$GUL2%< z$O#u^VSa3BX^CZw>OVMGwSMUL0&e_QdvFuiR^WrCD>7iS<;OpxSa56_j|~M`{qG$Y zzGvY5g@Y0)#&gWz>FHD!^DiORZi;q;eisFAu=`(8lsXd0W?TD09_Dg*qCn)@5<7DB>f#YrMFTk=+YUir-Ekw96A60azDCEMr?`ll z>&4$NT#$I%?dKw2BUpMBuFoZbN&9Fd>&FrLBtWb(%~U$(hZ*Y#XRU5*f#-`TgEvPP z)h==5J>!3~`MvX@`rY`os~$tMi=kU*CSqEzG$}rDzkj3ugn!%kX@SHx^XeNnQoeOu zt5PaIQPy^58X9rfqg|sKsmALzE%*Cy#mo^cUV-U!Vm3trCf~|h*_lIhENk4p00gyaYZBFX|*& z{gM(lep4bX$#~!_!kjbSv{&j>`eqQ|jgWd$?zN2PiQTSqd2@wx zeRCyqq6rF}8Tjk*1xI8oWUfow${c@@_#)^UwmW-F|mxScy zO-GDU3&GrE~_qY)Tl{6T(@L+bx~*Qb{Hf= zu_Xci6pmu(

    P%&MA=}$$x8KU|$wtLwn1mkGT_DsO(T;R&v7)J)oawVxLfvr(l&? zscBJvb?5ck@kwv(n5oAtr(3jI^usjC154V&Qh%_2VwX6`gg-t*&;n*$q++?CUCa~3 z%<7lE&xgFPT&hx?7IYDMab`v3q2i*_i0anG8%nuO*7rj?s*>XsSF^MpDEJ@s2W@I> z4vT%%$<=wI!@>TOvr%iYW$0d^m}J_m_#x3k4+B(^O;g*VUV zcwSU@|Dis9uL^gsZTfi|jSZ)we>3mK?2VFaZ^f2-6J8Iz`Mu98+d2n*Eg#Nx7j?<^ zYW=bIE;ROVyTq5PPSro~S)KkF;)(6Ot}j2nva!!7&fv|s*7q$#K12J?_MP}U`gg+1 z*K904{2I+3mHm)Y-sCCgY4GbteQ8~T*Y#>wY=V~+Hoq>y$IU0xQx#V+u$JADrajpD zy>)_7!8$4a{r+>^x9ygOZzQpb7~huJb5bKc!^SJOX99)<1GoJ$)GC9$MXsL4E9+?# z`E?%`c*TKv`{nJ#e4E+A_jd*Qy2~-G!Vi??0>^9am&8T{k^{a4PA-MHeP$5T+7n|` zIT5Q1bLStIJ#Jbu3Fh`k%{#0!dfR{L&Q-U~7E87JzaYQ{&kuqfrX2h#&s1{x2(1UK z2TrMuv#nTfOKb~*o|;w-%AwjJpokXOX2i}eW1fJ|oUI>T|8<1M`-a!nP-~-pgFy4y z_Tjj9xQN~J*h7v_oOA4V*dUxWTqoGPIKp~;=Un`Qd>z=D*!rTl51|QEKexw1x>c#f z)aR)gX7ukJ-)jqY+RMMze0|^E_*J{TBwE#gM?yj}L;v@5HtA6F1&1-C^)zJz6~iAA zvyx*5kR-o^=Z2S)t{Nr_*E)?%KEytB53uKqo9Qf%YY^}NY07MWe~4ckA39Zt>Nt+f z5e?SJ=Y@*H-S(QEO+Pz#H28$biI?#=jWvwjjV+DW({-O`O4TO|syyh*+LSpj^ZW(% zw)rFTrMi)|>H=r0WG~ar8?q)%kOBDb7rt(ooTNb!gv--`LyTwiKhL)2w(|a!q&oh!dj0c;VnVr#nN^EqHlcPq)f8MFZgGi>YF&fYjXE;>2*;VT=A8Y zvnR>BY(yel=g;dssMl??=J3ccB=YVr4AJ3@ik5bV2v&p79B zT}AGk!b^pgR=LN&Qs%?@I5MGk6(8xZUK{Ow{?fqoqglOLZIXHmH|X zJTfYFo9{Gl-_?KInIBlZ%wG|XcKFQLh9#x;Ky7KqDK6YsMa#2u=wcBV3QS+x!x^S^Q(q}Hi*_t zk6zI?bt9p|5EbEnh#U3jr;Nt$my65jFZV3hm-T&;08uQH>Pj{1XpE-jVD0tmkkg29 zQW~AKRWfI48h##eK0<+sr@WoX+q32kXnYR$z3pZs!9S9zvkh8)DQD8q75p>4C> znw(c@%0t$!<&I=N`?>QKLOS@GE$IEMMm_J7ma1Qe+hmV=zAp^B%5!CMlKqvz$?P@n zM+#?}Nj#9P?N&wBCnGFh1VVj$2B}k1V4`r;T+W!r9N&)!YA}Pe78-a4^tu^s$kRKMM>0x&7|}WAn2_;Qr(8x2*%L zZ{E;w_VH4<@8aX+su1G!c>g{uS|J)h)5|sBzF3Htr?Q)iem7V`?C&K3 z9@-MtH_gO!ef(U-E-PG8xF`WRCMG7P<>&H1!(7kc-{HV7ZHb2g0gpAn;Nak3gfGoQQ;!^zxM`)YVG%Gn7N0zdRptbdjT>7 zo&ix*R8iCVJK+C!>VF>j&rnN$S3g}JFJNQ<KFg_ zGym7Ae}`&;_vik za|GJ#e0K47G*K)pS6GbobZ&4`&^ z#Kqm8=Zb-LYsTDGH`NKjV^~^l#DF&8cQ}qlcUWl}rQXEV!ab(efBQEn$VjI0b{}y$ zq;ya)GUb9M~(8!-_mxdR;hncwJRn^6R= z;qLXqPijrFi5|VbLKsz(*$)n-TskQF5c%oEQTQD_F$XrMsA*k?SKZ-80R^#Aor*p9 zN(Dbx>YJpLFkQw8RJZG%h;qkQ#mko*gq;`FD~!ZlslBAkP4F11Q%mrbX#->1Bi4iM ztsR1@sC>!6&N<$q0-acWRVyjkS!M_I8{x;aDd*E&HeQ343%lhr*VljTD*x4(6@`Ps z=31ZaE=JwIl|eZ8ffEuc>QY3#=xF_Ns{k^;D0I%Nr;uGguVdZW$ulmK)K4K$oaNMF z`*$QCD8`C_)tOus@*O?nX;LO;70|@$^kpCDd10Xj#sXtD>=n&t4V`p&hi8+XP;IR% z0&ZZ6{v6XdUYrRkh%K5g?%>U36#Fw%w^LGbOUpa zkwZXgA?9~KA`Y~LnkTpoWbO3F=kIDE7IudPtWr2Iw~B#H^Tme$0M~%$G4I?QBY+Q1OaRqYp$9uOxaZ^n(NKl84D>do8}ju+&1`JGf)U6?z&e((3AFAo%z zW4gYnEdXL62s<^;QHKZ55I8p5wN3!zy2oz~kllD?XI=V}3Vbb09&rv$&QsbEKWumo zu-*F6lk|k+KrIraAE=XBA_~$x=d*;lH5BXC;KEL>(r1s@EK0SW<3mZyIbBT(Nop!3 z4FFoPFMj0m0S`kv@LanjYT0KEnsV4Y&R2Y%fi?V^#Um-YV{bvQTmu$pfssS2Ts~0t z@1?Z$wQWZyRKQb-FGWTgg1pnKRk&OqbTujT-qyX5VudhbtWhh6=r7^day^Uo>h~)lQu`7EMrUu(4BiuxoVmF>!Z4Zg3tHUOOU#2=un2UYi7Nlf5zuFs3(>SQ63f`Zq)3=k#I;3-S3N^=Fl$ zq0;b6y!P`{EAI<28%r)<6iBpgmp%Wz`91%a*5zP7J1c1d^XJ&4 zbY=zbpdd>7x|mKwmUTEqyqs;kroMyHhuJwMBTGHXH%=S668-3_lIhFq@KHz1{l6-C zoT1~~y!jJxqJl4{o$V=eZ?XE9RZoCz%^YX&#~j5XF!-!ZcoJnT(#6OMi|?Oh?Wic|B;;O~HR7&%`cBLF0x;-b=;d~hj2H~#`tKvtActK^iQuwCLLZ)fz8&G)9d)4)pSY+w2t!A z3hxH2r%0|FAjHcOW2mUvua}j_a5a%9&rAR|+$PuAk7=Ka-q=ecRPNi5@N^pHR|s=c zPG<=+QI}rhc5B<#OAGPk`h>-y@&5E}z5{HKC=sQ#BHxqWUAw5JGH_HKXy@d z8Ya1>8!#K)8xn$sX#rZih{;y)W*p@${+EqQb~a$%a10VX)`mRALmH|&hg`}fY=mO z#R#eXz3SIqZLas%o*hHkA@XDg@_r-u3H(maX<%88eCTP8d{w+~&!!>J6iW4Y=kX{X zN>%sI9dzuU56MjO&w;#MwiyNk?)i4L|>&CkQn$AQwh_N<(vkBFG&hvF?Y# zP-#OLxv(X$-2kGcP|Fj#yn66WKBMFEhnRW8zsoMafZjYefXYSCCwgApxBHLf5coz{ z9R!6kmUd#a5Zi8#c8mx4@|$kbUP=z)+&1Y^t-8EDn*JrMXFJf!Nb$@};)XVNNB*=4Gz4Vhz z`EFu*`}8sh@%?mb&INLnND;9U!|&h;rS5^z+vhZ;uvagfV?1UGp_33PXy8pBrTs5Q z$M~J`k6LQ3gJ_=-4Wk|OOpEOCqTiPIhyj1uFdn6`wcadJPz3X>gK=yUi%4bw<;X>u z<`GRnX(31R-W$ix_kW)zhl_pxX`6gcuc0n@m6w;|tp znuy(&#h!;pmIjKwg*dHnFyCQOlwZuG1JW480Mm}iPx}Gc26WUPuzG-p_>I`y^Ud0{ zf=2NN>#li7%`h&zG7G)xVn;OB-qWPQ7D3yLMEXhCvgJs6vB8t+^B)8bNF9*rIIEPx zb(OuPF~i2?4OgfaDXO(m^u~=^{gg>1n6ItS`PI`9*jCAO)8|uU!Wn+_02UagqSL zv9qURM%9?(pFRL__KL*+V+!%^sH-85I zYNqiRZgcee?se@h%YFGS0A9H=JjvszM|UF)?2+;*nT!dXYWy(4lxTIHAP?3oi;#uy zGKu2(47o(GBWPmU_5^t8LSF#?frGCui5_5ocZTcAY}fS0w-WHH@A_-6&kpK6-!gtL z$90D;KI?0SQ3`!%4};A(YVDb+(zCka!SGu-V{R6(y-ldA1*uK_D5Wd;!E z^EFTt1QE)vELxQ6o0EV9;Wg(Ou?SMPZiZ?n26KML2aAj_k9#7*Yn?66`#6=Clf5al zuSkpQ(cB3wQEuK1vU0l8Xxl^#<2@Ji+Hi@n1U8Ys57YkH`mZK zC}RGfJ?Az^ZHGA!81<{WXfSO1*s?>-rl#Dk>neuLsb6#L=7%PYA*jkm%V7KU3%p`a zj3W*STXULia$Pjvo{&`ZUmmgV@7nQ37_6nIf#GRl<{poE1Tlo@6l!3q? zHkYE{#ET(bR^Q8NCbd-%bR&kT!{9iRCTD^!s@ zG$WDn`~1Nd0Si}@&U5m2A6T3g4{dLqDbL2QKHF&Z$U||=v9a8BpY%vrId5!Gr2?y-KPt1+jHsvFGR?z3XjMy% z?Qn5=We5tFTD|~Vb`mkJkMyCH#7Vh}ecQ)It_|$*TLmXo}Nks{b(BC-Bs(OLR9J4N1nSw9Xb?@-o@@B7ruhRkx{h zK{IR$&r;IlxIZ`(%aN_}&Lv)3!6tJ@yBe;2nv85?c|5HlCaueP@@TfA|MzKKQJ<^w z>6wl-my!LdEyND?b^%ojQgs`YeA4X{B&>{>crA~oYy#Z6+UYFISq8Hw1$>y8Gtf4m&d-vq9Q_w=xA_uNJq^P|M1Rb?O5%jK24_vM(oYj-y z;#896(qqe3R5DFKb_YLMvK28Gh1ee=9X!Tdtf#cL>5Z8ly)SFhGaxl`0Y*=+qfo58 z>0ek-N;t1e&wT8SIx#6qjBYHPUQa1W7*HW4HeEA}M~nanr%(hCb`qijYFH2W86d>n z1QT$#@}T}efJwNxjI7J~jsCpn>AW}NmP4Uz(2 zTC9CX@OMd2I}%QmZIuZkic38SOdyX2*~3)}SN0AgA_ChimyER~9=%Ww|xCYNcMj8`Gen zf}C!7ljO_7?-U=J_LO3eWsT9%BWPr;E(TqE=ay83VSB`0&{$-?Y$^o1C=)8n;lt^u z9r1>cV;7PACQ;Zv#K|_;8T}0-e{4@AroJg*R{c96{+E9^h$ZwRId(#?NwVV&6;^Q-%hj zl{bQtLC%*?p-$JXBH>Q?b?-bh_?e{?T@o@&5k(P1VDj8k&@*H(!4YBf%Y~loVbV_k zybkj3vNSL__tgp52E#PN-~LXJ9n>Eh($TjDNk_H3Zb5h}F;{Wp1jWkV;TL4oV%spI zEu4rs$p`(FQAiO*8-AulMac5Bv;Q4)&aXDVzWk78JJc~<)xvdx{jOye-7Hpu)!4qb zck5K2T=NI$%8ah)ME9~q1U)X6%O(4yxhaf3onyMS$Zh|S6L-_e2!#5<0}12-@+`}E zTOX2FW=J0~%}WT~>CrdBQIj8!!#LT}afZQ)Q&_()G}0&q4dDVJbcUjUAN|(=16xt= zr9Os(NB{{*-|l~mClb!QuTrA|0O_BDOH-L1xvec_x9+H@FiQ>3E~(5jmKj$Yml*K~ zQjfE4=rFB*obuFDGIY5xMTsOMX~7M@Ugu^sx|IL9=775IDaTv)+2uv93$1Etq{D=H z=6)LTv+K(Yu)heqaYE{))(SC_Go1bShB$0FDj9Mu2%yT~Ercp@Gokm5?AJgsU+ zQm8hg41Q=G;#g}|&a}mPJ06QNj0erOnC#X6WOS75jDeVdf$jf`68r#UJ8L6W!CqeN zOhE42tI4iiaEj_ib?z)+2`WZGUB7B*Am{M0*~%-zr`L0m5KD0)JA;91h@4O8EB))O z7GsE|Pb-Y;+OMnWQWc6*0&OwUy4x0>XmUIABrM`itXn&Gl)uWSPL&RoU-!9=X=Wc_ z9gK|rFfZ-@<1`F%8q!bj%S^w9qNVwQ+$` zzVghBEynALk01n6QitHgsTsgv<}n#Fm3+)V;=Vx$9tNf*%v5Lk)BDKK9rSOe0HNQK z09{B7FCmm#63_*knGj&UfR(Ff?i%(7`!akZ#G>sat*xBk4;f7+x7#<`H-O2mTD#GG z%a6~5n|hIkl8Zj9+C+nksU25Al*`Z6mc zopGPNqQmAs$fC!szC9;CKY@unJwI`8@Sa3;ReP6!5JN!@X0&?&wpXa>;*(PjwXSC9 zQuN=Ncf?k|rW;8uQ;N6%63iLC4 z`y-Tqs6fye&5xMpOaL!c+98lQ>UC5HZTS8=2y4w0l#iW65NGJg zTAv>66?`&R~tHBRZq1DVuXy=dE?V@o_ z?=6Irg;0o-(8aP4EGghp6idx!H^#)uV1i3Dg!9h%=ZXn+i;3bYVO(0S(Samqd1`A(0*e3K9>P-NX)-^ks&rMkaL>u0)ZSFP2@M&i<~;yx z|BY3c=1--%aXsyTX~iq2h35)eSxc&xzeq%v*l(3M&hw(QPLh9mkjPQ3;JohTb`2YL z@=bD+OzUci17K$`+n*>VkLW`&?|igp(m)B!`$T72+Y_g-+Z3Xcxv{008s`egz>2X>{s% zVO>)h&U}+3eC9`#+ul$-**>PybVqrZA4=te;=kx(J{pZkfyHCz?w6pmE>!kPk=c)H z{Bem@zc}$C#V?hs_Ebc7BrkgnEFe|ctRPNs)Az}1!_Z@pFlP!?&t&p7&~s}{O|IR_ z5%mJQUDlqM69_=KYDp1^U5g1YBrtNzYpSs;z=l>UY0nPr(pr#I2rSH{M0l>c0r(W& zg$9A0H<1)I#%xyNt}r4|0NAeoW~6=tnMhaURxaoh9X_iq*2694ss|$YDIf|HLpFoXZfzz{wF!K<|q7F6QZ=;)J-1wfZ z@0wBx3YiNP+nr84ii5pK7eMWugzdc^qZV-Lw`LK)0a^Y)QZiyWR_Wxf(^pl{{2U*% zpX8Gim+b4T1u$Rx?uolXxnDrEyWwLQS(Wg2<=F`~|6~}of6=&WQGU$vQ3j?VL-FI< zE{aAPA!jz?K^wv%gX|Tyi)kPav+xxIJa{!K2-6S`};gCMmXjADmsnQi&x z%m}SZOMyt32_%=ASw|wJH~a%1arjBzllKEGL}DN?(GV`KjqyJm zOvW{Sn9&XPcd&@`tGX15B^0ljS{?XA9Mq06O!Ue7fH@5b;wfGAAteK?I<1M9D_Rfw zCVXh$9Y^6SwDp-+ z|L*yE5bHjDG#mQkvhpBwr4^vfb_>y{#{MU0%+`6=_-5FXL(J8IFkHm z4748Ghj+9ap{c5U`l!vELHwfUIMzGVGETizm-m2@y-1n0pok^IW3{WGR|tx#Xpt7N z+^Rzg;nZ!Amvt;N;S5Y8mFso8k$4o2D`8w`@t3!`r%3rN0|)5RGU~RS#nwv?w=<+~ zg-Ay>S#t`JjmD(~zh=_4Uw;^~!JdwIElrzDwq;!E$W*vs-AM_Whp2B5KvnMbn zD1}X2a637oJbx&?JLf5AwF>E#61qjqq2zpnI%Bt?zO}fKzx)ND2!AcQR}HhNZjSnK zYCl+nBYFYe1E+<^4JUA~v8;%ESE1q}jv%H?ryB;kG-E>_7g$odNrkP9!gWhZZsnWS z-LDm&R`zO-fv}deb)~&=xWhG_x|hwqFUI!4pCZ!t2S~-Bk?#84q+_v0)_o z0;9uUD79p+^I-*8!K4LT>j&PmHffY9tB&D>Hqve%)dw=pe%6ZKduHQQavhhU7 z3@COg>a+cEEQEnYQJwD(D#emo+v;Tg_3i z>qpm=8<=vVY}?mozruRxnQ>dBnR4a%y>tQ+o>v+v3*FdzNvKSRF*X!F{n-otmKDah zHx;K|$G{HLd-8B>vs_l~}gtX~Vtg-z&Au?uN* zrd$R6nBtOr`34d;q4tg_<8uV$&|!$HZyu2}??49Dv;qrmN0frd)WmUfCs@xp)6(iG z-CpR^i!oP0 z6=V)XR+9!3&l9su>vRE<#Q%tGhy`(IUhoxys&62wjrF=~ZE0Ww8kNUmcDbQH&QqMG zF=5X!0%uiZ8i}f_Os6nd>G1{M4>IH;zD2n>KjBNn)!TtGScrLVfCL;ia?bTBDLj?( z!-SLW%q?#9phbFC7xlt9Vu%l?^Hu>sL8BO|z|VyHGzkd9c~TNWz4sc2cW&Rg=rsX< z>|RN^_i5EJ8tqLZfo4??Bjgxm6UbbuGxx126^1pf?b_Zy{8j^_iVDDWz!3t({rK80 zc+hc{z|=zIhcf0|kNFBVB9bQ&8I#biE);E2g8&V%0Zq^x6Olek*EC~@UbALQS)oVF z(-5)_sNr+*h}R;+X1s6*VEFd6O4{2yWnkh$-Kh_gd$(F@%Y6H>4vejA|ermNK;?A!l*p*i9{vV zE3Hv6mwJh;rm$6xPgU*u@kf5%1Eg47$kfu*QP2oWW z*u%Ck#8pYGJ53VOEJIm5UW+&d30j`UGMZT(Kjtap+0Jirg6289jyIwbGjFmdjj;Y`kT_1qtkm{aieO_c#U#qI^uoFAr+i1e0abttwq) z^#QEr#SQPMIj;6Xs+H_P0=#J}gJhhKfg@F@QqeKQAt)!L^Kfv|)jxB)vmq#`-A8Sq>Vb4r7=z?`EQl%r{lxfaC8vqF&z?Nf z9y>vgRb;EBEOIA9mlOjS?d&7?#6*M9<3@0Em93x5T#sJFVE zUcPSwTAYw7N+sV>#qXTK5KCS{V=T)om9j?DUq8?ENxfCY6|w1A#`-E`-qCZm}P(ltm$-jV>sO zM!E;pWXK7*nRmCQqDYP%=P)>@6m=7PR?qHOWyyZ-4A@st0U%dQ&m@1}GysVB&1WQH zuKuxx%HF}vF$o1-0vPYy&*jfpyv5&orxO==902>UcWwwx1{q`lD+S{iv1k)oax~+M z6u6ZfA1H?pwVg{ez0evc8SM!LPZG;MWWD&tJ>y~e$bqw$r<5|5R@iYB;j@Z|Aq5<`3s{8HpRiA_}&@^_!)Ch)6;yx!0Ar1tqch`uT=x3 zj<=Ocsq!Ab3!2+@>yTZ!M*I}bOU6(^LKj~v!suCVeCS`&JEWrDFzQHcJe5zQ{qQ@y zJrwZ5^%OK5qNJ1sN;*y;p5KK2Q--!pllMQSBwAz!l-x1G9QyW)Qbt+h$TOZ;L<9Ru z7=1Pu4?B3h`S}MR(U?GW-Za%Vr*_a#Zjug^&nY=!4kx&6P% z&Ti_7p6`~uU}$)IQSG@-u{YaTv#YsiRD#`8jB{+aRHwmJF8*|Gh^sVyc$ZVd7zI?` z91h?3AP?!NhvR3hY&v4Wt$_i&ujL-}3~b_Utz2}U3ENAZay^oN63ZfrB;Ach$~W?m zpH-!G1d}ZIu{b>Td_d^PIUl*{G$iG%wK3;0dAKJ!3?l+)q9qL=X<`MLh1mef=(nf1 zv!7D)mmeoEzz_DB$CPMc1Wgkmw0V;N+Ma@{s53!_8Q=3tyr4Tdt~;Qu*fl7TT!{?9 zb#&1{yLEKxGcW{lZc~@CsLPAW)Lo@psY3w3p`!rVH3BLC4*W^q8>e$m#(_3@an>^& zCvaNUeO(}GT_o%i29tK;CDgh#RCAaTKiga$9oS6^!th6O!UEKXiu@e$>sWIqo z&j7tF3f^)0>sZO>V#=wp+xGO=92tjDQYLeXFyHU>^x|>Ki9GCitG5AXEfHW+V$}@H{!s~`DthWyB;t^3i?X3I^H0yVga!=It zKauZt<672Q7`^8&o8mYC9X$XVP;OvsxitXhzy6>L;17zua)C!rM5cDfS?ag4u2t5VDWxLC#C$ zFF%j1(ljG+h$bV}@051oW=pC73Ac(M@>Bd> zMzp z)(yybf?&PbTrsKAPWaE3XHcY*Ym%pQ{wv7>Bz&r!1xlfS^<1Ddz-2JapNp#V<4udgB_5KDz$*6nCYY<1g3-b)^ND3v*bkeQzzE4owrP+F4f0g3qwZrm8 z_1{ZHiiV2fE0KC#zAF+!IS9tL$GXwHO7{3c+A!UF zckAfADxLLtloAE1vT4Lzfrkwc4V}eN+MmT9Z=IlE;d4Bvoy_YTsL^iyL#J?!X&am~ zvmU45w|*S~X-faC@84&BhAV01@fFN}>VE!}*9Ta853wDr&zR6sd(+VeYl% zMvvwpR+3Y9KpU%jv8qALncM@#CrR@d0AsN+AsMzXdfQkD1E1Fi!@}XN19fR{1a=S` z!5(|^)x?!~C4x~eFqC^` z^qOtwkDQ?FK#8Bw(A{&k)sm2!I4~CgzIGQ4P{f(zGZ;!Cd)h_^RUfvuC`+QL2`b~$ z0G>8;)2F&vQ58tlmS=ZFk=m%Q0P3ao&9k#RC>u^crgjKp^Tu-mvh(iq{mR8^laB~EI5ww4E&zjANbfLTPDqdga!YJJKLt^;Y3=-%fdBE`96p7m?ZE5fj(h<7Ksh#d*Wv2N5mNtFE%xb2zu4Elivo5a8=+7wW;%e~ z@eF6rYt{?CPwNMEqpT~N7#B_3tYaKIUVc9i1qoozD$LT^8z?*9HmaGm$vY^1Ab{w+ zG{sSEA7g4Sws#k74y&b%{G??9|J>VH+=UA(H5BRIoW`mAVu-_FSC%w@7|J#+2Llx7 zb;cq=B;#s6R8Z%(Z2gqJR^`rV2;e%ZCpB;N0KW4l*J}p|mkH}NmUAKFR|42ZCVD>*2yp$ava^Hl#!%iQ4{0VAXm zuH|y*Xm%I?<{>aOXwoFKYAi(yK7!ote*Kxb@tidRW!(iZMPE#wf^F8VySl=8cCDd% zW5kg{b75Gn@0|n4FJe$k`0DgB`x>_5S|8x8LV`yS@MY{$sN}_uO;OJ@$BB z_v?OL*JC&n7HNRpI0nRPFJD6(fE$c?+15Dg6xJ{XFX7gUD>&<@tt`#*1ihpd>+;m3 zL$Z?6H)JLSLhqj_jol|I(-wRFZSmgOGVTxKRCB898^%<`YkLE&-p!lNKjpvd)te$4 zvln2(4_I?IRa{;3@j)9cxv|5{BoA7|cu;*`Y^@Y|*$nC8xCo$ zGaikH=0K{Kn}LRL7yi(o*K2agGDGFOnB80#UKDT)SlkP2#B!Z?Dq;)=xy^h@Ml{cv zJI9c5#E_A;kN(PEXrLk9IXdYA?q5K1&hl@gLO7i_6I9&80cboPVibf}5)J91OFI6% zU+`e=3$d2%dP_rchc!+@n~OejK)!nTa`)UqBKU3F$@aJlgOY?v0-qN?w;%3-I-aM- zU_k+faek)?Kiq)?^-KSbO$w`2;QMjRdUck_0sP8tDe4Z8tT2>nGnH8rP~sCTsQKbP z@r|Xz~C#-&y-5l(_BQqxL_p?KAuMDuGKMj2JD(;ls=DH$dp{%@pDs=CpHv5ZL zZ8Nc;1uD>h^($U$Hw|0Q2Y5zp^JxoYh84rp;d|a<4#SeKHU6A-GZ!ww5 z;@39hDH)jq8A#5FqlQZ%Vqb?+MafE`&AQyd-f|NH>s(7lEnZ{Bmm;BF?DZ^kh}^0- zVypFW;ppS76_0lBsbuF-Dqr#-W%A-kWYix&(&+>LI1f*Ud3ub4<`&RTg;Mux4$CQryb z-n9qUTGMv?t(3&_myEr;br)RRBZX0dB0972#(&PZd$*GYaz9nR8;R z@_|!Jg8V-g!U_M)#aYyK;6uP9g6=<`P9LeE;CFaCv$+sIUKnKS)0kJyYI(r1XnJZG z;QF|2)zM($DVpPr>$d3$>7+7Fncf3xi#YvHAP>8}u1Mh(N6RBkk%5DX?Uhl%ysKfg zlzmBv9#G~zj0N%%nW&8}lI(kXi!V-DGC>BiBnYP|%VEi5fQ*>z-up79>t~?v8j3R( z(xA!>xgNjfEU&^z&&z>*eHGP9x1gI#3rha-K}Q%#N_dDVO$p+blgu_>+02ywMI*t zl~XTS_JO@76D9yhMlWw807GvvWX%|IOJ8nfY}D3(1G;WieQ4cz))_nxCGG^&9&M_) zar__(NsXg(S10NQhOn&3F*Re>-lOkc$?2^t{S1Rb$XQ~r$hYnHkQ|ZeEOq=>d_amt zFG}17e7<{^lo4}DN_ANPm;r+Q(7n}OmYdc4S=i*F4kPoN5?<%|E6}^^<)hA&UCsCw ztXzHp{{*1b{zzV8|10|@sA+<)~R8u>0fzBfuy%w{7}x{ zr&vc;PxK%Q_;Q%DSN~HuohVS+xBPj+! zDMZ)T`XKt_mqFja&Alvi)-! zqSIqPXsd->6-v|=h?`kKgaBV_9lnvgVgADklhU&>y7osx{5j% zmzfDUX@n^R!N3DPMhoL632IQ}=4*qYQ$Xqr2Tab|JDmu=Fv&A3KccNlwxbl&feSN2 z<5IfbrY<{X-T@Ex`iB**3m+4UQ7NPDYc&4>F%B9%EW5eXcj?dSEyOya`OL_@tFZ-) z{14jNwDn8rN)VrLa}{hXcub{`hJ_%2hiSxy1OxHcAPReTZi!lToq+r657~^ zGfqO(Vtsqw#03{#&5@O!McKvy#;@5KJ*tUR47*jv3)z`pG07UXFmL}a9{Kv>$a)N| z4T|34akC6rHq2j5ynCnpeG#>rLB!Aa$rwJPl1T4fosf5+bhRmZuFs?F-)+3TiriRw zuM$JY6mp=V|6dk>hY~(;)b_5*Msf0NFi&u=#x-^z&pI~0_;8QJczUAb$i-W`cAfKA z75OtKEa&;Hwn+5(NuMp$FW)JJ(oiM?pw|}2@H@L#?-dr}rNvQyM3%T)R+@MM$Rj6; zPTPeYgB$V>zz(_eWT!1l(22N7s+(f=@F<{~t>2HzNdd1cEdHDSKUQxK67<`EKDh&+ z;1_d-lB;XKf+q=8O~HjZ?7wL#uNYYG{8b<+&3;w5kNe%i& zl>4a-dW$JBDat8ar-_-sqBO8FVQt8h}{ zJ?OIX!zipnznf%@0QKoyAaSVKT32aD(TM*meR z21uyGuUUP0dB6sZ)ol z1-RD_ThQ1Rjh{er-?e2I3-!*FmlF2eCA`()Qxm1$;zTRgxqtoEJ;=HbHxsKD=2;#6 zHpqrYzoG(7nUNivcGl9m+X_TTIhj~jqUs>bK1k^1T{Fa1U%2VpbW9sM$jbWYY^OK!#nkJ6>Z1%!>Rxv*CK`~k6 zM=Xp}-;~O&3V>EY)|-S*Q&vZ9#25=?!ID^V6QCoQ?^vhpha*>$_?+-5{fc4^pUGc+ zyJ3v7x;qP{*xCfTQBMzW){lB0F@Tb`!>(jr#l) zrYZ(<{TSF2i+C{kxyo26Yeh~bgYLW9AHn()6+~+=I{_cOB8{M|utEjD3PR}5mR-Ne zsnLT_in{QUWF*hXK%wa5m@ zTrx>N@y+Mu%NYFDr9+FMKUxCnzl4DZv-j8wG8gy3A{m3}msBA^#a*-cmd((@mN0;4 z@k|5ZmB^e}D!|mbz4QEk3pFABv!@hb3e%v?X!dZ@YR=H%Yd^Es>-1ASl7o5MiAD>7 zVo*U8>h?B7M2xTyR5%FuszbhaHK6S(dm-yf_i`SLGpvcc)P|_+$aA+tRa`pcBxFk& zR`xVi>|p&csCyn|=r*i*kO%h6YDj{eb&30Ba!f`Hs~vLoS`frFTZjW_^N8?V6R zOIm$U@f+dT4<&xR)=pL$qto{f_wXg=R|yN}zVtR3UXh0<*#)nf@_Z2?TT=gVlH!iLAGV%eLF8e4s==KFswL7Kyqf zKJ=zlAYs1c5KBNLW1H`Q&n8K!J`e|@;46>A^0UvlZn6dd406JWu>K~|%P<*K8Dr@* z6sXUf^(5dA<&^`$RHkHkGANFnU?FSzQsig9v{{*4Vg*}K7$yAk-cI><&kmrvnA3ub zsm>oLzC6YHv#}IQDj|Q%T0&Me?|HnK(@CyJrWmq{{O#@W8mpl8b6ig9Nf|K4+vu-koj*WkJaAx!MjWPb_VQ06rPWC<381TO_x$*Bm?yF-Z z(mpO+U#8RzvdeCAp@rMoW% zy!rgdhkqquhEXCTXQfVq;uxaYylU{co8lGlcyJZPcs4$mr)$+CcK>6R`!2*U8eVqw zP|XqTEz{3m#CmovZZ9>4HY@N|n5cD0j+^yxa-5sKiOp)igc|3qlP$ zCGm&P$&8>Bs-hT@W*0nGsHsM{Iu9k!Mqx14%It0oLXyz)@l?N zLc%`xpSylfJGst={U^vICmsEbGY(Tb1qsPd^NktPkS{f&BOC8zAnes>(f>zqqt{>d z($58NKFiXF>-zH4;Lhz+FMN2b{GbRhrk@%&PnZTV{U+Krt%t2Q_yHOe1Crrn8AJ5s zTKhQWYshSrV*7YNuTzaNR2sQPOsezBGM2_?%WJRwVTE7~*9Pgyf@#NJC$PAm9?Ual z`SCSWJoXGkEeeFyX0??my{^yYkeiMwjW+Rl+h7k5pte?<@hJWC8Lq_rD9!l-4+*JR z5mk}b2W-Y}xsGZp6hXy;+XW0oQyL3OE`$r;YbLhVhl)*$$sLgW!pE2pJ@6R_x4yz* zFipb2wN;TEDj4Bq&|Ca;__of~1@^pGV1tk*Xz|Gt$NS%PRF9_(M_-(59#YncFq93s z>0A}jb(ZzeMUC1KIhAns*l+~<-^XnHbf{$!H_2kq`-wJBozF#QY-0Z2Nu&Z}>y?Yg zn;%LU5P5&^D9`+#{RGAG{(bSmC|=DZXRXF8nYr&svmAijy=?Ff9N^e7i>Tx!wC+>_ z>t7U#S;&(_;w)O_?Vg|Fw9q9NGII^|z@t#U*!I$b6V~tn>-Bu_p64ewmqHZL{CI$c z@SD;Oep4<0YP@Iqu8~u{rZShZ9%o;Q6v-BC>(#x#uE2;ZZ##HZxM1~HMdheXEa?8M zn>)u@+WsJXAsh>83ref_Zs=($cYYxfcIYeY9nx%2?&4~0r%-msy%vo}|8uI6_1wq! z(NQ+!XopiTsLZUHO)#t8?4b`=e31#tdmPz)8VFUyVS;=PGUB=y`*LeQ7-q*rdcfq! zg#Vc5M6Mxcu|-8Il`(qdM;CezFMHccrsfmc%}-Tq(Um7+@`OfktTnpFlryb+!tMN@1;#LRpC*C5v8Dz?To0hO00MH&)4z!U%06|BLY91gpA9_N zSd{Bl5(kjwy|GcVMi>#w=7&T)qt9{WDE<4Q zOJ-5&+DJ-A_Ber)gbiAFjP;_-;)Q-?=>LPD@FvHu(X2`~YkgK5!ujV#3zUOyzJdh@ z^#e=3i&>&te7t%I#)pj!9{kDAW7%E%+E&oEYZ?cB(B$IMA9cwXAP29EtGHow(sL@} zle``}`-W|E#VTJSFg)Doa_b{h{vF8wdU=AY>si`shvr<39Jv?>L^V{BN^_IR7eg*8 z)v)$id=J0BbAzvHw%L{rDE?bn1JsGlkUqY^hOdKI^=*}FZf6ECm_)OPzhhw{6~FbF zrS?8l+|jX}Ax6Z(yr}E}IL9hr$N|Y)n|Awx)8;l&4LyG3ZjYdCIx(LSOyclpu5AF!??3v~W;{PRg~05RX*GdR$Xjn{iT?b!$0# zINX!z&bj4%_CVZ&tgOymajeVx_2olf*SUHqzRZO1!M?QV^Oi7Ts#R2O2HF%6ABv|% zL3+bX%lvMcAU$Yb<^9QtP-1n+r#fztv1Y_ITP~?K4yM3#?Rt_TF~=gu+W1^)B=){Wcy!?>usK{_3)v~>nZ`0iE?m77hgQ# zRbDW3$r8;^jo5*&@mybnGdFvf`{{15;Clf}>f?&XHnr%y)fFDVG0=&gS~30`=Pg#u z(}D#4B<414uxr@04HshPH)iyn*-Ly!tPq?_% z9ZLBhI73ort!}cWuJGv9wa#L97kPsf@^lf`88~#6T={vp`%8| zyMt`3sY-8H;y?qcC*i9f02mxtES$m0PWluMx(XWEfbmhufeg7LTF$#xBxDMO?KTAt z0@nk7>3ZE#bB8`T+?(8A_MM`L^jo;^sCRAjR_t3XEP?-pY5!9Q8Z(xb>);Eqp2Zns znhJ|rLQjAamohJ!c^@{^bPzl1-K#0|4BotYKr;QEg?9}1a)9Gi!S~bemC>!3XSG{d zt~wb@_3yj|68_4+9=dlU>n@%}KXiALyZSfF!f>pxy*mD{5d&Jxv-hP9Oq5{*`7}|9 zGUYV4Vlv1*U}v?0&ea8DHYV;2+54U;`>?9I_gaRbzYo`D3#diX;G%dAeI&%Uv~eE2 zj0a+Q9orNk^Hg*vnkkuRPXsc)(df&V|22O7jql(LYa?b4e+}TrLol4xmf|bEYjRsa zBj?r;i&=6y_(#96+RHcFT^t6~zgGb_SbK0_v1%4*q`j2W_QOr9H@Vpw0Ac1IHL5we z=Tb|9R=7@V336lMdhztS@Ll0-zac?2$ijJqzpyJR!TKpw(Kn7S<~jN^&rB{e7M+yX zy6v3Dy9>pu;XP%l`&ClRiU-xmE<3s)BzH5HBdG&E`F|nS6^%W*)aaG}8M|074xOU%{q*#Yf(DPh%BJRe`4tw- zKh-g4(eW&$jy}2e2>s_#?AECxq%*X5EOPeN>O_E{+_#cP@4UB3Ki7VFZov^D&mToJ zTDCWFj*%g;HB9NU93<$a zv?!X0X)I|1kxQRIW7q=pwK6PPsAfq4n=K07z=Oi|?0#0%+j-ynu#nmh-hI}z_;#Df zw-Al**996ybr!Ozb5Z!-#O7l__;XRIUU`|owoH^-_ce6${LpAovAk-QN(N&zlG1T< zIRyNNx_2=Q?6ZRC-_I-D+;0(a`F~PQE{8)@3>L4+k&c#tSX>H^yS9v9Ti)Jp?aer` zJXE*l7u*tkA4%yVaaT!K2S=GGdf%v|tXAdHx4i0k@cc!Z`mV{HNxDCd$DW6t$AWe| zD1^TkhzY+ThoL&osuu;qS%d*evBO0V#})`9Dsh2)%qdIR=#K<9JG=#B5o-;2q~hGu z=xB+O@l;k#bpVq6jFJ;A!VHH;4miSDF$|40@(dI=e3|Sb^r>lvGrYu@Xn}Kr~mx@B)na$ub={dV6LULEvo8zRQs@w}J8@ik_jazxu6W4Yx#mIHpCV#YP}=YU2^d&S!_-!7nyC z2Z@ehtRW)JaMR;iTD*;$#Fa1xTTccT^#0!u@ch7|XkvgmYCRMPmf#Mhpvfz>2l2H3 zJb4G#@mmEveQ?6vaq~R_gJI%#s^Ppc_%gq*B^L8OhMDT6w{$<^e&lMLD2E>M8xs~` zxarXrU^nsi!{<*VZN#lI=W!$syN8EFH3YyHaU|vhX?4=tajPsl-%TB7k@26SIFz9` z_7*d(A6H2Un*XQg*VNpB07uKfwlj$ptUp3|8>W0y@`No!?UjKTq{^Kiz))GSQ}X3O zUV)Ei-Lf6P;(&qo*RX7G&ca`y*Wsxav#DJh#E>BwQz_3P5?Y zc6gD@UV(0fDX|l`4wnmrmFzzMWnEYgt%M)W_Ez{92GcKi;6B2#QBK}^Ewas5GWJw| z6#E2)Y<$(%nPQT^Jy0LfoOM2N7TYDrFB7B>>`tsk_i6~^1O2y6GRthxchYS~H&l%m zOnY(VtW)VOlA(MP4UtN|;0@L?moqj76F+FT|9!6OKjEOvsxNx}BDb-OJA%aQI%lykc1U_8HCxyz0Z-X@Rp_l-}_Sw_ObHiWNpDXW3wo zAwSg6$dFfJZQK)>@mAgta&^d`*F;Cb1Ex*pst*scb|?hGP7It!W2JWe2__%3;qh2| zfeS>DhKyc4DrU(N7nD8YCBJMzzrgJwOuby2$E?jC@-61o&i8P?2lvU4h9`7)qA=%m z3l5h40WMRaD2E|aF@4@cP#iudFebo>(p!%(6nIzLY;c_HDDr7J9ak`w0k3(W>}&HA zv~rrL;*Y;@B#~uAuAlE!@t;LqFL!PYhJe+`c8@IBGkGZ9Cv%Td1|tb14@TN5gOs(G z)qwD@oCD8n9^C=!L>|&poeJGAX7GD>m&_SBd^GOJeSJ7T%A`?LW3Q?J_yIYwH8wL^ zl>4JMRmbGc9A6{MIHJRckJ{9DyGMZ*Q6N%vvqa4T5C;rw*vzP4)4mM7N1TR=NNaq#Bf|^Vt4zH;X>xxJ8SzibOeytR1%4q^p) zh@_|1q*1DKcNnSW>9)CiTd|~|u=eZBXz&9R>-UXZi6?b{Rl=G$;5jU~(PUx<=<5{F zWei#0~OdKayviWQAC^r`jXE zIgMRrpqlSBmCclwB}^ka4o~2BnJp*k=;M78e)Ne6)EtI=I)8*a`FPuR7iRL$xd$x| zg{Uz0;gECA_5$x*)`V^UhM9S+G#6iz_Zi>%+)IOb-^>C^70Yf~-9^u`RgOGrJ?PlV z4-5Sgi?i_9CcWNMQZ%U;KKHh;WMk%4EFm{3iW^eiE58t}^Cs|vqB`s7SdUQlq^{Jk zWYZwr3D*#%rv>+;8t2?fiVr)~>JHx-h4l1%GrpH$Fpc?G`{He%00X85QuwoZpr4>yUT(9vE2mzV>ljvgHTjjE$8YPkaKevpHhSZv(MAL8AmbJIwK z3I;voE$6$|Y34sH9895WM#?3+;~>>={Te}Jy+>-O7^X7Sb8k}R99u}U_G56Hr1f)N z2aLCs>3OaNqk*}&ZrxFcn&j8H4G(pG>OAHh=IZfFmmU5Pp5)|Wf~?()-@Pgu91p7t zNW5HNH2%7ZXV!kL_qKWAi0^vkIj94re?71tG_oX^;osIjZE;t#$QQ*ptj3t^nT}9| z0khkS;=SaiMvg6c>YGvr@Vr}u@NaPTCFlElVX`Qw{+6!t>GCs;0Lk2vyZr*ahuZ)O z>(e8LSK=;FO++?qpc^b@uaRBS7dTN^SorRG6;x8w(Y^A_s19T3v`{32rD7E;TE4N~ zaCA&i8GQopcNs7i?)BkNO}j_#TW5|RBIF0o>t?pHh=aAGY#nP_Y%ybVs-xPh1FGZR zWOlPh#nNo1ujzv*w5)xf!}fb-C3=5 z(y#EX$+lUp{K8Z2FVqB&ssdkw(4@o2Pt|`yy9rBq_;11Mp@j?GhM2p>yRJ#iWuT{? z2MnbL43PZhvu6JP-AY-)0olsowNF8`{lDd(-3#9fin}*0-!7fB#%iG(01;Nb3j6A*a+h#8o{bT1fobmeR zbil)a@+}f=2u?EKzaS5@o7%2k_?>h9*m?X5Zf-}%XMM`-P8Li@uAJ8{lwD)CkFlY% zHqNg`0lt^E%ZV1pmo&klgd7u*;CCyBLXftqR2MB$J1}>HbmlfMAH$; zm)|uY2o0suVy(yLEl$)g%ye}B!o~~=`a^~r!kU`|!K-m@QTX!g(W^*yy%*<8l-!lm z5bf0;lDLv`GGGvCq;M3+SP(?cIN^)<{Qg$MUYyYqBACgHu{NmOS#vK~DX9H&r|xPA z`%AnUDEfL73&6Cxbq@{^aO4`?;-aSe7uPU^RiO#q=6**oB^E!8bNr3kF$4BHAWTN? zG=P8+CGRB%i=EG+&oc40UTxSaZ7YO zjp0VX{{jEmdI_lgxU=oW+h$pipEI$;e^Kh%Bppwe`$9XHL5=42U7PD$uDue}VK@03 zHCBNd+nt_;9trrUUE1gbHm>pi?4~>U7qZ5IX zJJ*z2w1*Vdh~U{OfY}1#=1Ty)@zz~K)(f#|VsJ&^ZP=2)u4WvIcD*w0c8v9w}3Xu;#kORopA(ayVCkE_5PWL$_N&&H3OpUqkSHDe<5 z3dD&Y)nZ){#N59${H+i^xz*rKE#3?G9*!+woU&76odn5W-bji6ng?H3b(qQs?&n4n4D?j-oXctl;Oxtp{MoZp$3M3l~d{21T++BAU?~0Boz3TbG|KMs8 z2zPaKKlQ7UCb@stQv444SC3*{4?n8~x}B<9`54oui?Yl=O=HZ#`ErahI5pDxxfr6& z_|M$ii&kj*4v;$ytcD@UM<;S2w66`R#=4V%F7)wvNX2{3N)Oi~0LG-%WkdF0(OX&i zqKWKCC2z!L8VP*-ICszC;@a&|_q=XgGKnycQPe%vstzCj$q$s^mnvu?e9FyD?#7@X zm$=4SE~Lma+xT8pw0d;w?4HvT>XlfF! zvYHIpO>=!O#MUNxTzUW{kf@(|Ve?-(#eJWB+o)oLPN9?Bgm*mg=FjCBA=k}pmu{%6DFEyJnFL9=Zd;j zPdVK6+%qI1T`7i5BV?`Reagw0gh=#wEGWp)9Q?Z?u8#6l5^xfgV9eNcBVAWRtfEV?3s|hsK$4--FtllUyWBc z8ce{O(u^}|^zo>zMg9Fq^(M{H_6i5qZ)jd24E1q-M-bHz>QTX1&otD^ELa^9EGhfh zp6@9o{N+6QyJ=c(G5Kw#+2otV`A>-+Ha{~yxhORE1HVGLT)d2?ne|YBZ4}?@NE^^P zbK2K#V`;>^eOA#!!A~Q~_~K|oK-~Q>@X{5+x|bR#ZVB#WEPdEC47GUQR`9Nm>VPKe z_qjyt zrHrpgj^>kJ6};Cywb{X46%Kb%+l1@ZXx-YR?mR|q=jl5im?zxtCTG}wHaHUYWvFg$ za?L>(bMcv$W1QcK*WV}!?S-8=NE2pulM;{`z85m!#kmi3Wu;==nSCDvQeY>_{6{|u zw)pfXlXB{;c)q6FKXMK7cT&8VDNFQ044b%S-SOtYZ9`Etv1z5+?5k@;m@>`(@$6-eA)VNJ|pQ{mj-6#Tdwd?tnSxi@tIwL7lzX zF&@+)1Rpja$dWJi8}lcefVur?exWMdfg$_8I+j)8H;~$NUjeVQ_V9Ge0fcGIryoNL znB$2QZU$wsfTzOC-XZ=b$-Yo<%^uo!gIW7ZysuhuW{O5i;mOr~LjLv;#_grbOs>CK zo0;qLye(*dP1Zuy`7Lj$kh`=7=jLGs#ElP3RN!g(6Jd>TO0O9F3#)x&&yQ}-hQa>Y zdW(nm(mT;@O0S!fNWK8?Oj*AHcgxj&VxgXkHY0)r&5g0*yA_>pB&Tg3=fDs-{70pE zp<3teYWdH8DmCE3L~tG>&>cSRSYy->kniV2<_e8DZcM8$!$6c6%`jho5wl5ylTgF} zB$2Iue41l3Lb>+IkD5i{i-;+`tbeGXbNX?jp)kWT+AqV!JJQvIFqy!=7D?yezi;Y5 zFG;QY9z3Fp@f|R*;wR|16IgjPvnWvA*6+12^g>wmGH~F@ zWjoZJ$4}w37eYdoFzt~)9V;wjeRVZvI z4s@VSCk8wdW~!0D89zbwhBo)d_Lq-aXeOsN6kWDQvtYb>jq#C|uStKh@je~@3_MIZ z-NfikweRPQEf=m9%(|2e*kMh|@$%&S8=oEZylzh?({}`oE?;wc<2u4BjO6W~J0(C!#F#KM>(h8-Q1w_xVSfaVFBr?3 zu(Ea-Kz;6Cb-~aBHmSrYF$VkFkyOMM0>Kw_Ix4&I_#mJ%_$lJ{JdPMqZ|507mTqH@6t}(sm?AGn;Y$p%k$Ta* zve_sdPv9GTr()sXvzZM=y#Tk|j7;~oR8vrTLCE#{L z^bAD~!w8!bGVsdCd(WKQ&|a$E&^{Ec-_8r<`7zJ;&A0O=dk6pWb`xcD`_;8ue}On* zykDqdPQi-^PR|8S2$0>d<|G#U_SZigEFU1SJM_~SoHi`mG{8za3#Wlg*y$P~pvChR zoKE1iqH|_rDttOzsD`OTLh;4JP0_p)vR#ar%^qsmtu^1xw?~M(cdV8%w7kCEJEd-% zbYlBQ-1V?!l}2=BEqKEc)St}4lpr(H>Jlge2=)w+#F-*+ zhsxmYcKn4Jc~#KC?P`ckf!iVhRgU?>8*JdWqfpU?kbgLqhBQmRf^mDU%bG5FQ+KDb zyVcM)oX~i+T_}rm2K0o)XEg^i&Q1~)xv{`*skANLEnfanUfSfQJicRgaqiQh%_p53 zlfKuoI)$XIuKrO;={E-a&OoxyCpvt@-NluN(Y@GL8byI*%Rd=~goO05U>yCkxHi>} zP>g5YveviQnlkQKiWsL0_6o@RpMkL4zgxUcA>2CSSW()%ww}VF%!=tl znKAa#*`5NgUuoTNvN(D0%R!fyaq%}ER4!NKq5TF&yao_Gh#n^XmCZ6@*&(=)#%BxZ z1$xG-mSxKy0~hRmCI9pKt>ltQ%;>Gh@f#OkY5sHBIr6LTnxwgMV4NkWI-5BG;@O4W zyEMXW?0bk%e(;&&%HNkg&0q7 zBhugt+X9;Nn|#L^=`Z~l^qjMS^q^Z1Ee}4?N*m6pLVVxZ%A&ds9DH@I8(hlFtNeXs zq|BXrD7)8gIH;S+-soJEm$}5LrUHMGgs+~a1&>V31%=|*iIs0g4gD~)}o zy3bH**>i_m{XKnW0)v3=@|BO3iH50WB%RocEPG{Na#XiDt0xiumN7fYA;o}9ZrC?A zz3R=9r!BN09=*4A^hI@lp(oz`$`fok*xu)o5YOk6wDAv@;@vWb7f0w@)nw^pw~L@}$vz!v`0UmrU2WEfhi47;Df-GWE( zh9x+a!3`APIkRzs>~enDoEiy1-r8NqVvhApsUE#x!^v160KmvA@`XThvde!9e;== z6my>z*YF0B9KWNc8jzpnv*X97WmBu?yHYMv{7wkKGcJ(r-|Jm|C#AE`v3*#r1D39R zyn*;)Y3LEUyU!QB{^OwRd%Y9D?aoIK=GUg18qR2sr_D;u7l=;w-R|i1KVePm+Zz79 zmPEmaMCTd*8;3(gBh%aMb-Le-UMp&isK^@j9l5lNxN8R5pVV+m7pQab7h^;8qug+d z=# z;6?EYx2HZ?k01WXti?P(YU=9qB|G||v+?C*($&U(-f0P-{qt8p#XP`)E0I${+lH9@ zi@{wgrE>nuGXL~omO@th2H#2?n$&iP zsMeolX_{5D32$0Xiw|<0TzY*rG$VBlInE)^2eJ2&;bT9K>dH&QE!Oy~(J}riEY6qz zP`zoQiI+K&rVbW^1I{AASKEYDPcQX~8qOAs_q>>vc=2;No50x~D@3%O%ecKw`&+Ya znEmM0|B0|47oh$MgZ#Kv^lN_JkcU_d>IyQ^xDiogqg(MI|Mbd^3O`@nRalBgZ~1vd zDE`EH%$Ysi=$YXwe+$gi{5v_Az5_Ru&c@UyD4o>}`SGAruc-Lo$;tV@4YZVhNJ~AM zgTH6)s-nNxTnG|<%`g;+x_W6w;;k@5Gf@AZ&_ z8xxT|xh9Q|-v zwC88_p0F6{(@i+e`Qh$g-d6~rsrp}mGWzv_MgAEjp$7mq0TvTE&uD^{vAP19qS`)+ z7ae7)rmQ=$-P9!U^Ho!qRhwMXs2$fv@R373`y@_mAJq!!N9+CAp4jHyu84kJN}x4J@bZlWA;j!{jxDEQeF8G`4G-RSca?nAppJk$E%>?g8pMS;Z_?iyAnkk(0v)$ab91PY= z5_**AY`Zx2<7Rz^^oUJ>v|5p&ea z8y{7hkqgGaCx1mo9ub12F!uYMzrb})sLEZ^XgdG_=}#F8Ou8lr5pLwsr!^Z))gu^9XoRRqHW?Adr8TYxL7--VU@BMOeTMP zI16mX2GnM~(fGb;@*!~J>~>-B#B#fp-FH%m#(Sg2o*C>rg}EiAg3Iq891n5Zmm_ZD zx^H`{x%t<=Be=a3G`*(cd>S2!mOym6$d_)-#-Duj!TYLhT5ON|q+OxAEq$Lqf7BLJ z$vbEF}&pTnbTvo!wvg ztBd`xy8I$1p(Bk1sX>tw20sLU|4`fmVZQhSfoh$M4>@>Rxhm6j^UR6hVA^fz%Mat@ zey1LhX1KQx96O_X^nUO^ID^ghKc5S0vRiU^7ctsm$GiaU!{gs~PCY*SOB&$)5a{|! ze%&=x#oF_-PplRMAN?#rWP9&`)$tB!gflru+0(?C8^jF_3l{+33Jg!}SW zW5W15$?h!Bir1Pm;m^*g2s!)Mvi29>O-fLSv%ew(w5}{hABG=JPQRfr$6kLn5KFk_ zo6wkS(|+^Byn#-fCFDF4c-;L0Q(eQ|>)BjiT)(GeBJN1rOe`~jn2+kXq0qp5+yCh4 zm{joinhkoa_-Ngs`P0L$VL+IHz z=vv$6cfOX6`|XJL3RM*DPVY+t3eJ$vaurvZ+df*&#$hLLLC-7(L!16)0ZJ&(=* zOuC4p+mBS9rOh)L7Xt2TxPWOc}RtU zZE(^3tnuz6KQ0y*S1D!LFq$)`p@}5>{-2Mys@s0;tMCNOnOry|Pa z2a^_AuT5{vK0I*CGqif~UB=m^{bLS({dZ5~C>@aPyQq=L`BD03hpH- zGV}3Id2zVEosfx4LCoD`opR==PFNmTIkxkM{>>MXW+X<+2`*-}Ht#*0H!*U#M_fl` zUA1(f{M2XZ>DoSV=y~1PmeZ1a{>!;U?gQe9ew2px$jb-7z$M$zOF!&1Teo`NK3GD0 zFlr=9n&!6Vtlv2{WE5mIUgITSR>_l11VdZ)9o!IHJc$}A*gmGSS(?)gHDvW}xJJ^B zo9j`V(H4ufo85nv6hm7xjhFij0f`sBXN49SEu=cH?#h_;Z=Md*rDON!)ZJXoqF#xn zvnNQBvYx(szDkivbKYiCUvoeF{vcN&xM8-8atXOjuG$|D`iXR!-z)zqfAXmSfN*@{ z!jNwksv;8PMI^o&UY+@N{F!}2Q!;q!i&Bz%F>fSx6(CSxlpyHwhphI<@MvoD%)FWh z@R3Nic}f>Aih2crf8jF#h&DqR+rC9 zy^QhKnkH(!&`2cVAJlqRrB8jImm(i_;eYd(Hs!nae~#e%TAiRiH{9QC=s%1V3~I}9 zu$MQZ+CGx?KS}(NGHt&Re!Iiiz&E02RO(sloD`?k1}Zl4BdWumT-tqM#WdFKOjFgY zcvebS5Px4~T2s*-TCAAjSosn+fC}K~(SNrJ*YeEgcy;>guDt&tE>}uZsvN4VJyWQBHkHDyH%hl!zgBxOH)Xk25yJvpjWitR$SN6yUNZ} zkmcy6*x9#(+8@m1&w>y?&DyQH?5FA5mA_#jQCfeFRiI{eg#BhZ1@G3lK0oGCq;+ZM zFR#VCd*eh**}(8hs?u_Fslmmo1g*hd<8nVZhPu!6>FU&>ligwBX>;t|{%H#c2rW7w z^OUam_dSjf<~e{4Gg|x+`a(Dr=>S)+!2-|3bH}1+jifxuQZF^6egEt ziC+*O6SV$wjzBHX+n3K0Bm1=oj1}GMyCwL~ymp1$v$LC`d%fnVHCaC90bdZyQkyQ; zot|Qx)^f6}VbbH%yz;)>;qkSl#cUfhG#b;ISmoSf3f$TK}~(p+o%GHNE1bY6NLPVg(cg5fzl4NRt{OO(79M0jY}8ivkKL z5F$0SPy&&z^b!aX2+|>;lX9cK|2uc?op<}`sfFvw z!fXC=9>3L`OgWlIVXqPN$J^HH;u3l_XLcuK1xo0t5?#fC6`hf z7cE=*pap(=B&lCUF1swErEg#~n?me0hjN)jL2b|5*&Gn$d?x%->IfIJV^7zWonach zr@cav6loT!<@QI07)jXO32{@mkc}AESLYV@y)yhmn!eFWioBpVUr4`bn~JmLIGsVD zbKIRxoopF~SW96{rP?}>80`?&p;stNyjo=ATlWzd<|6jJVjUupE1&ZNji|FD){{dY zTY|FvrWk;MAw}o0Uv3}_tS21$OXEx^fC+I??5>o4daA#Vr$%V)mY4TrCM`n#y_#3C z#Ma+vI_84hn^dVq`NUk36@Fv4y{2|y8Q$wn4WQ2ptnJi}G#qC-;AZ+9W)$Jl%DuFF z`AfiCz%dak(T3l=J_<=fZK4EQiOmT4OhO2;q!Cq7U^~=*>0xm!9BlY1mnJ603^{UA zP){R`Xe4ejcRb&_e>Q6^#7bX4MK1B|$axgg)qo{|!^)%qqy5aN`$6Z&GJ|4ILo=AY zXoLmEOEtuK@^dQA^5xa+a^;!0t~ z9zcRSIz^52o75ri>TgxGvHzT4j#u&gg&dAN9}{TkHX#cEakW|)O_tcc!aW~ifuC$3 zS#sV09-}GqCQizZNNA`juDXM~PfRCM*_8thN>(wRN(!G>nD4P-UGB6A_B^o5jI}#| z)xvHJwAIQMBs&cG(P{F8KH~5&Gwo7YWIL0eF@4&LR2S0sY`eS*cMwB*kP*NJ*;!+n zs5KM^_oA434e>B#cFe>9xbCI1>l$(nxK207o&3DmVYJPJXAWpFLcx|Eh z0Q~qr&ivasnJm*DvCQS;-+wdKN=Jz0q^-8}FSfcwv#DN6P6(x=hrZgEX>3fa-m*us zQkx4sOZErj$pcK2W#;WUHMQ7AH9PjR%vuz7H@g>A#vc_YlMn~@j5zFkO<&$6w@~cQ zlsS)<`ukr|{(WhcuJ&Di=?=Zg?#W42VRIb6tjmu1Ust7mE63ibVtNT@>S9GEa`{2l z9A;g*=JCzm&!dal0*1CfFUpQbG_`U>f^shK!hH+NWdzCs&hTWQZSC02kf#pV!!GFH zbU45C@+XLlH=X0Ex>3yV-P&uLqfJKv`Sf2-B4%U?=#fSU&6MmQsa7xBQ!ygky%O>4 z_A_4mHOe_bKR57wLhiqLKO^3{Zst<7&?xMIQ_qz-;N-8uR9*<%kAk8PDCjwZlhaB` zlA3iNssw2XTiq{#vqI?cBa6s?e@;=ti&LZUmGTzy%EZ#{g|JN|cwfn+8>!4&oYCg1p93s{ZD8BClBF zASod>w0eoN>rtw$*FCx4dhx=I>#yh^R{MRvRd@8MME`}%xN0gbGavqZ*{`)%eck)d z*3PGdkO(f>2XXTO7yH6jBy!Yy#qN;n)2L8w-~A0>b40>-^zQE)2hnYNnmUVai@0V3I}?m=HUV`f@JmiaX(3O=H1V5BLesPz`K};OaVL(*L7f;@KW$3x zKu_zaM`>ssba)}?Zq&st&J<2bud6s~?O}bYps`oC_a4*8XsQ$w8pth_rC>+CUGTKw zTmMD*#BoTPI$K{puU+8G>#Nax@hDq_GofXBXnoDrp3)tjF)?}JHCMlKbKhw>Q|XO5 zNDkJ~r7o|c?W>-8w@T<%hLv_7BWp}gGYv6DVa0oYmARM-oN)pj*g7f1v0FSEfXsJh z-8|M9^_%-;b=f(bx6-zi)Q#5|uKdEfqx}~;_O7!||>O91{3 zn;ne=(|*%uQcQJtN?;sJG+xpMVt#3B@cW6tqT*dt&gC}_Ldf~RuUz?WJU(;fpHF^b zuT0+@F!y1wAW8v;F8VyWD8FjH>msHVvWWg)7np;zyO`Q_zAKUNHeV47{*Swjgw4UX6dZcDg`6|)j5 zSM8U36?#zYdg~!T<2VZBclJFN+?0oo9x}JfSQ(I?dUiQb zPHyz!Q?VnQ5>KZkIF>bk_JUqGEv`V86IU#t)l{6Wn=YR{HLu5~@sXg3P4hwwMAS3H zbIjXJeVY>ukx=D7DHo=HtZBMSeBCC6Q`}i=;sviXr}!vb_iK+k>_~r)!m_uMz{-I{ zjZcMCdUNq{y?nZPYl{#7@%h!L2~f_fa|o>Y{B{1Q>eIORSDC7q;=L6l{peFrvV*I@ zO5J3Bj?`JPhoV0z6^W%@pZBr~Y>iHTtTOp*aSmH@qm?qIQToK5G9GiwtAMEkoI)Z4 z?sq*3x|NaZ4IR>fV_sDHub65Uo43}2p5GeIx;)dx<8c@${Z>A~jah0}&Lt~n{5t#5 zu)~I6kbEKqcO(gH%Eo&$`&37a`#e{^zV3V7c6GDG$IH3jm!wo$V)we+YL-`7qx@3e zlT1otED|bBo=@?2*@ivNOt%@+IhtyH)mr~uG;Uu<07bT<0CvLw3PU^f^Ou|_l|%oq z)=)^TI7}awz=gf}MIhDC@l2Srk)ye}>GD&y=nff!YXU+r@}ez33TCEn`mzIVelqy` zTtJfX07w6KBjXroi2-8$&TjFX(9Uh{F7e%2Tg(z+d?$ZX zdzBMrIt>>T=B1PMK*z9qaMk51F0fcVPTH|8!w9>AGh?ol2Twb;nn}tfcAgzSbOQ8q zn9Nk8M2olT?;r4aO9E`P`7NiNC8JNazteql;(AI~#K|@V;lTHAR9rlc8`uX+`>OerS|Sjkr2{qnB1%IaqB zH73*{fVHZe&_PDDH!-?WMcS?`@FwVgX!ITW5ZD#LfAFG8ItR z=7;IWZ~aP@bD0IWlqo%J#dY)#3JM2#XWV$VqeEI9nNw`dvzEJv+Wypxb}1W5af+73F!XT5J7% zmUIVDmB3q?eYvwnTGA7eeZu3uM8K%ik@lKI$RG9}vXsLX>bsNX~Ev$sB{ zW3BzkJtKXk3%4O^h_-ZOpcm+k}fLYycO7+iMl$2x6o-|l8ad7W#q&!=bM9?N>S|S(hq5Te6wD+u7b^ly>t2zAdTagyb3{+IlvP#3XBDZ%|FlL!U%< zBwf?ZmQT|^r*7O%`^itggMl1fUTd4{n!CHHP@Si@q=vvT?s)InJnntP-=WnB2Ctv0 zViRxDOJfHm>FNvcK0F7G$)#*w79!BPtabG9LfScwTP(&tW=oV5?-Yh5D=Iq<< z`#eL~!^aHNABl}`yV=as)yyvsVt!aoEYemGPu_}h z^L13&dN)v+<;3rgR5x=O)EjyZVfXO!qkGG(UKg{u@?CKp{Q-L(2vT&{+|_&ZE=1Q8 z(l1@3hu7<@1Ge{)NO)ilm<(y&99m$A6CgQJr7S6w50Ia z>!FQycLnSCvvye%^nFi3oz7$FTFqAw*K_tXBvr2iwm#7+RG@xoLGX`rxW@0s6@HG# zU$h?06RLy$&TpIp{<8qQGi0H#Y2{y&a<_Ly^6UV%Ne^jX9&H9%R|Q$ zL8rg3RGIVaMTfUmjk=W0G^vNlYQ2F6EgwVU%6EcOb5>}a%rbgjPV zXknn>NiS|s!xm7qv{kcqkH(sB3^_l1MyvQ(n#%jz`u%3@-2>F2k90=`;E~~X$%VUt zoqI%gd&XRV!0f}i^`oVo4m?#7rTu0fRn}7^124l{kg@)+YuB}0O=Vx@aLFv(QmBj~ zE~Pi5d!&xGi1b@*4FdG(Z^#K#rWSL!slf?QMyO&y?u50_SJfS8WJ7V-tcroa_NIfk zd}8O-VYDx4+IlPaV`d*#Niv7g6cnq6v6ihRv^&FzclxF6fP#2}7bbl9U@cK2Mg{dWClKwPb_RK&-j?< z$qatHp#oJ=lsn2&K18B0`{$=RJ(27FD+fh6&mO}Q>WFy}jk=P&^jFCJt1)6CmZc&P zwxU%eGPCu9I)m7-Sl1Kdu=y_8%5-*~>pVX)s2F{^XECSBDqVzml#-&AvMYLe$f8)> z8TmPDcmiajt*pWyVafOfR3T0B3ACM!mp|S4;|(4et*C!4C?sdbtj)6n%$O zBRQCP?o4ms_y2{>(nELLeZDDLDP3nUo+O8n)^4aFET}`M5EX+i! z13sMQ+Jo?RIBP3_&Lz*b@Q9 z6U5(~`^2kk+QS9!d<<{+pHkwy{k4(l`0NJeZ)~%B7DBO3@ACy3KycxM~x`_@$MbM%vi-OpJRDCZX6zR{;7-g&?oCMyUJVEk}b z&JDflnb}oNiqv#Cc}ci{=e1svnHwFER>8;jRvWK=*V9oaMJR?>e|~!!4b6*fQo)KW zO`lkDyDrHR{%8(|AwGiEx$VUfzK$mr+ka~56y^0K4D%H@R{$-PgP?^d42} z`0L-$yiCyPp~lu}QRN5;_e_Zr64mj={b}i=ag$e$_(&j@F?ORr65^~Q{NnM364(%O zGv`#mSY#~cHL*<#&IHH2N#8q$-984MI7@A{{cHUCt2OkieX>~h?qc?HcQ1>+dsFi1 zzyT&v)pt9cd0V$Sh>^EjOyRb`RUQ zxkc_Guuon5y!Dj%jRtQF>s;~P0;JDh+G`@a9Mr{pbY@ffvG-9lHbpsO)wr#!C!)1% zT;1kvGQZaZ9P&j_=E(g4UTm3*?o5D%v8>TB%Q^(aKz&kSwz5|rxK zeQ*G`js4TTcW!D4HYGWd?E??ub-y$WWmM)EP(@3hIAJ668P%^>kskm{KUF-Tz%3O| zXSO2Fg8ZVqY@Jd@COF)$XIJaS;d0>th?z4!IezTeUs()nVi++(Bg~h;OIKdfv^C=v*lL0JfYqis)+BVQi0PT2=RrIf& z2Xv4P<#w{m(;!9s`I>rp-$kGPMG`5;9{fa-j=XL9a{oQ{Wc?)eEzPF&Zh<9?uUgaU z%_BXzbGO@Y_x2s8o|9uI?tP)H9BgT|Y6kwiO(J4X_wr8kEXBDc|MqoRn7 zQHP(~>Y53_0Dn`Fn4Q?cg?pK#+k5lNMek5A6v*cw=*WKMx`A3Lo$R^m4>}Pdg=5Ii~Y&4kzpo}j2ScM_bZ)$x{|<=Ht9+?ZkU4I zGrDbJiUx{wBEL}*CR?f$Tza*wSnk3kPxAF@3;kfl!1;3m#^NKGwL0_0 z#2@QtPYs;cINYEc{DZ#Zj@SSLPTYP{{=>7x3)R}~+aFZ_X##6N8AAHQs0?#k={7@6 z`KIbs$xq{g`*J;;?Z+u`C}aa>y(M6!@N~=YYt_uli{4HX2sZZ`(YgRuWbCxiuh(&H z37C^@1wk9{wTEk+-u(FrQ5!+tZ# zHNfCb%j~<7PCc@x&2ee}C-|u0?Y;|Y%ww%FZ%ZRhatuG`qjIAqB)n-z>CmcAxh>bI#$AniR{a*)pVo|La@8y3G zJZtuaD`?n-_Gl3?=icY^xoklhPqXtt{ud=6n3ABpg-Ysbe*BZ>7ygj8pb*^IX@N>s zL`293L4CPJkaVaL%;}ro2%8U)Gzhg=KJXBY`yq|O6k0wAuQVGo_$8<92Q2{Ws(!t( zT)p){^QM;c)0Qi*l8#!Y0B&cQO~VCadf7cQ*%ji0B5G;DzU%YW<2NqhGsNN?ZXwauVvB(sl6Zhg!s_gDTFb=&^y66i7W7S++i2Kndv6HA?SBs2%tmI_>iyn-PbyR z(5~*~xH4rFP_@HJg66+^%H?@JS&{Vy$wEr#m;F)qJy@#7eZ0OqFkr9KX1RbEz zAYvCWw_R4HJ9*#Y=xdkWFA{^PPsW$hU!chXys^^!L}tkR=bj03OmW4<;&6yTmcQNe zlC-FrHWhCHj7VYBBX!sy{6URRcA!1ABPzvma9tskeQRJAGS$Jb<_Yio~fIB%mR67kKUH1U<|=PhvChzu^~?4bsxE8DHJv(eJQCV&G0&3D zyF34buxowI=wZZZ%cYg=fwR5iRwGNcU4u(p`O;?cC!YYS8|T*8St&;O#6|J6^6jCm zSP3N`?ZfCOL4b#~VoVw2!BC8jN6yv_6vm%YN1Rh9TH77y)%L!{td|_@vjWSdQjToV z)Tx@&pTQBIazZye`l_29jW%KN-A?9NmUjw_^PkDtiw0s>_$_mC7+oHS zncu%$8Qb#P$zriV6??zGd*S9X--Clf2rY(}Rrro?+g90=G>qa<=v{!2)mb{A+Mt5a zYuFWypOWk1u}~G%unn!Sf+I3{8W{v?Mj-M4^0T2hJ?Ao261MmK{b+CA&+uG=)8y2dIwIpBm3*uTTC=t0JPxXeNk z5Xk1tS@SmyBDs?knrV zmey;wjsw9$?`u~xR)HHuSK5!i2y^;-aXG-)tO=qYyT@nKUuJn--eNJNk zj2Co~tft^Iof425h^Ga!)-gr7Iwj`rm z#$e}1_+mc9cjO`CU_EG+CyjQqU4k~nMY!2}Lb|Dh{oj$`dS3>Mxa^F1sX|g{jpW(F zZh7{off1y(Tn}WatGF|%fJP-{;-i6nJaF9vhxVAEoF&l$z@<2*CSwadhi-9wkG~W5 z$v(z<`C?=H8Ep3kzQeP}Rlhojnue2o13no$hwSmNAMpL&$1a1YV5Mvou<{sPSJf_u z9rpq{)3ZVm-?)Q&=s#@7|$t)WG7kMC;qUJ zodJ~yQ8|GG;tk}DAPfFj4z2(FvvW`{>9_i?kWw)v%C<}ddkq~}_x8CZX!?y^(h#@7X z`yiKW=Dt%Ula5b|4?LDsc3CdAHFSMV?4ehe)G2{e=HM4kEgi9Cl@f98Z|m*xBa>V- z=&la$Qn=1hF2L@0t#&)ipZSV<$HWPmdzv{;<_7(u-84O(EtV91?VWBUZA>m1SnE$1 zZvSAGV(SN^R-!^12xO}=89cxv3T=*Zdb=s>CpX{*gu2doWU$nlX31r#A4%I6<^FVD zT`rrgiY@C6GCb6gi7uzGN?X2nsnF@d?2(X~?tZ3wu4_Z(=+tfGbIkzGTZo=HKfQQA zm{$Clo^9{w!(cdR4TtmHrlohy^~`DeWZ(Pw`b(d1Tf&LINy2m`5@$RBiIYiZZstgi z2VahO3T0&P)E5Llx8>?gf7-`7M0b|7t2i>inP%SI&#Q)AppEv-tQS(($b@v*)Y)YE zV@&9cyGybFIHnleuaVseywfxKP`8o3Qn|CN=&9>^9`yFk09k%@sB!;z{=;5{sI^!iC-*8?rWjUu12JPg;%?|9Sa>z&##99H{!YOO2t8a z-72!0ywZ9>my;z{$!4#z%hCNZIBWM=qlegjvVIZTW zmIV%`@-fWA2Nwg0$Q-hkHU<3Xxj^3B))Y$6fO(5)eYkSAn4#Z{HNciQEo2(xnr2iQ?2dn&rAvV_Pk{lW*LKeBL zwB|!(0+_aS(}YH*BHX5!;v<8sn2Q*u;ueU$P4ro12^6 zfAf7bXdIO$uMLT57)Op_%Xk)8@*uR-K)TXOI>iCd&eGs*T2!O=TV`amow-qfq$Kgi zyb$idn|X+$kv`(c>e{W64EZ)vnSi(5!DfMh#OH-wB873^P=PVow9T|de%Z;{{+Sbx zCY`;HI4A>JkL73)?%IgpfREP%;6EjlN@GF%*D;BAIJ9= zs9P%sMq{p@`8Pe67%YIySH!lU@P{l?3a6pIjCW6?Y9rDon4Z5K8ee&Az7dY9V$M6b zPQFdtwdq;~G4`thi!6}qomRd3_m(p{u+CxoD)i+u=(ff*8XR^1?v3of5uv?5Mi{n* zW;ATI3m3(y?|(gR@_WGIxUQ>QGm!j+^7$W_rGE<=HYSs!haBl<;NV94+5ZQ;yqJvQQ3?2zYvP4-FEGG`?2m1^UNgBIF3pOa!HNpjb_Rr557U+N$0D*fUmmWyuHsEY0>(KxmTK z4C8=azIwmVW_T8VB7c6Z8?~{M-lpz zJ5^>p&NxJ8`0dg&k$su%P#crwQ9aqG{e~0joSn^G>_?~g!dp`~jSNEk4+_P^+D^`Q zi^0}K)KGS3a ziH0q8;c&S(UlG)X2SHndmR$pi!H15vgc}4sQ&>7lnT~aS?CA$h8@&&trK111r+2N} zIc!^cFUbJ*ez=U3+Kp*ikvlh~H#)bZ0p?kBRy~Xi=@EaIx=*Xv6}dx!7p9fN*I5v< zK>1H~DlUQts5-WVE5D3;-7l^63hHMA;NG!!j0#=rH)hs@r+9L9pJg8cST9pPot_~~ z%Ye^h%rCT$Rrzk|lWJ(z`>2PnX%@w8_He+1R~xvnv7} z0cdIIT@M$4{|dSR4mW(5lf@C4)gj7(_3!kAC}yyWiLrB52WaX@>h8NEiN51OM44IsJc<9jwqxxrux9*nS55|2)@%v}&-n%0o|Be;Dqc-=?SdlFE z<$q&8+ILp|HFo#r|HhHYmDNBWZ(Fbw^zc;u;*p=9Z7KD;ZED`zF*c-X!Hvb0^=k|h zQgl&Ya4-DLKlh~>;{o^e@8lyyUZy$+uSy<7bEZXEb(LE-u>OfAzUQwNsuo@%(3Xk& zb$>#dcpBX}6iNCd?L%_^A|q(-8)Jn^N42Z9Chg<|`S)l)2xJa1{yOf_cz;noC&Ul_ zR6n*=u*w-lxnh$R(P+IU(=IGgqH- zA)q%>5!#h>aZuo?s>Y@#j=XnRx5ajMnvE0eti_QLLdM7Q@&t9ROH)o7iS{Hz+d9Qo zTMnBUP$yi}eFv$4{hn97&a`6~W#MbIa*09sL*}jYc9c@UQOQmowtCHYy{xkMfG&xn zty1SPr1qDb&;qoKE*L>wN;0{f3G{pHO%40xDPbW0Ou-fG?@PupqbX z==P@?MQq-MV(O?~>Ddxjo<`uo9fBq!zO$?sx>WO~A{)5dzI+WfunRihs+UkgI^X}S zeOj!XzwI_@t&nF)zbgKl@sen`Pq)D-?YRG)prDJygg}v ztwN)V`W({mE)XvKwkk|_Smf!pApCDkg6W|D+t2tyBy6VmuS0b(D;)dzUj3zF$%0U0 zSMT6vbk26P&88c&eOl0JVncdtEwwVcgsBk;%l$h*tgTgNdn}NcTQs8UXS>4Q=VflX z2>WbI*5YIR`ap%oOzxc4e(#WJ>*h-)9hPzZOs`I&1>R;=_Xc(l$ig0%pzCpCku!cw z{8nLGX0`+gV63|^A|!qe__H72jfv_@*QFx+S`053vKb12r?`vp-T8ObbW znwxraGP+Gf!D90VRb01KIV82`1=qX6wZOnlOZ$lo;QNvo?txy8mn?pQvm_`Y?tA#* zVY3UzOrvy6fcwo+NN>?5Z8I-QY3=>K!xi-cM^{S`LsOeBjoUCb1u~dLCV}c zycEXvnzp>YoI=!!sGI$ny<{r2;vnQFV@aH=x_)^{$?S;?+XZ!H7d)Hit{u zPMK|Y(+B`nPT-c@hYd+`gkpP9`EeyPM3SH0xOFqw-X;VnjIpA(>5%C3e z$!+^gdW^(u2kgIF5}?5PR*7_W(#=db{!ZxV)>epkj}93U7(}B-20`Y`aYM>@ad0+`m+;p*N}zcIlau&}K!g36MD{wxO7yC7EQOeIQh4_x6yPu;mb z2+dr=*tW+ag9(MG?3}{jY*D=JbMF^>YOH#Gqeut)Z_L1&R*8&oYqhUD6Q0Wo=sh z?7t-e?&VLeO3VrBJ?L1YT|YHH&NW3_)Z&Ejq*xP?5EN|T>jW_--r%x;9dGW)NSrmQi-j!Ta{XHTm^s0IVW$^P{(;XFCGh`bF$um__=uy11rOZ+e(^EFw{)-9 zLc^e~q+HeKgk3h_NW(894j?`r{3c@{?uMS0zWt-VWgVr&-4Os-#+memgu2uu-nXzd zA}LDW7~nGplF_jGN1MFcyP(~~@QhY(r1eA>GLpWt;uN^bEbn8CkaKl9ys_Z?9i4Y~ z?PloA>1%w^^rr_x=68^^`1dA}ci9r=>l2ddn##@V^@mNUMt*OAU(UrbLwx=~ei{=I zoo)S)&FD4T@*tk)TFf!moUCnPFQsprxMR=kQuRf?C$EidIA}BF?2!eb%TWDSEBT|T z_nc1Y@Vk)y-1nFd$@vaHg7b}EiQqAqAr#6CMzgRzx}55s``wD1XNp(YO!Vj1aJ~ld zM^mUbDp8hluX#g;aic=ih@vR3CbKlHDF3M|1``k2K>CEL4upkpjF-yW5Q%pLDNwPB zhu_cOBc4LgEJs*b8OZq0GxgtxWVD?RoDrc(+QX@yJt5yEBs{1ZRSYOwg5d-q0eSB>GwS00sj-jJG zaoP|`2yv?FUu()>Oj_yBUyHKMTIBymYegFp zVt$ZA35?CQ8a>jZMjGXIP~`U!0wTQ+Xs*k0y$`Qy^PlT#88FG}e-3BQPd-}O$q}?a z@;%CPw0dM}L$8kd6;)oc!lK#f3hfKMiv28FsrGLYj4YL(Dxb~R**fs)8FA1K+e~y5 zny)r1ww6=Ws+=FFpGm7|=1cB5`Ud}cwpL^&vMSTT%*#xu`Qd!N*~HKFaaGNYeF_Go zBkM7frqm=?us7t#0@Nqt4SCMwjMJRbyG%lj*fwgf=FP&&3N44t_+ZP&KrdaSMkzSn ztbqPk-)Amc$R9=cl3{m#U&JfaK>c^!d& zkc@exw@&nAgz161U|x+rzJi$y__ieDmvn&2xctm_a{vzOO}LaW|5rNX$Ed){`%7x= zIM4swM;kuUzRi zAJg)P6y(D+Kk5r^3U^|cnW8z3rx{n2|JhSGlf?OBD5wbVUg>Xn0>tE^lp+b3tGLBbmQUc=B%_oWTS^MTaswXIH(J$BI*@>GWrI`Jt&F0NO1KJ#@! zjCB0Dxq5KVw|10B(uV0-i3cn4>({JBF?_1)*_B({mhFAC zLoKC0+jT)=FSqs2fc^W>dDakubGr`i8g188roR2fD+e*>@%L1*$jRFo8{6!>DM?oc zdrGu~4M^y>6Ew#SQNgyYh{sEB;(BCCxIo9150XCZb5ky z6h_%^?T;$7frGw(v%ZO{Q$ZyxY*qgf7`@OqbkH{iQ~v&OI}AOlCSR+sh1kfgwaLJj zJb-?;W2i8Ixf0{m>sItWME0?&H)W~n3EcCzt=t&ZC#v=9E!Je$Y#FNX>XAL+IhR=e z@n*zVswGw$!7Rm3qKgmK((jF;;^~3@%h)cVWpHr7*B%xJhMiBr990La@7;bQKephI zjQr){6$RyH@+DxUSDih($bboCjNkI=2+@cIcMG^7rlY-6pLSs~H~!;T8PNQNVUSGI zDlQJOn5hIRxGbt%pQ*j1ZD2Z=DW=-n*Rv)Tr!z4dtP{XWa>85KkM@u;j1P8cwBFOH ztyM33r~nHUws@LbjY>w&vtD=I?sLN9hygjOkr0293r@>Nk>pfq$2p#wdwO*ZyDuO+gP8J6*)M4Nqfw~ta^z*TG-4UGM3Zr-?kV7fhWrG{k*&Uq_X1$cppAR-O+ zHxl4S`maH$^*J3G%BEiwGP&!*`lwuk0(=+pOP@<-kwk;mGd(hhHR>NEpop^elQKDK ztDkg2ebeyPGh0ax1{9~jE`JEsFjE{S(>}B!t(qXbRoOPXbaL)+nNt9VH^^1~PiB%9 z-{W>tLHVMB5Feze87is!8}?#0*Uq??pR?YcOEv)%ZjBjqI=>esdXkzepfjB`Ain#9 zAGL=b3Zl(SjCk3MxWbxQ)-K?9`tv-R(1I60u_sF^)f0znXO@oU2E7$j{chqiSs6^NyeEVQ1Hth z%%RRqK%Ki0{GKCco`{mYecxDgSJ2E4ZZk9Z8K_;Hgfei}!i^uo7vlJPo;DSvX|2^c zJ@-D`8gfEiS4bH_fm-<$2TT~piqBXL%USEHANeU2BwkZ%Duc${Km$BMd3|B!<)a$y z;$&RH$BhoCevKYjg!+fy64_LfRFUSO9KvJZPyN&*!zbo^y*Rz9<+s_Xm`U3Bg-H4Z z=0SXD6Z>{tYML@VOp|-e&Ak&~^s>%9Po_?MOq21K9Y^#-eV7bmEsK92ekI;&*e~;6 z^EL`QXRlPpezAe^WrTPfg-yL&tI5}zRD11Lv{-MrZZzfk8>&CY3XAN0CTSzNS!Vc` znJLVhx4>XI(6M_$-Q@=%ZnN9muUy_S1? z#~=jV_dU}5S{$kr3)k{^Exz00;%Ru%8_-|J0-&>oGL$pnD&|s7!X{}UZWERMY>Jt0 zW{0&)s!7CNtoQRO)&)H0;KNZRMqHsSIp;J4wFj|KNEvOTyTu+v?~rP$`QC_F*h+gK zi1}=cwQvGRby=(*uPsBzx{b+Rto=4Ce*CW+2|IPnQ@e3uZ=}mWf;kbl7Qk?)Wqhe) zbGM2;GC!D|pz+jmST`_G2R(O_f6vWF)BnS4NGo?px&xsomi)6^v4JDOc;kLV(`IL`R z*y`LgfEg{mM7K!HRT2|@gfpi~LFjE4%?-HrMk$f+SUCMpQu;@d!q=*YMoOA@L0X{! zhPK0yvgj^xh)2ejo;?V*Kl?$+12~Fv=0jKZJd;3W>4Z``KU9i%&RmEb9d~JnA2G`` z2%hVF#G?gEyXNUtZjbk1p}6b)oK=oy)O1T%RXs$JDg_J7?J?hl!f)G>PuNTpk8F%b z56GizAI3Dxslua5yoQ&8R&WbRR;9&lj)Ez=xM8g&|N4NL%?>52|KFYeH)rPtJw2h) zLyCUK(HHcoHMmUd{l3oH*0xqz@JtlkaWM+$D@B16#7>g$Y|}&m5O3h<`dfesc5hDI z-e8#=3sH3S_MQC^aHY`k|prrWkxRp2#KO=?Tda@7VSgKHRFSA)aY2Gum{Xawl-=ikJ>` zBRD?ZuGsbol)b-CLWg-7ye>1nB0PRUcltq7hE<$ewUKgW6}v&_rUT#qRj4f?>-EgL2#?-tUQ@~0LLo7Poc#&T;*uV@sBSgk`eWlF<5xsgHGyQj;idUbgxrf0fv-c) zW?x8;+ityqOk9raqv;xoSyRKE;rQ|>EeK;CfF!^oS>NE$j)IW>$LCxk#<8P{u5mF3 z_c1=fsZx7_!_RF^#AT(=nY$G}LPgH-Aey{r<;i#(8B;DwVmE3zd~MnrMr=Ff&eBV@YMHx#3b-iTkby zHI}3(ROV7HOj$YQf@Y%TLWw}GWbR8YAflov-~#vrzRzCY_xrc+Kk&UipC3dnuP0oW z_kEvpKhEQv^Ekg%^Qe7T&TyoCp23#30WG~ky&_fBh7`Vrd5Ls!Tg)a%*VS|IZIyQ% z8&p24{j3+OO1wigg`bsc{FCvu3s{cd^Mq`fLJz_ow~53816`zZNjsf0Y+eiSB>TRb zR!;%L)0~z|BR?4`A<&q<3y7G{cT~qqajvQs#t(@Z&2wz2TUlpv{D)yi(1v#VVX$OH z-^9?=OXC4gU21K@iq?6$=rsgFQ(Fb+pFAS%2sk)MT^eHvLIpNr;)^(k@DA@esf?d_64ZP`<2LP#>W_S2GoS9>nzMeKKd67Y z$S4Y1cDtB3A-(~zwW;cO20JVF{_$F6_(O7f%~94#ti z?4p0Tm^$GB_Nl251~zHyCsYrx{lLUokYlGxsz<;la>DZw==sTyyH&f7q%(*RJ?V$1 z6Kiu2cVXHwCLmebHHym|oqbr#{a-DBp|u8Zz*%UT|D*4!cmHjP{r`GXfiyZFin3cg z9*RoxM=N#nvrH2n-8~pw+nHeAQg8j!_!*tRD!%o6fs@fKpeQ)715-pxQO%9=%)u9Y zC#XAAb^bo*dmMW_zHaDWf@{h#W1c;2TmQbu3vC|r;Tpmj%w+8TZ4?vBA&tSOsp-4g z4;ZQ~Mwp}X3t8m2_ukC56bIG=^6wP+gN}I|VR_X_TY~1tMpGgdYxJ7=cYDl47@da(4W$x`_v#IqcKCfraeW04ThBr= zxer3LdvbC<-Y#I`EF*h`R(e~V9a%jO&NU|PQX86+M74f5)>SJzZBz9oVk(-`4}RK( zf^b?$+E|U7V(2L8yFdX!&y^YFTx==K_+ZMm*qVk>n;$WTzusW?AHlyKdTXg|TGyCH zzm8=K8FzX}?4z+_S0=H~^K6I?p|C3oE9N@g(7_&uw@Uw0cZ6^u-(78O@2+Zyf9;U| zyCXy`^=0$d{;G$2&3BW|*Q({XaO1?0M?yOyAMH;+slWNxL0E6 zZ`z~uZ*jDZuiR5fi!oszYPOkQ)iIvZWlG92yDVg@uUQMjNSmlRM?G?lLcbVAPV6j_ zKR%WVemVE^9-{(rU(!5b$AeK+cnSW%X8ry`EU>~|{QM>gb%f4CcNsNulxyJ6WK z_nI51!nd)~FQ5mi;+d>XF#e#Wl{lqC`Z0(WBTfk_4h7wN;pFoSvPb1~Nm;xvj1&?y zemizv6|xB{F8+!JuL>jS{Xj>3t9CzLuIdKy=eOt5ypR9#tRj9-ymzv*osEjyTFZD~%`QCH0$>fAol>@dSR z2p#Ht5;&FR3OKfajUfifD__^gK?QfG1fzgQFWWqV?ruD`+V84Z+vAaFT_PkCX&}F} z(s9jh^1ng&^4`XGAYIYBv9zLS_@6)9H7UU$d$j4VN6Ms0aY??t4D(J_UDX7$?0)z} zGKncVnq}U&^4iGoryG_ts(q7%FNBLN^>@RywwoBQcKYz~NYkGuKk6DGMu33Nq2Y(R zZ_YaAr$i+gV@>HMn}`Kay8!H{%BCKW3%%cOTpLqqw?lK`P~FzQ>33c%>^cAHPOM>r zI@K|t<+81_!G#piF&wpz(YK7V&-!&?o6V;%yXPMJ#Wz$S+99S9Xd7c)OUC=f{WMiI z-Hpsslbg1N-gjya*Lf|5Fh0svs=gYB1~sgALEtOe?mn8q`-G_%38?V!#mkD55XV4w z%|IK$Q$odOJ@ZfcaeXy9LF-`}$9OQ6JXRf)e2c7>$pfQvp;eRO`qD9ZFn;~{oIEH| zxd_h%37_H3olU1+u|fP|HGZgM2^ygbPPy*CNNQV=2d^1^$qncf@fXIDoUKwFs3x8M zU~Ll)wuBJ2*5EeQK-tu!nJYmNjGmH!jo;$_3^(h1nU!K|@Aq-4!~VkJNxLe)v@FPjn085s9-ou2<1AT!jV}Em;l@_Im>PIs8}bTm z(Lyyg-e4<7xXa1FLNy*^r@h&7Z|r6>+ewYAQQ~=36TRT)Q_q&q3NwtHANkr2bgjV;AEJsVP`zvB=#jRd(Zfd5 z#_+B1$bC1&nhiOp-)n49r|ymY+vQ^H`9p+W!&hIn+X}8ew5+kGW_j3AAifLt6${lN zBOkVuH{*Rx@W(PWUAw4h6Z)x9*Gl96NI7)QV=V#jX+q`?^I@N(u5l5P5Zcfg%oS zytcgjtqquNHWTq9o{HGe!K45sh*grvAf}y@bdXIzCT#6m4}1S{4u9x98TVhtoV)%G zM74>^re`#z2s;NzukWShJXypzQq%h_1`mWqOj_`UlMoZ{1(AR;EvSA6OG#tkz7)XkK z$0fst+tz8FHJuxfC7(}6r8kjs#nQ%;kesa>=dt0D530M7K4#V;1zSqL91x}Gyk=^F zaoe@EHU=%~)edaj!LqexWqT}amL3W`#*6^fTo@x7BBP-YSK=L4v-&O=zCbh$_zivd zY*iIGjGLeQ6f{xQFMXX-omsEUiNc19c)-vH@MqE~GJg?aY5jTg?#ARhNIeTbFbrp2 zFGy}Bi(M^KEl0wvm-pVN{~UnU-2G$FS&K3mL=aB zRi5TW&b)y>zE7c{LiYaNJtV^JL!Os&mlwviw%ER_(A2yPhk!@lrgg+2)xdOQN{#}bVy z3J{aY-35qs1~ByLWTPBxmdhe1Z_X+38$MJGib;jBa!3enWOD)8-meqN*f-#iI=vSm zL^F5%Qz#<-=TQvf_crzC3#XiJG-_%^?;{vEn(Tr=EQWM1scz1Pt2z@$jL9lUZ*6Hv zb$r}Do9$vQYty)b{>-n~3>Hs`JlgHus+%8@p%Wpw|N9MEY{KUu#-CM>GgAT&A+Vyo zfl>ol?Q-m07|bE*o@Lxplf@YV}917kyz|6xl~dUY7St^So~ zp{=8ELHNkQ;{bE6xH}c%l?-qlmv?!o6^2~ET1R0wAUc1;_^K$-FJ=%N znC`G);D(q_rJpPdc?7{|19mxMPYkmD^!v86Ao^}<8zs|a)-l+)=*OAcFEa|e?mSb2 z3Wrs-4OLakGd<=vTXNd`6^FFVj$<)~voRVrT`&~2gQVWJ*kxUJQ)Qoa*UxzAYeI47 zDYcMy8i;FmW3M4BM^%tSma3aw4B#m3dMITGK;{EOJwr?Qv_<1t=Rxg7iR(a-O$MH% z4&D7D(g54;CswEi*^hL^ga7_l{l3E~zyF(rzGz$`T~_DSCQlwt*-l6u;l53zeeAsKK2(5+t+W{jW-c#P01vV4(7;LIu}tL}1wnLg z4YY^ShF%K-?R5;49B`kLT`aVlzxDSDaBB)MmE3siI;9GX$&-}Vy+NltTxHRuG*bXV zc}yHm5&x~F>Wm8UIW zVt0z-Db5fl?gA|ce?cy^`nJr;b@dHID_VEb2Ew{qMTE?hqDo8pk zqD{j9Qt42;!F<)Ld6x3@i$9?M$>Zz{G2irq*RzqB+v=Tn(=_SG-5$b#fh{+Gnm+L) zVy;#bpV*m2hWG-$d7o++NMla_^DcLquE(EHr=j8D`#?6LSwgs3&`&^iwnOZ&%~yGB zGgw89WgUj`@iT&e8czT+T@~$NWmkDY$CgGU9bpO%bnh`23=l9N9pv{Ol&#^`mfzh< zLzTjaAG91>xFlGC%rk;{5jaoUs1tT<-FQr<)CHvDYnUvQ+}d4n>-M0Ktql@?aBEDL zD?mRk=r3Zy_|X8FNfMZX5eoO-xo&Q5wRmDeF~P?n6l>z8K?-hTHOTiCf3@$#-wT5i zA%<%k?8VhbfpmxFRTfq$&&4Tg*rOAW24&%e^(bXT+1C!K@u`Ib0ei~Uca#Ge%=1)I z4b6PL8k7FyhRGKswL%7^e|^V+jq@bHynS$9M+cG7U7n50EAi1!0#;5KD;DSu>(8`N zu=vSs!peF2^Wah)?d>I@$riE|5pS0D(PjEUHcHKghpKXsMvP}deSt{-n|h@lFlTa}2}0Q<>Nxg_Su9VULWC(; z+ps3w32AL0k1UXk@^NG(u3IpH1r}N0i(Fiez@oZohC9oh$4m^4wR#Ht2T<#_3j9=U z?1AzvgCk(ZH0B3Xa9fN#Uwvjy;)Q2klP9&qNS}P9t7*GGrT?cmCJAc+4s$z^TI?fb z@QZbj_=m_+3Rp$;rQ`lHL+&iVVcab_uuAWEb&oxf*SgcaL&pu^sSj>Me*RRvgE5?t?yH(z;nDmHN_FsR^l$ercom)_!X2I^ z*=$ENopXoKRj4~;k77^D*~37 zP$x0Yx61^#!MPxciyD%KL5&ynHp0lt#m1vL8^YQy23GM^E=xiT(&VzJjosp#>ts1F z%BDFrDTdgz4Ljv~$Stp9SBxIW)|2Fd10UoCN|T8Mp!#2-(b)VKV^4u%z?yNhSKM}P zNue{|PNo1_&h`ksXLgDKVRcpj=jv83XIq`t`xLf2yh)NxCN~NBn#lGGdSrFv;`tIY z7qv#MVj%tv{%ei_4HNm4o>Ast5SK2UPG(DgQFsNv%Oo;S!ZTN;uGTw0g{%~>VY;L1gg%la7TrUt!YH~ za^>iX|8|RLOL#J5@+uE+q)}U*_slSgk!9TyfFO|NrV6H;Pi7nswh5{ax>@kT%VSt6 z{r`2+aIxG$gE!@2JPYn3G$Mw%ZM6YCAs7w^I=?edkT)M3!W%%@^!hxQ97OP-6-M8^ zw>S4R1v+sUVfY1JLT15E!EXX4h$M`Vd*qcZ&yD?6BL28Txd-UioVfj@W{|`!6Y@u; z>(U7l4csycVjU-+09B{uVKn80?-JmQ!o4HjlCSUK<8WW1VFH-Nx2`i76PyL3T)r}& z8fB%3pKd#hNU|0VhBduD;#58t>@5h>-EX?q|B6=RRY~8eddz$6)})DLqxCTNaA@8^ zGrR4Y%`$gT2z5U;IWZz~j%xihYAAXQ)Bj!sSr^(I@Lo&+gp4Z)Vhm6M)U~{_!rIJ` zNnNSr%*WN;+wHm|XZM>XMm;1QLEGNB;xe`n?yisppAEe3wSJlPNdFNn}G@10{HpD_@E0aBQ;A{d0O%(-~hSj~g zf6XXbuYjZH9uaZLS=a;)5Q6C+p(RROq$}WrIt4Qu(P^DdLisia1S6&H;}0#xlZD;+ zKsL%(U&uX>Inn%Z5D->p>&=*-%h%REj5Od?vvx7xE4ip-f%Rl7s>^(CwU?^}aVIbL zx@*Kz*EGr2^;5@-<}<_Yp6xNN;ib9P!Vib>{P^z1Mg2N1iUuKNqqwnJB6TD7IONhI zDDlkC*3*w{+DVN37&BS_x z_o7@jnz$G(7gMHW4dXDZ7ndd7!zV{w{#H_sDBp!xR9OA-s^A3Hex93ha`Is|DkGCJ zq!pG&;=aH)P#Nn#?I{XJN~K9I9?1)fh}d}Z&CQmU1!odCJO&#HNzo{d*OgAs=R%d{ zUHZ+EVGe{6A1}P`C2cx=xH%$yuY|#b;E&kwbevlVG)(MU5#0oi6<+0q8FJh^Y<-2Y zh2?Y#HhFF4Czh>Ygn@E_QALTlY=;9J9Yk-lM7Id)R#?T|bD7fJ!HSJrCezK2T>kby zbVJM1OS4mFaCbf}>&=!M4@5(fH1GU24P&faB;l51NovOn70cmirO-_Z(O{w<<4Hv} z+qZ0t`pj^Z`Q`V1-RVc>jnVhXP11jK^3A?IMs+3tm2L{rR8OWq&z<{KqUTye@TqrN zx6TP*O!gShBRQyz{*F!gpBTFiHfWZ34%S`H?Nu;O4m~oT{-EueEHiPl5X}WI%(~g4KEspQ&4!*L1AiE@d#--EQ6O32*4fz`G?3AU#rInKW@2>l0i_ z`X>3WB~E;YH8`3wStbhv=gqBsqm}nEzo65T6!dPyKjFwhT9AR#I;%mb6E`l)f`%28 zc2j+E(npu^UnO%9Z(p)tZ03046)3VsEr$8Ycq*)ERD4NY%I!54R)-HhZQf zs^YM*^^A1H*X5}lj&{5&Mg#h_qU4Rf%{wo;SI)%d#j2_wR|Z6$u@m z)ml!QmoAl_lvKXe$Ql3rnYH|&8x-Z0TwZ==*L>&A9Zw$zNIHC~hWsZfuVSNhJ>f@Q zUk)_BS;gqrL8&?5yg8geqs-U3A$qt7X7BGi$li${p0Zd0{L28$v?-PvdCMJ3ok`@S zx@Sg50RF8L03nSGc{N0p(T}gMrx(Jo$zSSbrSG=jSWrE)dSPz$2xC}jvUU^4D;f2B zkWD^T%_|U~ zCf6%%-IjvnaRYMFqAy7~^B;bKfHdDTGO!vn_;q9o?u(n_*eO@InzWhwTss3=-itg5 z-BTWIlZoh*)l9YQ_k<5cay+?j-HR2!uJYoiej93~AFP?mDdvHrQJwUg65SdjrT5`J z#N1PN&mnw6gpP#@Pk7Z-!P;<0|7qi?Y}G&mKKsTh(^qx_C5qkpfi;0{d|JTeC%c7=2C<2vG(64H z39OY~MT*V)(zF!q$e=L&RXz4i^~yIPJRMaP z#)-je3gu!SEEnE;t>rXeyDHW)>CU5TO#pheo^A;87;}FZ!+V1slkw6x;J$sl3EaD2 zK_Ju#1tLTC>o4wRxfiZrgw8oHa>e?MK`8O9Q-r+c zplz5>)GgFT2_SvfmSU(BWgh7q@Cv5o^$JKs)4h_W@VZeMY`YNy< z!CTK-+NRYuHxk3J-b_*nA3?3;Y;o6bqFT$N6_<%-?3~Aa&`{tuNiVR{)?y8bQzM z1$sxrBGDKgBu12;)!|axqvsSNCL!%_s?`MOai1C+V`Wz!`55|)cGS*a@QQFOe2#Je zGOKLo3B|#T!oR@7=760pVoVIxL~^~P{$vKM#Iu_VS?ZOj_SFM3OQu*Gnz}*^y4xi& z4T|gjdvx$+uYg@_ty~v}(G-h=%IYO07*Ns_n32`mC|1=UsF$nIQF(=Ox{O5zHkuUl z^D6avG~M+bt1O$o;&IOvQ}F8`CBj}dXMGQBBFB(snanGpzg6V)Ry)?H_gaw5igZzd zrf#&|?x;r2?0oTPDb%qbA{CI{x(m&#E(k0jwtA%}KoydtDPVl*OVF zzsMDv0jzi+URUdd51~_s^wF5(!nl1_4V85=?9n@OH|4CM)ZOPS zAiPz2fL4IEZql8so+^Dc5+Dvv^LS1ZX|aq?VRh$L8F08|d3pV3CUni*z{cIc&0@qA zOO*Tpe7Ept@sk#3lFgI?RYj+Kab6iQYf} zps3*3LbO-S-BN~eD0rd|121;3FsN)gW$`&UQ+&j=DKDFrw;g^GmmLn?$C>K1=5uK7 zudoIzP$PYpR(H9jQIBTZ(&(-@5z41Ia5sR7qOBuf+$dXRNh8NsgB&PehSs_%>QV!9 zkw205AZ3A%I#yM)A+06jDzrA_%C)B13*I=d+L+95Wl0YNq{KHjn=0_-HHE%P!Nj5r zveYPtQ6fG^w1P`dZrWgj2lPx#bqv5@Y13AXx07+@2a<%KO zm-i-LC?_tdHYx02yJ7NB76LI|JwTQehILb@t3aSpP$4K@Ea4)<3V7uJIukXXC@(Dv ze|Oj6tWC7;nLvV**J6NMQnGayZNM#LMCMrdKIfB>kiZ}r6+IjONLfNs-#Bk&U2oxf%^?Sdb|Z>n0E|GwdwZ`q+wm#R z7Monxf3e&AtN)-*Fs9O0F&k;CkZQ&b346JkMpP`Y@Mqb%jGR{5s^MhNZ;%JF;KS)x zjBC#LRr9PYFd^z?oAUgd%LFZ}8Q{e|aDDk82@;0eCB|3y}0wjy(jiPtdj z3~ky#lUQyzbctM0IUtjqIl+LUY*yJEnA5^x8a(L_1dqMRxJ)8J%WmD%IcVo)Ba7og zg88)Ms98x}uAIhJf?N-O0xKV;&}x@nZpec`m>YZr7rhAWC_B8~pg4+*gonug<6Txa zfR>dbQWM{iV-!_2ibc-@d{YHbj0WoRipQ*HdA4~lG7fd4u7NhHPOk%yU7MlV!95_+Mi)e z@1)EKb5OV@%*KiTfA*et6q4zV-r^yFiW3?f!j!$ex6WmRWi2^NdEHjsf5>F<+IBR zaBNC((*GD3tg+h>2yl@yDuDUZlT-?@t{9A)qMmgoaDB+nxiw9n&AnN6gG@x*}bqcM!f1*Y}<1#&mW!18Q7(<^fOzGDVq3<_Dvw4CK zu861%1u#LvYQ8{LQWSFpo2cv}(Z&X70eZM*e_LkT1t%^E)p;0~S6)+2n-g~2a~fLd zqSY~N`?wK*=%nC}<07}%ea$f&&ue|El$2(Zb)%|;{H*+nkcDAk&oGgSMC9%`zmp)u$X(G;!rfVVrx`WWIK@w?qx>YO1_zvByB7 zS|cVLG_HUl?rXDHFY6W8D^lJRB~|pc%}464Y-by8>efi4zDWM8;G4jjdz;5*eg{2> z&}v*#V?e@)I0H;XUs{P)?aXCTyuJUW3G=TXX`({TV-sVn#m|N|ydX_ijP6JzD7bW= z%%-k6t$X0&{abNQjq&9Y$)J$Tz}XVb+aXI<-`^A}yOTj%ja4kM$j5-SRsKrjlwd5P zC{BSLk>b)dPf0<#0g59Uv;LFN2eN*Wjnxfh;y2K;!`6YQ3?+32Iw&_(4x~*Vhk3CG z;>Xy5Rn)il%B;O`s zsZNpoKKyaYO8lGO?b|BOxnV7BI0SWfFm6xB<-ICP*5$ZirIxWF`(RKnz|*TP01UfL zLjOHR0hWgtg$%aS8x^E9bNw;d;mW#EzPGSu`4L)xEhY?#$lsK}0wC)@S-T6D$ld0= zEPjY)6cHUmfvL_`ymY&i?)NE#bwkT(_vWULCpM=D{B6mS*v+($0~#>_pUw=NxuN

    mwYtL8CqU;SMl1Xjr9m(h;>w5eK^x)f%;z$o8ST^ z$Fbye;*L|sj9zzzIq!`_ z4<*%{pw)~0M@Nr2Cq$Lylwk{|DmG{y!Y-P%K4Xy5U4ma~s;WPf9a!eC1*KV*#dHq{ zkG~3EK?;W?1a-tl_*V-QFtg$-9hBnF^qn_|5Z`McDvx795QSWSs|0C4;!-!cmwOYT z2t}!^iEQt~8;`hnH-33{W}A`CI`oQQmo?j@Lya0X0Ikn7=Ig z_iVyOX)Cpx`ghC(BG)LmInztj<|Jxl`tGOBZx^;R;k&_cjHrV5q?rjzT1~%{+qVmt>4?h@P_&xmB=}Tcp zhq=crM7ulBlXTThy)33$G&SAogcKo$Gkp38R#$e;y0EG6cpn&v`Je&W&rWdp%EIt` zFg>^o9St;j?3yj3;x)wH)fen=6lH57?EKj!4#B{% zUDZH*eC71^81d-oW;Ewa%w?xVaD2S2asTaHjatu^-LH%gO?8)HRM~R%wZYgisphE& z9T=(^80s%|huKS~vucatIcZznE4=G%&}5KCWl7CGhmaiBhJwtz>&$DtYC|0?fhb_* z&M_XBglJw{Qp#yK0H*|hw|z(;grRkuDm<@`eGb#e_na296CrCo_OVfDI!4g)(W(j} zt<``>L>0BFCkM=E+FgwlkrZVoHY#7A>7F)>Ju2`ofr^NM`P<_-o!p<9+k}kn*r8BU zT|1@aotgH=h3^3uh`>>}HNnzUbKL}PZDYxL(-;(|GYE|enbtK}?W!TVP3(5+!pF)q zxYiXk4aLAoM)&Hs(|Lop1KtJNan#BVs{;9fnbN+vW^o}4EMQ*pg$gdx{%v#&9jg;Q z$+zNvLH}!IrY5gK!li$XmscfUpS7w$(XjsLS~##w_kv=UKL(BClybHjIWej8)| z`nQnzf)B+9O2+|o(W0?^FuT`2R-hE|8%>jA<0G?{c{^<8WI)N6t&b%1PZg-~bo>PK zx)=9NNS-k+Z(+avks|YfMu~g=7_+#h?EuKO>(5DpJFZCQc!ew!25_({s|)TDa_xcv zAd_4)c~eObEnoyseO*%EHH)-0)VzQIj*cUTbtWuh1>@)-b4%u#P%Rs?QQwlCZ?6~D z?f-DTEMRBsuM2l!J|_H7A>AP`@t;A;_`#Cq!pR>S(+;~8U?LWeC&m=$ApRhFGund* z2x!47!zm(sXjSM=$hlk1)S;hB?`IoB*4vjD$Tck(_CyXU2@KTySW()zF-DH02%g+efMD3^?Ole)A2o4+95OR)b=cPpwVaOIuRl?N(7riPXhoARhT)dKPl+GM8z>OSdg&IUXnbM{Tm;{}=v3SUr$Sak z0v9(F41x|qHv+1mWO1*3>H1!BT-7t4$S;fw1?Jsp?^nMyEd9N5u`O?o?lv$SzwR!@^o6$zj4=k@1X?HB2m-4ePQ zvPjp%TcP;1G6@shs7#b^4ab_t56&=AnnPJg5(-{$ z0!hAPQjh?UEJLvoB`hfJ#66RoS@8iAV?Fraq;YFb&%yrfhA62CP6+ASB>Alkzn^jZ z_9nNUbHAwsKN46yI9MMa;sv)Ue1QfeL$hd^ITtz~@n}x?@#vb&d}X*=ef54mC3oi( zfxW31xRn#+N~XRaRG70ZcIsH#J3kBzQan-IsQAjPd*$G+YIkdT!OcXM@& zYNfs`wodNImV&xhQ=h|tObh)wv}oulPM6n;ILp!%?$arOgSoY)?2L&IHxO37JAL#58Gx8aA5IuZU3mzf(`Q zIe{I$AQ}qq46IV&g^zttR)2@N5!nA8xnoOoF4N}Qu-NeZ1g`cZ1F)&c0;6u$(x5EZ z^mZ6_riDR*`RooJ_Tut-?h(itB&V-+@luXcF3bVG;CsmDy^Zf~n`xF^F^icPTNcQI zor5-nPjUTmpu%n<4)b2oC5)Ua>}ftBTWXT8jp{H7FZE~u4+c%*W?I2_!LrL3A-)HP zXMKIG!^`MmO0odg;v|`6R9+eN>R5k!7m=@h3U-EiuR90P8RwT)^DrTi(s;oqX-k4b<{6T zk4z!RgQEm3L9V2yVCZ1mBg$oiGAJ;JnU`+BX2fA!P4DoTz++}Dx$3wmrm^baZu-F3 zP4UM)JeK>{h5Z%f>pvij5^whf{&n)EiD{7hmk?E5Q!f=wlv)ACUod)ho8Ie}+@8U7 z=lSZM(H|vr?G6-e#KnT~uhlr`tw1yRxq9(P>( z&qUeds`@`Q&->qR&{1rUZ8f!OoI&N6$IF-9lgl5HNjFNH8Uv)IwbsoNy<+=_{VpNz z>jGK}V}NpdqpOjhswprcQribRbx+3(vsO(`Mx(h|O}Q}A#Pi1G9w%tiP+At)Yo7o1VS-WRas~&)_T($j!=64#bg9FZ;oc_SM1$b zW&Jmmr#KeYDmEPkyBE9{1f<2M!>j%%TvtAGO7XsFPt;dk=b2ivE~>%2nK_l>OhT>Y ze-b)>$#-XAd~{)1SR3Di@_y9)~IbK0yBPb-{rfS)1eWS z$uq(J>l&uuqPT=MomW&F)GAsu*@p`5@I4F6{0?}cl7Z6ZfJ>TQF&Z%<0cC7qwNyP! z7p(r=H80=#04$2piptSI;J_rr#~Wu92M6x_g%pkcxCy=0SA)!aIrHRp#)w=s5DwC)E32D#%D42We*{Mxz=6RBTZT| z;aVMpjZ3DP{k&XabpYh$uVm7{1&-ogFS0ZsXoy*>t6q(#&dq$~I89Tn^&T2sUAY@U z?Edi3*?`KmD}Kg)Fqh}wTPY@Ou{m2L9sZvpsc>}=z>7yJQ8-piPjaB;sQOi!QvZ73 zS+GFr>mYPP7VN7((;~W1j(~Z;myGu!eKr``#ueUB*n_h5mD6mBdeXKmkzL}-o&nO{lu7nCYaS3 z2!U+oYvN=={3iW9|L$vwwluPX(k!757*F-~!^n@J?%-PTSci>$7Utcfq?reB5)l_% z>s(wrsyjiNNdu5vcM}@|^b~OH25_S@e9&{cS_sk^9VN!m5my-_re5e1U;ziBL7K8l z$Ds|E2%MmX*DzkuJ>-$-l2t-O*pEQv#b=&b|2PyD9Q5p2eDJK>;|PM>HBC3aA|RT^ zfQn9`LDpFd?GsPxA+9?CB*!U74C=++rfPVzbMFZx$aaEodG29UBz?ikPb$edq z@O{s=bXVUER7Sj4{YdjdA`m&~op1!N8S~EbhIYDDJ6MX;pFpaAr7*TTBfj zrlly12!MP2g)xoO;unnnIiGMym;Q@MZyHhtv#Z|OztPW;J zHbH@NQW&V(qJ)}dKPujZHj7gJHWQ{ksG=7yGN5SpS+Hr+p1;SjUvo@<8M zom=3AB-N~|ay8E)3@47r#LsBQnjde_$?}CDD{%f0d0ZE_Wm4Mh{K9wt@By^TMfNX( zl53~p%tj3AigfICRvIn(p7d4+Hz$Z*CoSGjmW#Qs+J)S(-$+ zk1r6dwJfvVjl1Xj?p+g)`*WamI(%~iLz2*15bvT4eP~i+phCj7W=vuNVB$`FJ z7**!|+!tHL{HyO7rOT?XxX_FmAuwJ<%C+bUl&~Wc)j<+JCX*`N*AFY_^FOX{sC=NO zKmT=`n@V>o{rYn7=!+Bo%8V9>bqCLQYpMoi!cJ9KV=Y7E&kQ5nO>OTOH|wv87_P*v z9k+DCO6+dT*7WZmI5j}6x^FCGu(e5f(J;%_ZI+21CQ8p*z!igC+u(%(Cgbg>b0!GA zD*|7s|2^Z-Oqh$A0U|jwtU^y24qXpgtzaXI7r%#-+=U*zq&?KeiM+A746z@I=nITK+@ zUA_fMgU|CRT^ig_U}iJazI^ENn`?}0P28F}2yc+6s1_@Qq~?Wv;)x!5m@80=xpew>6 zvgzIXqrPfBz?a}( zOTkhomuYv3Y(=wxAwGe%{u2%KZ*ex9Dm7fa9EZUKtjt-zy-Da5onVy!&Bgr3IL7NW z&KpWzHtsirdrh)$%!x*~D9P<<7D^u%rP=HeaW}IJh};bpyqH>!J?qv_*sou|2FjIM ziX(20`Bebmgy4r~{#OfNusL?F10{Pj5nzwNdI`S_HQOrgy%P)~WV2i$?6*ey<}b5p z<(wbu<%o+`!v}@t*-;k#nSF$3j$7zWO`U$j+TNq;+RUlz!Gl|&r4NtHd_Jl%h8aGy7F0U!6-YR4zw`eh;~;Cnwkln1|0FYXY{tHc06R4Z)Dx+h~1b$zG$( z5)6BUV?%=|+J=Bl$Tcg6Wij!ODzG@ruX8m*pT8(XoikRfHKAl*D5`NW>r9b4x=1te z)#t=hv-OY6QooAv0|QY8s7Q|tuumZICQs_XP%iN*Tmb@MODBCg^%7Q6a1n2V1vq*)95`wIv(vXH{P;hhPE+popX zTTvFkw0(TDZ+H)3%Bo^MRm)eu-`9IJ^K+X~5xDLIZb@85IY74;FJ}dC2jG>LhE!Bc z)BG<#z)}y|kq2(qY#N<@W%VPEaIWR{aN6!72@u)NrUkS@irW}o6uCI0+3H#TnDQ{vYEiwE91#4L|tvA1Nb5M$;e7jW! zEBX+oq^prh%kC*~oQLc#`9tb~O^61BtN7ZGd$VGdg2IA=7H~UX-DE6gWHvjWj1l#1 z#>N3mP6s$22nj`f$Uwuc5@B(herT2Y6Q3nsK6Y*7)R+gR`zK~g>9s?n$A78WWA_6Z z0(;BrQd%`Snn5;Ez*Zkh!wV?Fv=?zjxNr5i-TjDUp@>(@?05l% zgCr$|ctT;PT!tu@+ay1QLS}sTueR+^`DSw=zk7V-YWqjek5YkLTll zzum95+ubsB{pm*gb8eIvJe4_DDn4&l63QUGhg^sSxbPUq5ZUP8;YlF9tK+TWv(I{&DOhXeSb9+t6i$22h=Z)tYZh$iy{FLN3x)4TjH6oOVVrqayWgH1 zGpT^ElFNx1I+-lSHlc8I5f@Z*1LR`{Y^*7WW2H@s5S!wZQ5$RUCO;A-1!(?rA2m!KvM+@-8ukX!z z_oYU4b$eG@->7dCKi5l%0niUf|MNdI`N3c*0k{MV5 z=}z35h=E9HjGWl(!76+7#bBj36;KGDd5iy0O)^JM2o_DKJbwD1ck#{spP7N8PFH$(59B=4BH@pH4f!GHl z+Adr`?s`?<>4g`!UY5~}t)D-gpJtY|9+n9R!V>UZAKR}^Vli?^6^bd6*-iny(LmRC zAy^}=l$Ze{_)=U8dsbb`R02gjE(y!*MEBlJ&vjd4!8BMHLQHN^Su>>vUxuU=nROb@ zJx~WMgP+ZA)Hi&=flSpCDw#@?icZ;lm7cgP`iZ&l=F5K;sab^a?DBu-5zcysfY8 z?V}|-|F12CqZ+RXYdEpPR(_BI;X!k+!8!qR6;6b`a}2X-qI4a=UvRs@{B$ za-v&4`AtexpKmvl2i6$a{Ay5dqv6xic($E3@mRosip`xJH<5fRCR6ZQqX-D5g=*t7 zlhKKWNpVtU)#ovFl(Jo)>H-$C7HIZ*+bIN^Gm)U{^~E5RWzqHv>4ubuXeCpLN>N7i zm_AV`iWN%U28yVF{nBAxZsV_=;2OGTTKxRT5qXzC%R?S$p~mx_5uuiXny zu3W8@comRU99lE?KY1zU+c5!N4F)NZKJcSzZp#sGA%^1D4{{?f74?<&nJRjY%taTq z+K4(1pU}t)Vy<4ZA$m@;?0YRWjA`}jO#qVJ-T>w#27K2dt*hI?8F4ugGGfuVuoB$c zdVdz9Z3J>xsy_vYx}RUBnBjIuicoYKtXZ3LZljlo9~$fI{kTLhlWgHK*UC^~SF9#w z`t)|J5YqX<um4{DCU4CJs$D0CI|WTyOrJ>VV@kYQ(L0*{<(lH$DS>+dk7F zTO{h#SSu%e32&!rrL;>ch*>l@w`vR`6aPs0v*@k6jR7k^K8<0K%oWu3?G;b%t|w5r z-Cq^aXA5{_LmuQV2{(@V8k+Pz^W8b$A~+~oVC`QqQEiv$vrHwR`B7xo%k%eGHgBH} zPcHPHXU>%ct%*}Hfug7Zg!hahPNEHl=2mZ};511;h-PRfU7|6i+juG2pzWfW0q|@h zY>gpi+8N}%$~W#Qr{vv`n&9GWS2Gj>Urd%G1+o4Unc_YA;*aw4`8-W10T=$ zS}MNE4)r8|fs$gJnUyfPOohYG-P|nl$0qN3EGVAtri`PtjwB z)c925YLF^%TdP6dexxGe@Ld!=3KfJ1y+Z?a?n$<2+sL+T&qa;FBvAV-8e@WU;7~ut zu^%I53wm>4-)u;=ja=!pKk}>CHb3@vyJBu1U%bN`z#xs!J_<%4h8t?%g?=rD-MXF zr0~5m?yBXLlMUPnkct9ykcY=q&X#*KEGqeS$FG{=me~O_HK8eY;9z#i>Rfx zmNaLi!dhn5duIX69s*mR23f%odq6*}Y9p(|CDZqQ+MuYI*<+Wy1ZLA2{Z8T&buc+e z1FGG6TJ}}cu+G`qE$HeeG`&w%F#+`^JV49oAUO38&qg3$LD!Z5UtPea_=h+B(@SOj z->j;YBe92XsI(qSio$eGN$!NP8ItBGG|YXU6NK1V7)K)M!?h@842~Xy{v)TfYVE#p z>IQX=#-!);xBQG~a(b7ox14RVSOlqXRLpL+^if!*@n6iJuy5-gZ)Io%~^W)K%%kCTH z^MaWjGdF9FhJ{tg0zKK6(cAd2+h*md09X!2A9rc*w?mGzB(3+2x$oaFax<}ktWIq>mafN12Hx|o+d z?(7Rn71rwQ3;y6R>X-XWeVJN!x?L?MQH^>&_FphP&73vom>hbhS&N(c(*5Huz(*vr z>1*$%{@lT9%E?rV25ZZuPk|iqF~ME$%I}>dzl(r`#&*p}{C}q!wbrH%UJP~z1o|86 z%?}%GH`fM2tWdMTbBrY%ibPg4!v;~Jagto0DPsbxo|+8fQeimG?v&@AynCx$_ch}I z9Sbr0`Ri`Odk=KSgR@TNRS{?_ZOvQ|VuC zaDUbr35O!jnpxwC1b`T=7rxRMcb$(NWZFCu54v01shEA4lVhY5w0>vpa5C?-cx7+S zR7X3U{5DwxSqmJi-g3te(IOoZfhx7mAxJMyiH!D{7OLKz1P$LuDRItdQz z+vH=v4D%i7OIzXjh~8|hqQt6-1COUnr%+hiI?GpcN$z)Q`d`#k%lLbHK=%JpiGM$> zjaQSx*cE;4c40MTH~@c24R0uh7FVK7*0u!DIsrsHq?yhpOY0lQV(y{TIEU-NfY=EO zVRJdN+C zKJXU$sPBA??|s>POZoFFBZ*~n8}T=;2VsU>sHYVLtDa zTmXi(&~s!eNGDQV-CdEa`HnY^5hbs05gxhIh_P_(A#}v$ruSv!_V5+beRJ>s#UMi& zs~FyE(Q?S$E7fbEop5C4l{Om!Q0tAIU;tlajUD!y3&sZ6FzE^mM2E-9;M@o@Wf|T1 z8H(7T!9z*8=rWk}_8kq@U6&Ab=``KuLz+xg#}M50%?DFjSh9HT%L9j8MXw?0*Hqsl z`(JNhG8OUM0f!M8?dVL%OfVZKacS`oEUAUgBwG05l-{ z7P+~E6?o@5ie#sa?I6DWD#Mk2FF~_Hr8g18y$&~$V%a^9Zgyi*yd803 zoTfM|$dr2ZCvm#8=Eg5NaJZO)0pS=AZQ2Te7$?#*Fced4DjhH%rKm$6OBH+3+d7}H z6;U@TQwoHPW6(P+sikKv1K6)%Q`=W2-b}tn-)I>;nMKC9tjBI5?Rq^V$a@_b%_d}p zp^t`P`gjTq7X&O(Ba=!i{66-Tf>9cFqhgXcQ?NV^I^-lj>C-z5$Q*Axr(K1cZPL4w z+iMp~S?&HEFf?57iOP;KgESil7e=xCe$cD!=3TgD9Ch1RIGsU?X8tfjVG`<*9z=#++Pi~UP<3|+tXhG(gfTx zcVn$=G^=Kx>1*>JN?>gZVAS@zonZSDAfPG&qn#44u})}k*k03Y3$*oWyTCcn8Z^;Q z9kJy5eYwBYNn$`XZ^_(yMQ3k&@8hd}lmA8eWZ2qLTs1FOt=kRIA{#CnpX5}s3$+eP_B8UB1);c*H z#O{cRNCER5*B#JfaMB!tJQa6$kCU4y=W6?=H1_8C}hhB&`|6A?77hkK&h z!!XG{+X*Vc+^ITkTL%`b`**hAGpQCBNX}MN|E0jB`Tz3#n|oNq()8b^q%^0trp^2s zP`|cM`oU2aIv!P5gk#)u*jjZQ8ULA3@@p~9;ykI>tP@+!rUfPLMBuplIFth`#@$AS zQB}LwnIAe3lD8xWxvPEm=h9l*o2=}gWi-JNs)ILU;cMq<&&yzIBcL4bZ|mt7>lXeK_kfbQ1=VFX6*TW8hNBGQ zqtDP{Mv}@+0f>-$Lv8kLvT`XCuaf}CgXh6X60tAuB`25mnXOv{@vfYI8FV)CGKwAW zQ5+g3Dcu@zQ>G;=-*~($_;=TEAWwBfq$Vu|3(1^cxS$9tcCjnVrf0~70StyFbsYaS z5j~KPeP@X(*?5O*UezI?1w^eZL-*QoqlRkSRTq{bN;?$z#&aZKDBAWgN$uH&2{d2_ z%q5}#_aB91_6xC;RThW4g8qVG7$iW)n?h>#iVRF zwrQ~ZFpjA~e7!D$dRO(*#7E&%WYAQsW`}X+uPc9VT>LLKUDByr8}YYvora1o{*ci&Oh^D?h==U-3p`6MQz32Z+<`tT`(H+{qoOLaJ+Or zBlPoZmHB`!;2959G@UI>_I%Z6uJW|M>v``{j1JU$he=McgkN^E6Y@tEJRS&T0!DFh z()6G2XQ%}>mK6ow ztQV(+TR0XDL2h1M76MrdiqQSVus2wt`7^SMvc~c}9=thJdBc2F?9nSve5&UeZbi#6 zeSpGAl*5-uxy_GZ6ecRiI^iYr_t;)zLG@%RwNN4K^YE6*)mJ)LZN{846+}}9xw$xN z^$jnH=)oBi6Z7z}xu2yhH?m7No1%tY0|(pT+48#sR~wUi1LNJ=uI~J4FcBBm{~yJ)@ul9{@UKO6Vhi{F2%r^7CbicC;kRi3?DdScYw-Cv1D*8+ zzgVEWacX~4F?!Sfue~d9^H?POoF;JX0Hn5+ zBW@P?XBO)Tv>LNBw7nDpmQJD$!1kDQxoDIWBnin{%<$yU2RQ2kzofYm)iCEOMA`yl z$!kn&wMEKysm!=8(JzocxJVzuuPxv0PBe}>KS~vy=@b)g!Cdoeu($2h_z550O2Jvo zg-M{a6>;g6QUCSY6xZJX%D>Mwy5vi5c0?Qt-$CphcK z#E#tv+_%FCjg6~LUd9j39Vd5+%1Az8j3Rx{WqZgm5B*|3{v2$|8cwB~71IHM*wwcO zgA2fSB1E}YW*_BFceVphrI-lnvSl;Fn32Jyc6yeU#-m@`><@4mF!=s*E}HW^bago+ zm;K$ftaX>WmJFqy`Cfp$>rB0Y4@y>n3k%gwT=FRHkt{#+AdIE{>#eYcjGNl*nc#6} z$*~7Ks%|^&?MBRMk321IW_B0{9rYcUo}jIGGW|`kzP>@Hny@IU)nArAP)kkNsTavj z8+*&ZP>x7_)QRC1lbsaB_=h^3Sbh2>shxM^T>;<2SB!zF<-jn5sB|}s{KmQU z_q(e3AEch6A)l|ADj&796l|Hcs8GbE(#OPu!%7-u04MlA&gFIrP-j`8##^fy3H}s` zgxZqk;eeSlYwFAPN3E?y&>^I~vuy{)r$mX?6hlY8pN7qy7#1f&Z z=5@2kx#+S`lHyn=u5z~-W)`wFv)4)<)$-IK|I*q*K1rDEpl@VCDt>LAi@K`RSfKxt zS$=6nmO>XU^gS8%y4$M2@Fl}Ra_(M+TG7R?>p*4s*Sq9@e5g_I@4TyN7S+l2`G0`J zjjex(QM(sMh4z_L_zrWwu%MJPd~E=^8BvfOO5rEMqdC2Q8h9=Vjbah&2r9qNC&An; zKP_1eMBl;g2-eXS4>~|y8|q7;i0n^2zV(R3jfSeNm2U8Wwl)Z{v+g=Qck6l3z~T~6 z_-7xT>(G@aXVn&XkNlvMN?DA)e~Ek3t}L>e(dD6e>kV4X6^7d!h-U7q*?+@ygF-HH zq(#BL>rL0hdjak+dDqK3r+_u%aSB_lmald0Yv6|mYK7}>-FOo4;ns~o7ng)}z~t{r zsUbz4p7k=^yirH5A%SwoeeBvC3J5tHB($bxD!#;F6l7x5yhHJtq6qolCk|42D?Sl1 z!`g!W%>H?&7yxkh2Bt0I zD3ZAbsRTN`@D9#ZLedtJ$LRQ^&X>Gp=I+ENt_p$mXmwIY4sBYXj4f*{m6J?g@!5WT zz6|LwuNYV&7vhhlw4YIgv)z1bS6z!7K7*9QVd`xg-}xFupH$S?pYYa4s)`g@%f}eP zE!{YFnnw3h@jp#7-p9Q$#72^N^Tn=_32U|$$GbsIcggiWCW5jcob7YT4`-;WO1jawP=pC6~tnW9pmW}|qFYVX{ zIp5Bs63+V|vcM38k#Tg~@pDm32c1O|Jok_Zo%t~o-$+VIIt#tPu=4T#gS zlD<%CPIqT|e~N(sJzEM@DJ*XQ_z$E&tgSLT)0JD(f&25yV6PaE$Z|vUa7*1mHF?m| zY=1K;#K@yi>RC0;4$x$iMi%{#(aTj9l1>O~BcQQD@YaGL0KIsI#6x(H6Z99 zUt-0H+_osEm^<(yW!kKf0^U;1dHD%E7hSO~K9%qy)p#xK}2d%&QUDnF~0nu=&#) zN*o`$84|F%nJeP7!&LIdfyt^V$6m5207%gwKX@ybP4A59@9zPvCEiaa-@65}U3($;s+w2GCF`Dl z`EiK2W_gD8?tf9%8|#euS0lY5tq4B(|fg$OPj$JP4DG z`fTzW_(Q|_v8dCYHJ>jxrP7N|I}sNMN98yp1IV7)aHv(_b|wLY-qJ?aWCB?RCJU-a zU}>6_(}_ES+Yf}ZMu1U%ANkbxDD z*1KM~ui`5_>Wz=05Wf6w3Ra{9PRzo-1SdS=e)LM zAO>mPdkqOC%M;K4*aOtC0f5eu|1r>saox8$hrQG2fdzTr7#DtFxHtW?N>OWO^x7%Z zfFrc;4vYpG4plw5_M}AYyiW4mgRwu3 ze?$crzvkTgbXG=T`Lu12;1_BAy7L=9FIb0WL>od1LdlE=XP@l*Jr3!?&KM=n=2Z>X z(1B3DVoM=P$M|G4sWeRWNBA8K++&ws;|{pH`y#Xh)aSAQU$hlDn(hMx`Mk<;w^=Ug z!bzeK*iI1j-7Llt&R$f*G1e6U{33-6DGA$%09;>;0vNzbdxkA8;w#>XbA^Jr19h5m z!JKEKcLW>BI43&sofOhi3ogiJihPeeuaMSIeydyJGry$c(%+oY%+UVh@>A;GfN;{^ z4rt9{eZXODiBUUJmpTLRneyKQFllueeXz3g`|N_ItpJ_VEo@HHU%k1N!y2pR$UtIY zh?N*f7&p4en6(V7wPr`*IBPi0w^`1{EK_WfrJvE~`=8~2r^mUL7vifYA!m+&R}jTk zh-_)?aCwb$~*TZkF#2>}dO zXReT3+$oe2gc}*;-}&2e7FNt!^9@8?aRU-7Mw0B8=NyaRRvJw7g9LW|(=4k9XP%AX z3svD?UZYd5Bi&WKZLYtuluzpK2a>{^L%p|OuNme-vGx8jum{jasS|p^4uwJnIwVoc zl1Vp<3Ri8V%*iX;!^Y=rE+4O&|F2Ay#{glE(BOvxKk#5?k!`3`loEvNf#E z{MAF|IDho4JAj_$ex~aG9m@1*g6}Ek%bStpGCwx^hXI@!L)Om&$HAQT$)L#4t&rJ} zum_PC0ci9B!)-=;jrK7-ETb_;YG>G7J$q!Flm&1K>yCV=fsUP1Xk-uUfu2HLrWYAV z&D~iAK1LXk_v7>$qP^yRZrVqj&#U7|hQ-_lwSr;h_acwIMo9~3hyit={z+qJJ*&x$3BP0{tbVilpWDRS_J&>~^t zySmJv^fe0b>%AD?9tfn^$CyzSWdd)a3{x4@mLe2I9z|*R<77b5M=d?W+(VhmF+e9h zS(hUF-1I7p_M5N0(y*I+c;E`6;(sc2CY#M?7}`7Ep?vpO@k84|&VFC9*h=+s_pceK zCG555Z#{9gl7K9^N8Adsh;wjQl^1CH>++U-|>JJ^-Tu3V*2# zr=}E#+kOpU<7-^a;L@o6-3TmeK_0b-;tX)fC%Ov~Mqy5pGYZ)XGltz?LR!xhKp$LumgNJv8$aDD0kn= zPU?z0eidK~nLS$~I_q|?!?C~UD|XqZz$gZO6-)4xaw4lC8{)XyNK|bA9^;6H^l=se zYM`fSt7VS?Z`QPt>3(q?IgKo-qP3{^$Su-;M0xuf;xG~!;a8S zpi9>NUIm1>G^R@_ivcjo%3mT}FK~P#h>mIH(AxlS8OGSUX*UKC6SoK$7Zex8F5}WV z#Tm@3d)Y+#a0AGzksRtV00JG*a3C`b2qMAb!7%MM zLiS)tZAs~Nbpa7GCT@ldV9&Q-d=HC;VJi^*{mPi7U?>3BE}g>QbjXuk85WUT(?zU36jh!n4oU28>p1=W^?aw(n;1 zQh-dN5;icJqt=*=)5a#7&plzx{e~!Wd?H;pzphIQ&g*+m2F0WuAVwjftn!Zb4}0_Z zh-2`6AeC11F2WTaYt^P4m{L=l@+Vx9^d`KHGBnmI4u8w;z;afw$W35H4tG~;tC`v} z<%wgrt(D<8B^ul924}-9zkHkKh@?5ogcI=ZKYts{d&HEl!5f7IJ2@Q!RWokZg@2Dq zOADpP4)uFZ3fqsRO@(plFnq%AG|udk$DZiqJN)gAn_tNc#Cg|BmF5EL%R-K+-k^T` zKb%feK2aw|k?Ys=|gC6JfPd+lf-*JCan z>ONgigD?UTGSXcnQN#(O_fz$qkAFQxQ_p38Rk-Wn`yPZ+2_?6=$#g`D!C(MOS7f66 zqymTwB3(3okV-DXNpbwObN8PgrhDGERSRDc`oSY<&Z1FElH2tjcqN^nNEd)9{4&CbyY_8Q*k2i_vaSPGWr38dF2zPRil-`oBq|ZKSiKS_c7`7{8*`{w?3@-Z zO-7{Ba0Y<&#?zuk=yON1`s{*%IKkF8{vA^NIc(N7vA-iRkG)K97eK{e18G(XzRtjl z6F)`>W8d&hCytMgVfUFK`-j~Jz<`gi4UWd#D}z$MUqkIz0WK>*nSV|_u@X_iNUpWf z4ic_D=U(J_e6}hVb@Y3cjGba_>-c|Sx*a7DPhk+>Fj5qk=`*ZztrRG_GG3Xq)MV_b z;(!F0=1!ltX0c1ZIOkPWS?9D_eL~=P3l+x1TL(ZFKEHl0GX*v!T7iFs1rd61tK?q5 z<1%b2(7ZFk9n}0i`|hhSyh-?%ZMqcT=;MMzJP+DVJQ8JMH?uFNb*~RsB@0zT4xK%E z;QFDCWA9&`Klt+F`dcWLekTmNqrgd7YE?A8`7lWQTqMu8V-IER{aN_Ma!JZx@Q5~#<~PzIS^imk+)WYq z{_&2|MehF~2v;h72I9n?!Tg8w4GReUxrZfH zJ+!mH3+0*l%PZu_V6}(Eht5ktW~E$^H3qyY3%uzHI`;m`%PY_BKqmyXmhXzD1%H%u z{;hvb<)G(}dDy{6^H>ScMs79Ehdm_!j#IFMBXLY-C41OvGcxsB@5p*Om6wQR*UjH( z{cCmBk=5PuvEi=0t##*Ylqxq>D{ynq;ec~r@slbJm9xLa{Z{GN8zg@s=3djhZU6a& znCsu4@-V*3eakB7cIp=tdI+}Z59}|7)-Jfh#`p&q)i32KWqtBL3PStSQbu3_G*C9= z#2mbpGn8A}(CW%}%cBHr9*{HEIVj9(o{AD+O5SzO!-2WOqf7-)gO1?PZ%L zkB!r%>(!z`x2v8&-)0zB-T66~zT%d8a5W=2C~WwjTO40`<6~(rChy<2yg@NT&trez zRa(n48g?!t-a97(d48VbtdZkCh#fhRR4JS6#4U_76-Od)mn=5S`lI|Nw3q;KZ*<8=6?GLROS z-hgsjB}TJcwf4${*hD?8euu2w#o+NCe|*IBM^}RoCP7)9-&Ht|9>_j`aQv1Sg$B8O zF|e^-GnSFMNzhD);x`9VCT&ZLQBQe#4d2c-PczTp(a`QCsGOG13-SPI;xzY@hGCkk{EcR(=j`MlYS)c} zKZK-UnLNzi-Xd&s|8=E}C#Jps+B{*)^&VJIAnBQMNAt2GZFXY&;SdbMJxMkyF0&QmN=gf`)pGXwE+o%L+7(TcHV8`%2&D~rr3&Dj0tynfzop*?f8=~5uub9yAS5teQjLB z5?&i+SI-pq{<4DZZCR=9p{3bLNl|FGO|a&NP-T8;Yy*l!tz3pnT$NhnmDVWdk#q7M ze{AD*$&n}Oyvu{eaB_!*>f@)seLwOJyRIrvU#~}=f7gs)z@v_g>~+3Oc`6%1dnLk(CRq-^r2gFy|e8r98Ewp z@eKH2oS@vu90oG_G>GO)>$@q+&Og>|v79F@(p95A?h%`Ql56M}>m3a{ps)c^PFR#s z7{iWrdX4?Z1g_QleUP+c12_M5wcEQ#onV!8MPk`i&I5tBj;t`i-TtqD*vvPB8mInD z#2BDzjzrCqCP9QQ2TQ5`MC&VF{db|7M1u5>)7=MF6$OM&)0Ix%3j8TAEun7vk5JHA z?)J~&3jtGX-C>ZnqduNDEE68|qH%+iR3^Pt{H{(qA>5dKU^_sEeRH5!PzQrvw@H|~ zH4vn5$S~u*kOa?+kK%Cl>MT@%uU2VTfCm`I60%TO9nYo`|8U#54h!BJf=GRC+& z<~mqLn^>zC(l2;myw-h6U(u{oThf(gFKq3s^ShW|Jk^;`@`zm={^PmfyknNQ2ZwC9 zdMdZ9qw_C0UvUl0dY@-;Td$P+OT5@n)_&;m^Gn{xqSW0xRPf69Px<_3o=RvsztuSB zCl{Ro%{zBj;+}+TuIhtx!+iQh`Hs&s2J3tCX`Py99|ee>&kYk2>Nviaku!c+yIhWB zB8%?X#e7V%H!;3|u3anp38P)JlKaFSW9wQ8?R=%BBwQ_of08TNavys39`yxMBQ(i`cM@nN^X7cD`O83E--Fv>=-{U;hXu<0}y+j{o9gC@F zqw$z6=ft>}!Kug<3|(yR@8fmH>sc<(gEbatV#_v&g~3MyK4c=P)BRoGi;a~%5hlVzEU+|%;dS*)e=sKq^WBB1E&#CT++aw+R|4sLh4m`R53VM_O zHQD|e=yU5JZ5nc_8QA-(`dYgH*GZQIo0kB`qauKGw9$`Ug(P!E7Oe8 zKFafCioL*1vlr-iX4gXKLbbC))P>Sw=Atim+-(m_-6Nq^JEDOB?ur3iQ_bdm*e$7o zGK-qkpWkx6wFY}hU8s0L@~Z;9!oR&9BrNgZ-q9G`8REt<`^q^UDetElC(fkF4wajL z3Tsr5Y!N;qUBT4gweTMg1>c)Xtw#G@yv)3Q>$BWLPQclN5lvrm!s&@`@aHCXj}LPq z%ZhTTOhuY8#^hB_Cbzy6-MDY;%>BgHJJh(q+**l2Gj|QqLzlsOmj@?~+{wKsaR0%U zy+_uzj>^sfw8ItQ$lGL3RyMigo(G z+07hYck;K9UdNO~%vd-=x#GpM(}`msb-&*5W=`UP1qUo|dFbRh_dSqM$om@W3u8hR z3-S<5>pRyybpM+OBfV*_XM^{XEHok(vM~NKBrgr-t3tHZYPGG^$KPS8>O8A4ZadX^FNnlqakMxsg?4CpEUQcsKI|0}(^ZuJ6M!$UQ$11xB zFw(*DI3=I`N?uq&k$|=KQiQ{PG^<_L^Af=kW3B2rkgc=iIZ;h&9&t5;Iob|<{aM`= zaYwKqXJG!S+?=Ew?Er=5ENB zTLCVhO3M(Z*bxW0BLarbH*I19`shspW!%=IVN;?&sGYWGqDLHKAkd?JE;8=;4bjAa zj$5({H>kHB^RAuMPdFje`1Rb87o9oEe&m~$p%4#AZo^x4bC*Z|m zxK!}Mw*%DQ^sg7R*{+t{JmxJRu_qv5Nw%z1+s{|KQUN41$R9lZi8J}5jA8pJNEWky zV2S75NP!=vCp^P*&r|6)`jUD1BmW=bV`efuMxW*7LD&5bK4_=jiI8hgRC_Ufx7;F_ z8J;hw&fGqttor2MfiYO&&NNPSVM*v~<7<~nrzf9WXRq4vN~qG%I?uaq22M$aK7Ws| zzsX-M6fd1(w`4|@p`MR-v6pHiMkr_{=x@t>qH^}Fq4FvSW?KNZp;aq&>(Kb2$I&>qFaVJ=qPTN$ItFN*C~& zM-X?nR`lkJ18DBlBhp-O&^~N2)nSz2;O0uCn|?3Zq>t9M)@Xk0FsMN&#hgA)RTq3I z@O{X9**2R`7_9M?Q4_jj_3oxoW949ZxYTaK;V$20aQX{vpDwxKO#mfrWyxZPFITmu zVl7OaM?SxX&yLf5(=z-IIpZF(#+~Vun`1#I)PxA$opE7wM%2R`E%$_Tf zH{J&}q-eoFBSZX`uA17u*e-xSK1jWja+mO%FWt*z1M%`x@)v&4{v;3oZ(jP+(*|>1 zLz{Rx!km7Ro(-6yUt_1?qv1Dn;P)E#&m4VeUVYn{#yA$S5H_% zC5J=D+)bbA?mZ+u7_bR$49;8jexL;yqn9!fi)jUS_yfN!8fGkcB7=HO);4aR5sm1m zHu?N0zw6=ev7P*X`f_32hh5HKpGH`tvd-+d*jFA(4*;)k{>1!W7Qp#)fcgE+y(C|! z$943jp{idB-sQa)vuBNSV)DKwiiHE?5HIQXllZ;&>bM36S zt+I`bO5^)SSzuXFn9LEkbOKM6g{XieaNcA_R(Dl1%99YwC;$E zwssk+*5j4cv_&X>C>*rFalPLcfzIU2t-$K~v`;p@2&GXfpNKu(@suvVrBN;Uysaie zif2}LYT2SQlfSeTN~IjuI^Y;sapYxdM}?e_RF`|*epDHJ`_W+m)h|X8MEcd`Jf861 zSB`XvOC8MMKX{r4D*v+i>ZI)9>bdA)VFjVrB7Dzd4xfBVoAt2VrzFzylGDdf36Cc( z^CR&J*LG_TTXVY(ycg4OZq=kw?nd|Hbxr}`^ zka5%U!C@zUE1&N3F@MYqv3fFy1v-$;iCruGFefd(E;#>e@H4-S?%}9&&s;6}_Xh0HbksW~Rm~NTi0C_ahNNYsDRBH(d7Itw_R%$)+ZMl{M}Hm6zC0x9 z9&_Pd)8@ndHNSlbrHS5P{edx*4M(X+kB$+B0gG5_Z799+L>9-a$1NH&=K z-9O?ye^Uet{l`eJ`~jK{(H#f%CF0(r{1mpYu-uslNhq|BhgVO%edzaL}zb=F?X8m^?r<% zHY}zK@$&Lo=Bkv7bTf(t&Q>2^N>zF_*sHk}!5^nAc2ih~A~ip#!LtnH6&`@6-LzzW zKb}Jr7=#5kpxhG=0_u5K(6Xz6blBI3{LI#$;ibj^Y%gR=aU#6T+>K2n-4vBYOD}z6 zMijJaQ)PJK-&h+X}R#Y*uy zSr5Lxo6Y;=xl;UR@bme^HWQ3?LT@{@YH2bb;bwd2o|S-`%V8IO&y&mSU3n1rYj@_R5#1@VXjSKejl=cJKPqR^ea&Q*KkHkb^opRY=3KqX2#On zU$y0$&BN#6lkg>A86Hr!!@kmtQmrG8pFgN@P&a~no*6#wC8Cx5>f~c}N{ic#^*wUY{Kt_IshlTW`-=8KQgqVN+?0LsOn<2%2^Q|DiI#okVbuc_1 zO10fcxD*xJ2o!&#h$`k4A2`B>`~6H!5rjrQAT$V5@Z0%D(rt%PpX_9b@K0WfXw)#| zZVwK{*Y9>afXj^r7jg-SWiVNJ^Irk!fRqQ;S{V6uSGhpQ@lSoy{m4J*bYMmJ&FTpG z)T>XFNrFe1JP4z7zlb+N&f7V>dE+j7Wl9PHN*>X21373b*wU?~JUg)`kDckfW;PA7 z8zX}s#GV%+w<`{t!@YVBfxn7=DCRVLE!N$X8;ddmdRk$`Di0{F zuprZ?r+L^&Yh%ZMZS?=6>OK6S4*d9WTP0LvWSmisjFUZ%BUvd|D9Jd5>^;vqLXvT0 zMP@{jnY|@@oxS(ooPFHg_uc37`96NX@4xUKukl^Rewyesi;pDas1LlNjQvOSI}$!U|QSbnA0NFtgXlyPHodq-9 zBxl>_^}iJVG=`FV;}B606~@oRf0HFj3I0Tt%0T1O7-`9S4J!DV)p)`GE!S>zE=IBLoN7girAG00CDsyrF|#fa^L8^U^Of3tk`7 z+8_Z;pgO*<{k;;p^@x4xnQUzKcM#agF<9xvAT4%1@HV>wy^mqed#aEC*1O%@@M_=$ zJqhN}Jb-*zHf*w$??j`-j9hfTgS0EaR*N|PXwGF5WZ08bYW`UyPf3UhUrU>pnP5Y_cSCw8Xal{y5#^Fa2aB)-*$}KQHtVxx)r{5hJ&;VglEwu3H6EBsJyD?II?oVHI)e%&gcxvra?*V^Apw z(aAwlEfixUEO)z=M6m%e)K5lKKPbge&)cd@bp+&-gJiXn#RgrB&j!o$QSJ-2@)!zC zHg6HaT?ien#;?gsDFpJEB)uaQ^PGN)Ou~GA3*oaF4ZPT$W#_*+D_6@xc>B^Fr{f<+ou*va~|e!6WLY-)GkhHC#!i2Bg0OU<~ZQ*TDvaBB;Gf!6dJ ze_gRJx9+|E>{Iu|dsmk66^{*6>gQ7j3H%|?_A8mqNKf_>hZh--<~w;rt88D4h_e{K zE=wFOUG=gj4s+k}_xqmYy%E(6RLI1$#1s9(M(r7*7ExRZ4|jx*$Xg%8&b+2+e3X%< zc)ji6U=#0vlo`hxiFV?QE1q81q;&b>fPbn$H|NQDTP_T5&1qpq8WuFcI8R28WCmr3 zH!cN;^s&Q{Ov)+XhvBcm22RVJwr~!ewbmU%(8y=%u}VVb2571pg%_|*W8}~Y&?AOn zFfQ!Kt#6zjvcn@NMaMpSE?Wavj_n7QQk>&~YS&&VI{AHyt(f{l9bo6cd$WBL9o60i z@`vW-#F3^bc@1W;vJ7$EyHko?Ak9u=Cz~k@f>w;&mL7qDr6FiHD_qYxZlLkxA+$3; zhy#+5@_`ddr$ii8t`QLH%Azr)H+9wkEyu)CJ4`eU^!f#mcVw7x8U(l@WT1XkS=OU~ zW=G*Pm_b_x;sG+QbJ&>-srA@POJWKieE3YbFEMxUNQ@=>2z#6&>(W7LP06?at|aVj z*3MmdgNG2SA9o9)DSdaVO}=~zvlR<^)(b)s>yO8jyz~l`!kUyHnOdzSN6#wsO`&pu z8zqgu@rW=~7|P9zqn4O(;;C2OIKD6=k8D<+wg|ThQ+mY3Y0oxUFPTGn@Mq^tANKf; z>w9My(aV=8ZQT|36o0r{5M``^2m>87`g99d?(?%vm5fKWJof{NulNl z5YgsLQIT_1^Bl`ADXkK80Mv@J;63>5C zG{>ldqAja!Y=p^0j4!eaVOD};oqiA79d`j6j<2aMEIUAAW0`WXsG;mRTzLG4{3M#3 z-1^4tdomb)UQ%h0)gR4~P9#N4?A7s%X!1Dqwi-4soo$VFu!LKaY;kym%j5{GrW)mB zUQE6YGVE}%LQD-FgF&ECE#l-o`lv$h`Ljwo+%=ug%_0(Kn-r7x6A41Uk!~dJTSoS4 zRcB;P5&vvff8z!96;WHG1vFB2(@tuy%bY{NR9`Zz#t?cmP$u_5e{LbEa5eYOSFE2T zdq`$55C`Au2C06&_P#T?3H{`=&9eFk?E6M4Q@DD&D(RIMfwuyKBAN=1N9O0FymuvM zC;#8K{laN$rhZEt?W{ob`b==_OPTQO z{qn%|_?v2YgD?Jg494hM`t&BrY$yfBUcA#Ktp^%SUgJ8Z#NeQqs5}Oh@t!2J%#{gsi^1$Q^Sdl~7p+{XCp?-x1yl$)aPv}WvGR?{;oM#)18 znvNCYD}fH8n#ZIZh9biqEgw8I7S;&7Q)F(%fls51`l)U#*LQ_QokV-)(zzkW^$|10FHl93xPle#32VpGbqn^@T=4&BcxQc}Fw)DnLa>Wlx9a78lt_x(O9anCzdvk%K z6wFI_vn30qw199sjVe{ zwJ0*+Tk?Hl%l9{Fv;uRD)xt9EFuZ#YpHIFiW6pe6BNShDCw-jlInnoNf#W;8d%0dH zHuupyhF7#qes}F4!vFmNhLDxPQBU|pyoK7mk-ACLx)9tcDr<}~LHUkI;k^3Bar$c({&l(n0b)~^fryRZSrGgzBV!ao22Ct7P9D{NwEo>PbZ3DTv)eBayp(e*LpwRQGw^ZA9k4np0(h1t3y(A2GRnskYFuOog5#z*9Sxh3-tvLe zoyH+xN=uik8P@dp_Ma&m-KIZ@h|uafW9GyiJ?mQzGhdoB8@}x;#QW3QVCi%sp#g1>1g``uTzUH z2J&$VNGz}4GH@czh-qgsO|I$fXrV64wa3SfHCjaHIEY1eHn+nRqq!Z8p9Bpr^5(+U zx^^0(E7@`2qCx?eYLt10E*((53Zn$X&MHkN>2=7kO0_7f$!AoPUr77puTn%d-C^l6 zYig#|)|RfKdQyGEF;MIVRCe&_YAWSTeU_Ci07ig0UQyO|TRDPX`@vfX+BQ=+Tm}F` z0R}EKqkOiKcK=})KRpBzYT>*^oEY7_d6&H z$_pvv#dvo?)C}Y9dM2Nrex!*orFeT_^%2n&;9yU5=FU-_(>6x60u3L0LHsu@13!uD zQqjp4gbD4<_oQeqX~i+DNkf&3EnIRaPJ+*@LaeVHrlOT7R4>nIanTpWA>V+DFY>i` z5gY+2Z9ViUIKP)AzZxVfilWS3JD0fgYob7v$*LpTrvF^<+dw#L2FfLn$eLM^eJ;>sHeu~QyH^#T4O9Y2l! z!)AxhKOol*LB-KNdp+bcg_wj8Q_?|N&OBxCO0bfO2sL&zhJ-h?4!0b$5CFeMzrP%) zLhZt;t%$_8=xe7hH_Jx9Fd?qSkUNgi>kP)HZIw6$MXf~V!gP*gnid4<3prQTiiH9z zx`gOJ80sp5WnZei=Sv~3)XVuGdXp08r8dIORE2QP@75=elW%jz^&V>7s{QOGEA`GG zJx_l7Lz>&y*1OAhY$Nm3q<&sgi&3SCPWT(b4kRmW)? zZO32vD_Ng^Ct^lJp^i6ClAe`_pBuK;)$d7Cu*7SEQ{8p|;pF*+Sz+563h7BU^v!qe zhVDJO)!^*?<|IgvJldm@9(lGueD8KsjB?t!Rc!-+pNdyz86P#pIL}7XdBlWLFE@;+ zQp>K@Bm^eZ6gr$+&o_imUbm=q8V8a#|IjvG>z>BVOUJ=-v@{PJ~$#T8j9rw2n*JO z<8s%9VJ$@#x`DD@+0XCdf0!8k6jpkybrCcC{A%xA>P)_oLL2$WXHPPo{e9OF1eRY8 zlKD(nvWQfOU5qJzsd4Z+ZRsbwN0xOn@Jwe)gwxtAs4vEqXk6!YU=Nh2I@`%IYiSeL zUP&RY1Jw#+XVJzU=2u3~xGkdA7vUM2Qfpb=>+urpyi>2g#Au7kc90`E@3`ve;45;Q zAIss!m@FQlp{Cb_BYy#t6A8z&D8{dS#N%*w3o)t5&uC8dT?DJyTTWKx#^rL-sEBhT zv`bU$JCh01NQGx#wBJXDa0RekKaA^TprxA;3un_iOVcy|n@Nt5sk_lM(5gsH0|v5H zZ)KpCkFV0D#<|3jVTwBefU}=;<^zk>*4ySnLS}3`Ob#~?S`poxMu@*hKU2yNx2YcH zZl#<5^E{R9gdV}RKOCZXxEu@L{g)`_f1(1YTCf06BRpPjs}D_y8h9o5DZM+?^G-<2 zD4EW!AIv@v2gyv?)t_h^u#jycrq5Rtd?IL5)pE3Lo6Dkx<2>$Oo*r>9Pi0%~zG%?< z0W@^eySz6V-Dr0gO=gXE(y?w>0H4vi)Vj#u`yZ@l>Cjf_3t;qEL3#nCiAg#Vq z&eL2ZUKF0e*`*tx8IRa)%czQwfHE*cH0t z6Q2`@!_TGi8p^(o$yNsW+3{HVWh5nnvJ+6tSTUY(Kf1L=9GHF`1U;pvv&!m=JU|4= zwK;&+qFlUgU5QYRQG2YUa4xGrUj?T1TTf%uCX+i4`()R}OM@owz}r%D(mo*$#!w%9 z2jM6AGqY5#(H2i*WRK|K7syX0Ca~q<=7a`J$$|0^eQ6gZKx37%uU}0-+yM~s)Zwu) z>C!dNYwfJvlN`SCNqY`n!{3n=7H@zGaM?NbqXAy&aBs>4K$-1tma-$~UNWw|$Z8jl!XRD9nWJg&D-Sdw1^wJ>LbucE_jG8!} z2wFFZL_=Zu^B_YK?}TQ?vq@0w*WE%W?jJlDvu|QhspZ}JXmS`yB-{& z@&|gEK_%EZs--giAteDV@$90-bdh^VLs}|r80A>E_p#_#{LTPHeG`kSsngic_1jc( zbN^jRQg?`tFSRat$u_7a!!8`HQQz?j1{X`IaY9_T0XQ@N%6@X70-M=QYtbC0Cn9Rr zga=f3L{AA-f{=;J-k%VfBJ}C#>@E42@sP3ybZg(CgKGCW$)|7p)!ZT(mcaIqJ=Af+$8N%MHA6!cNWS8R8>C`f3Frum_h?~Xo|a1% z*`v-i|G%2DB<97W%ZerR#>D*t?s`Blk)hi#t#5vMpT>XQ(D96eGzfEZElD@%vFUYA z3vrFb6d^iNIk^C^(u9ZnI&!cqNGOANxoj<88T6}WbAz6^n7xBs(Jarb3xAqGE_W=( zxwyU(kR~<1;(ewLzjNt3@UhM#6PO0wbat8;_}X`~WJ@lrI{&FEOGBk$bzU=oPt}y$ zy>KB$GG)@-{Ot&*_iGmK%WK+;!>x8b;2DgXq;-gg3i-et!YeMGr(`jlX~bEtxq)d> zoeDK+%Wrqh7=h)`cIoj_!);92afu_b{9>^&N+pc=>J(5ee7_R6)kxR!We(U)*qC>U zEM1`WqjM?0J_EnVa|$ZY7b>-5?fV~DHQWX-K?CRNy66Y%#EXkH;c!oGVHn|Tspw+7 zU9m#MgS+J8Uo!e|b|u|UFM%e~d}-?ck)8U`+?~wNXcn1`>!2BTPTN_GsWYOx*bK@7 zFS|IeX{_+6MlmX^7FS1h@C8IQtOR_d3-u`ft|;J*2{h7HaDQ@K6q(}STTDK~+n?1w zAgRP%OL?7T<-wQigaHPsIi)*Kv?JsOU3{wRB3oM-k4RQDWv#nn$T~{ouCA-pjt`=4 zeS}G<*>92~%}e&QxvaX7%3MY4goS*Xv>-Q~?oZb%gvgwHh)-J87MPYeHOcsC|7V|+Prgrf;4~9MsJ+zKQ zYyNlI@~rsJ60*09ulxrV(*fa79Bhz*)fPTrd1;d&^Gy;~^b%v#t-6_titr;!GeU+5 z%Ist9L%ehKz~v(YC2tuzwG5#}(X}P!UJ2vyVc7@1t0Cus5i}f_m_Vw47G2KTb8YsO zpcEzKN?8^vczA%%Rqt$Fi9x8=Wn7KQV6AxvyZ%~n86bz{#;ssSTG!~G(X$)73*}er zlX4Z&VJG80G#vWaZwS6hC8Zf--D@$SO$!tkmE~YpJ1MP7;PV$#Z%@l^FvL4o&>99m4b)|-bWRg8cf=-ccqlC(KOOeGU)LIp%nK^uHZsY_4hp<;~X@5wqoN=HVz3 z^_b%16To1`JbLglnNf-JS=6wx+IXj5wNw0*sy^FUXfDcYRhU=8 z-X>j7R>wREI5;Xn>b6>o8=?m!&?B^El+{r(Pw?+;9T9}`J*XjaBWK4A%Rw$UTtc?p z;%vhAevb2*9u$x_x5T8Mb?`+;rGRNaNG+^o)JYt6Wl}FkUg1Q(1PSS9~uAYTju(Rg@M~6q;y|9PXK}$vVv=aScvmr;VU%F>5 zcN|e);iQg{LDf7#&(?nTq;4r&r+#LR?)2Q|S|jb3{TZ&dzrMX-4Nfd_r0}^&1l_+n z+`S_TZ{;fukUOKswhDA-bDUNCI*@}_TMx(`IH&rE>CdWfby1VSJBu@)8qBvF~7ykF54Yqmxi{peM8OZ@*b#m#1k=YFg#0VQY6f(%e?vhDWUXscff3)2-#-TfQr-y={LUmE%KyhBV3~06|wn2 z1DlI7-=|kPe5UV%1Je!0Nv0qEVt_{F9juhA0+FcL>Tb|$LTP;d%aqUe*-;wKUlmQOm1q|3#?<*AMw4d_Y%B?ViJDs2CwTs`CTF~pD^W6YwxEQY^))|_+X9V zM+VKKzOR|iTq=&&jFXSJ?m6vlGWT(D>6(RjhKQAy z3+-KBr(@X%X;(HA4;4zrvB6F1)OWlk^wVyQ^h8~b+b5q{=?3dg+(0M@@7F!{x_ z=Ugi38c%hr*GDg9h^zM6(TonfYGHg8g z<$e0lOyFL5r?>ORnMW)iHE+GVCCC)c6N||hxzr$s^OSqP{t_x$*ZpHQ^6wAw*QEom z>tSBRk-r8n;B&*z0Nev}@8Q1^O&ADvTn>M<*{x1JEt~7h99qmxhbBkhz-Yy?6o_y~fhv!M#>!H< z3Ner`U@zr0LiC7!PPP^RHmuGTiZAC$@_gNf!Ibk`V8F2Z0WGwmi!f2w6&xRUXx?}A z2Kd^%xigPYmhuPn8}=j^S)((O{+;N`123}oaZ1K;ek5Fxa5(bVE6p7Kt`NZC@oOhR zDVy1}$m~fKeWK9nI*8S4^Yi4FgL`w>j_!?l45SzsWtE2)G0ok*yWwwc#Qk$c3LUCr*G}P zAbJUR?yAx-DR^CmwWF}LbNJnz5{9sXeP~++%>;?O)@tH>Pi~T+80>oj*`aC^@|QQJ zIny@C_tUfNKH${Ygt_7;;ulu8Q*mY?5Dv#Vk4>| zl{6p&ZK;My`*o14Ifl|xxwVu+Al1s8M0Y1E6tumPhr=a;-fBiM8ZSpH>l3Rv%&n{d zgMmr zb@#Hb3~kHVcwCqK8=cX#Z#vek?tc5556z_LJu%YD0#FEfV07>k8`J`C=e_gxi@lAc zTZS2H_mXZ10RP2{n=F&n_q)VJZFXRhHq*0=X>I>er7Yb)fD5B(00yJV@BkID`)|1( zoAcU1oxS;PiM>@52|!WCw@Gt&gAQ)S+3<5((?FlX7ajfb;T*nh_3o3AV{?C6!yQu< zY)4Q39rY$wolM$J1pcj!{&G?MQMtS+RYi=MGNTqlZlYhG5CSo!5gmDX%^2hAGZ?L} zJ$-&3@a8*rsmqp8a}?X1h2|J9XKLz4F*ebOZ7v_$`q|JRm&j=w)E$_US1N(B^>XmJ ziJaWLF(uwsJ=S52Lwhf10(>{@q?ZaVEbHhb*D`jblGHG_v?xlz%YC4SrH^5!Tii);blQ z+F(fPOi_#uRMdr+Ofhc{`{tEq z+C8T(En#CQN5ovk`QciT>{gFB>?o<@w;5nyl*_sDCtGw z`qBZpya3n-oHmZ_oxP%_M5xnelu4?5kB_K1L_-RFwG#oqQC;6b{0rs=LaNfYe3Y{; z{xc7~0`*}IPTHhvm5@s8-tw|pkvEC1AX_GtZu(fF zx}S+c+fwV+kBt(!gEyP?%)}oVl(e&dzS@2Z$rO%>;?SVpgvC3) zMU4z;eFPRbH%p2S$%$e)-BuL;6DjOc4i&BE&E1(I6#Z@d3!8yXv_1#Abp%VxfqFH8 zj)=<#A-)c#O7H6Y1eoyXhMtN-!a`k`#tfFe5zySo4WIW19>L%S*tn^+#__3^>NLzG=pVkr9xsY{JFt{q&1IS5TtbRmu}ViW~x z#vhEp0sW5vS7V=hGlG1GQgtH#3e`AvM=&EzN&8m7FK;9#JFDH5_4`|$_OO_)jFvFo zbaE+s{>@e2?Jb6eD*u95!oclzP(~EejnaTLTIIfLZ>Qkg43->pXFqjJ-Mia?fI@rbXHnD&u00X^CxHK_PnI< z%?F+D^~^F%pbuG-j$oqfSmp(i(*5)fRGn8z?q1dFic8^}>w{M?o7A3v&FmD6K1iHc zEeYNIj^$Ll=r4>N)xS|9`|=;h*JtYYSQc$IO^iQFJYxtP{vP*F=5sJgJZjCSW-4!s zJ#IB3BwTSr{PTIV>OA~aKipEO*A&%{qMIkcTyZ@Y?|PE}vk(vtwTAWuW=AS2sxzLc zKE6M_0cGXS{@e$8j0$IlR9ki~wx-vtrTOeuZ57tOR1cmAVdPDa2<=%b z@8Oo+o}MS|;r1J*QLO1v*Nj(Zl2wx5C*39;EGIQ)Z$LWP8A#y`-{Do%LtD;E#3m&j zZCHi27&e+Kqk2;F8l~pns1pM#HZ!kVkgbup)IisP*1%Mie{F>E1H;1Apk>Vy^BA&0 z+A?qC3^Wh}jc#uUzn5lp)=_(d0+ejQ4L6G~By!!eLNpDS>{8zec)Co=_T~7{S?>I- znJRRpjqX^KX*tHR2qKoiY|#ByA!ztpb7&OmXcYm(Xwg&vnS)seM%&i5qrVivR+1FV zfk$t*nr`)_U(v@qv0kn}{5r`O_DYxsl1|C(@hZd4!sb9tpn!2ihNGlHht#GV4>ak> z<1ETI)Azx>056f@187Gf1GBeJ9SXm<*b`G50G+|h^pTkVl%vM#_=J=8fHx8CpcS0_ zG$Pl9Xaq2}7}Q)G11p)cOkWPuT3mswNR|Cwx?E2;o4@S+ifJ{1dg2y-vEh<}w(w zl(CKLcW*ACX2l!Ip2O7uH#JGZ-s2>hmgdYRossdRy1rGImw zv3xGLBwFbsPjT}Pg52wQ$p=YfPxJS#hQS`NzEe%p0n2*wNggdc#*Fajcjrbi>sznn z#r$C}10T$Zzj-19wJDeHksF$BnUI?P}jh45B(slyo{9*#U=|UKfAjgZiiATJKnAv-iQtpCUKLgK|+=tc!w0#)E4r+Q!y6 z%Yj^u!4Bb%*(5aqio?Ad`y;N@|)6}bdyQ4YZ-1{tZ0G?Kqf zq-4qI(U<1;0{!;OgN*g*V_NCJ_Yg{f6%ZmO_%d4s-2drbO@g{ygRIc+NNw45TxApv z96}%9(~z-1Z-@(_=Qrd0I?4S)UDj>i@w~$Y{x)0nd-7{${$L%|!Eet$t}7gEacEqMvAi7Yy44ozhW(=jvklSU zmah0bhd*B%DA!b9%d++Js~_oM9m-d)urKo1`R2&U#$w&b!1v@wl+>G>A5NCx9Dxdyg}6CJ#z#~XOJH7|B}O^vCG^g>-yp++qpP3P73 zyT8cf_!F}}GjTt}nrmRnFkQ_d89`j_9T^!jLiiDA5UdBL5!r~zQF_?v`^_%>`s|wZ ziT)=++*X1w3-cTrmkKveruGUI4~oWKY>nEp9C*8ZZO$}i%W8(TeUG_AR4zxwJQ?mMvCe5J!Y%n}uOT=G$gbl_-N85&h zwP`XeQC9+fj@HG_LYvdHFZpfm7>1>$=D_AnSi@!c=3F7sp3yNWYb5dNvIcD|UU8!{Ct3a8mpc;E zcb9T%oJ3vf^yb8)SUNbnp%Mq?4X7G+-XUiBQe95sl6=0}-q+a_QDbqp*FI^C8jEoO zPGp$qtdKv&Vvlkaj|$1JjTM-h3h;v8libk@dNQHlJW81crn@7@F0$R!t&ALGcOlO2 z+Xs&ig%UK&NkxEM452>2HDmKCj$X0!{c%QJ2e4OVAlx#zwf=p<7$Ndf_Fb1hBHo;V z729iN*5dDa)={m3CeDMYp0fuPl3Sx9?9<{-*KP9lRK+r8IRb;YBvP5CzQ0*wyz3_= z;~=4V|D&Yw`MVNMYjg3lIT`yzL&NZuapde+zDLOcmQ zu`f?SirBEt%GrzDgZHrHo`q(Oi=A}oTwE;dvbT(+qVaP;8I+@ruLj5kDGTeR&^tAT9q9-1uhuUvP=?f#8Mb=cvePI`WEMJ0K>G<1X zxAzUoZR{t{TgXQH#?KqTOeJl0B{_LiD{t3u<{nC|Z|}%wGKp*#X~T~5tYfws#v_l9 z9jAS*;uRSSfVq?l@h&`J8Yi53n6RpqB~Fx0*#fL$f6NZo`Vk!H68y}FJ_2tFZEGVN zJdweM@EVDRafQ%DTlQUh%C%Rg?^pAV9?X-S*B!-}a-Q$eoS8@OaNAY(XoOaxbDr&v z>*7hN!3WcFUo90A)LDS`Y^etCNMTJ}FZmq%xi62%F zuhVj+r%PB|tts7iMZ$T&*P$@y4(ImE`(+frh5Svr5%*b6w#lfcj9Re8hgAFrq-e?DU%4&7qR4wnAa z)*p-8+X=7t-;IexDVkApB(jRdTjrmcPG() zY|Qv#!DiskxqaG6>dbG;3>=gVg)T12|LXI9zYzwR)Y9oNpCMD0i z_|>VGK;knT)Ku@H@fPtGpmE!LqblCTGW2gzR9nVv$$s*u__cTKf^ab$i!SnCSo5#( z?hHpfJAS&ZRU2_4jGWi=0Mq_u*{nfboNhvht1Tm_nTz>ALFvz0OpuSDi!|e%+4@`@ z_UYV@(ZZcw;|1WA7l`=J{>~3$0P7(d4%n>qFO>Ch(TpE{Hm3>=)8@=xVVLnJqT9!J zKQ7Vy16Gf>5Jb6PIE(BWe_ z39%Z+(njUieYM^;;Jv%J)7$H5U`)oO%yl7fd+e3oShY_=;LzCM>k>}OfLnGec%>qm z(q(F$n|wXIPBCLacWQo93#cTD{!OHI+@TSGTP4f?<6SN^{l<7P`#kmtCSVmH*2aJm z;_Hn{w1h;qkD0IvVzfpJ5S~=3nu6;Gxh99Kv$VtaeP3yze}xErFr;~^nQQXu@e3Z% zC55J_OUY|J2%RR~hq|^q{u8N>l9_($Bn$CfB^+s|u-;60$tL@UNEo>A&+^_uH`cw- zN;@WeJtdmCcNcX3e33u)(h}&2o25Vo9NuQ+*0p6cahv!Og<}TwXbPuTZl0H~&Y5+K z_oMe{)CM7qEPmK@VqwjEXxgkxD{TUz(*ZZ>`4edd}W`+?dI8{NCxM&i0S6kSwosj7H0@m;MbJrAB-$>ZRXyI?Vmq>V%&g zHVmHUQ155pEfm3_(|9yDtD~@2-T(956IkIAx~xQSbD;pOC+&AU^TI-b^{aR=TV(up@WWly=1psu{?~em>3QrffK#bCW+Gw%pVk z%cJt?sSZ9iF<~r7?K@HF^pCyMsBmIPj>6H=*R6GsV(GdP?`?I?8BIX^7gFT6V+-5? z+A4qnH>8tW4{TfGY9t=q)sS3-E8RVI$W*$e@cHP9AwJ(n%HmoxUnrf|pgmLM=94d0 zKhsm7vr=(*++HeXTk&VO=Ut+)D9YqG@~h$qPIYr36qILj5xe(mx9SiE(%+f8OeYq2 zPZ^rbefwQ?^lR-p1loC)I_Il@oRjYVw96s#>)vhshVmLPlamWDDA)bo$N~`#6KI&w z{yWmMr&Xl1_8&s>$BfQP6SnmoaPN+B(uB8ys7E|O2b|f)2U{jJ5^34^&?oBu1>|G# zsDB!r_>*>ifynQh!-ndyY`q6#SBy~#46Awy2ZXGw+g{t7aKgO8NVv&a%ScK4t~{Mk z^uEUAgAy8i!$qP#o3z6x5bf9tpi~z!K}=X`t1t3pUEk@0(~7evZ^9_B9a9YTx$A(% zG=DsaQ0UqC&S|vu|7HQ42Eb}uCeR9v$s7>iG|UHxK9E+Yz2bN9y}u|&4Vm82|0*5+ zwoGC3#BmHIFk-)(VleU@Qd2d_2w@^luQf{Kw6cCef4kl)AM*tqIeBRrT-XSl^OOgC zd+}?)xH0E@xdcL4O;y-K+fH7wKgX4^{7H#9|9xn^-V-kSjj@1|h51Q}cq|W1hc5G1 zB?o49Md)%#@7pILY!eidqcB$kca5Yu8#t@Zwu(9@S2_qYX)h4YDHSkXak3m+agi8W z=w1e0CLNL+vp^MiCEm_|!5(BSdzG*12wIi$O#2G0R<)9$4DA(Gw|Mav04Z8Mt+orA zfC9qHB}{_LIB7p#OFV>rJfo*Fw?iow=(<%h7A~eBLKr;QdYHq5> zqyar};y_FP*DL3j^=Z10y{eAbi1x?cI-g+KLbtx(WgK+iVKf%{SqZ=EKdH)CxCtDY z4oaO=`Ut*z{jKdQP+g9MqK&%G`CbwuIeOMC7R`(AzYXzueyooo%)XQ)ZkXxoHxkld zRx7UNxFhQkjye3pR{g=?TmYH$QD|%HW^KHP+*+i$UkTZH{2}LPu`UpGa_= z)32AI;{9_$BxH^Fpooy(-FPw4^RfI{L@Kez_4SdU2|i(Sb#G>%3l!GD1sUe_J<|Li z(7N$eZumb4-VpJ8mQ82!Q7T2+ZrEczK_=v z=CLnEXdr&ZTArvYAh{&=Jr-Qz*^jI8BaMg0{d$KNYaYFeF&36Lt?hl?RzgL*4em^t zoOdPuy-4zS0nGRApCX>hRVnyiS=KB*7$Axml)6n^1*hz9)ROJi9rndP-&WUINcuS9 z`eHSIzR%uZJd-?_F8Wh16fbtOlzqMf z;7DD~yH&4QX-xq-B^Yi}hnlmhj(seR&_O=w2)A`$#(xi(xlWNFc5D3d?|X786VB2U ze&$C_ncUR;5RN;gn^yYi`D#Y>U)Ubc{ zm>(*f&@qNk5qSDInL1Tk1^LsmYT#|Q zU%e_+z@Yk}JiDy!Z$>$v+G`k9sNHMmfH38lN=MW$xuuOnJ@Ny0 zx7u6DaP`d`!Fi#o099>3pxNbAeVrj<1)g)}EIpU+=1r>JpVQ(~Y) zYb&EL$9bt93YYTzs(@(@wf??kj2Rig2!LQ@o<3fvxy}ETv@&8HdPVPLsl$6h`JZ%M&sfY5>vm#$A;N1RJCg zp4yXiXsR%=V_-6bW#!7tGj&c*4-$M57-*hd-E?=co7O|6O4QNh`_%NZI~1>-)ss#W zD(fuX{E};QUuuiQPgU9*rd(HF`|98M5CknkqsReH-g^$O$jUcRV z$XYS;sEqZqsH~$)AAX5jc|&vQM8FxB^-}l1Btw|2E*s}Fhco)b{R@yS$dB00?jPFg zyf|iS#2t=I&EarIg14qVBaV|RU!2aKVwRTa;+(kr_i8V4j>@mwm1Z0di~Gy?zscI0 zliugdM&bVw?H_=Att^gH!Vwc)ppuUHK9+!Aj)l}BH90vM!ga1=i2Jl-5Dn@=;{RW+ zFRpztHyeh3aNqCpo7(0S`Wvj_q62jJisXC^?puR{p^hpyVt%#IQlgXao5Zt;Bf=9; z-V*XKPj0`Xr7>t8(&PQ_8!fw>Owj?vnyofJ5e}*W zGAK}11xmNpDMAwCxJD@;JkH-=r>k%uErMMAmV*iQ-M!umt->O&aX8Tx&|7uLHnxFC zXbrI0as8BhZmy_RpkfsVnurL}8=R3mct(f9W&kG5&~{RK(E?XvY$BP5;47&hE!pPY z@HJ(oemEWYNwi~H?De_W$DFK3CG<*J16Mbr9UN=k_V*jQ zrekHLIhQL+vC4v49l@53DGvE{(gFJIwlR3`-^CHlIU?Z{^={~a(9(|9O20&gI^5a#KxK`q9n+XG$h;M@5KkN{yc!vX;Sn3k#%&!PBQA6Ms?G3@ z|7^|EmX9x7&0DxEueZb0n~ukZqp{->=!5G4c@GU5@;FLfWKwS2eL|JUv2v(=PhgxkC#j zeG@y+7EpE~#^VmH3d11zijmLHE4j-!Q$t*t5iljTGwEAQ71u-TGC-D_2I#vkD!0uw zgyMDc<(C!_f7~j~o%b%yGGf-oZ^>@FIB;Wlht#S=#}BKwJ4NnI0?xBIzm>Vtw_+H%E4SV=7aSJ> z9=IB-S~O()PH*(%>9Cf6^ZGRv^(^}@n=f?!U1b6HcH7%Kq|U?rZ13#YdEs@KAA=s! zTjUzFjlT{*iqMvXaq)tyLXmi$^&M*!Gx-LCTg?8Yi-47zQyeM+@f^h}PQ^+TL@ipguE&o-Lk;`IM4L?cXlk^F{c0q9Se}r6a46S#t zyz3Cy>!i}_LM~TUZEjfU@MbpV&Tr}OUIaPBt8PfOu{r^>1^28rgebZXZ~gkj!Sw52 zpLwvSjBTDfGA-dh^x@|u=Dh|SK-XyV)?^U@|8hQTu4Pca>;*s` z3+^fIyk{+Y)z2x+|H%^WUo(Fn6DAbeDw(!^zC?Gz$#B>CZSfDP$9k`=ar2#WOs6P8 zD+sdb2`E8gdlQiso1kglP3*V_#d2!*7~IpC(2%c>nqFXXwO* z$)9g=m!gy}{IKixxZ-c^=_~VM_TfYP9S?ddALTI|vI1KwUCOM_6OSU^BwgkS1+?O# zhS+PUwL<=U0Q{;8=Z~Bfy1HkY#ruuGSTn@dxtR``!ghOZ8KX~7Yfk-BKi%w|l`#pL zs=8O}$ZzdUS@d>hjAfoa;y81qilwemyUt}uXSja5bWJ)<*AvB)798gXske3)oMPtu z4{vfkt+)Ys`-}H6X!rbyt8UnXFSXszFeXWnj*KPbLAp>0vv8l}n;R?kh4ZU_PNjv7 zpmkj%dBQsVY0IcfYMZkEPTwCbHw!@+)@N1F?DYB8wY;XR!n{hNq1G#|>lc`42mmgS zt>@Td1$8E#r1 ztn7tK@6Xt46yc($wrwjs>?M;-GSb0Rg6{JZ2PABFz;Wa7trE}yBi>V7b_SFw(vu4g zb^#nNx!E*z1@>^wHvK;J;&WxGw8~UU$e}c5{wpFuRk!a5awAwk)`Dfe+ij0!$BR-R z5!5>Q4yO<1lCxoHdwwZ<(Eti7LQcS9#^LV+eOF+8FTm^e!8mr=ru~v~q&dqx()-HP0ofR1nUq6UKV1!d;;!=NJ>Z<`p;q&| z*i~PD{~Z2INb%foVS-De4a&SlLV0a%!7auB5dq z-gX@SF%y@p(7-p25`OYe_2ET=lR|AE{N868Dy}|G(Dn|k#*69h ze(e0lI@=qQA}{?d#GkQf*p`|18485F5gCs_-3KTj^P{(o9hcSc3O`~lr7p{v1+k{s zs$LtpW>;Aq`-FY;&bN6a({A3fs(LHLZ%1GMqH#&Ei(|E)C(rdkb)!SCfue1ekoc#wQS6SegDJWIejlBG6RM@Rl=y6SQ+A`!@`iQSdYxLZAXK(kAmwa- z(ijZc{DrxS_IQ9m{9%!?=t}l`#CF!*%{yUvsp9fqNlDpJ7)CmXOLI-Kd6vBTXL&fF z=!m9UnzP8wF9EfTo~4U$-cj4%awP88B~k;u(t%Gg7xhpb_;%Mv``d@m)xeZ<-9#2! z4)dsz^;Z*0NI49(@C+ADs6d{1>~C_yX{NonH|hk@uAF)Fn^jdKOqrTJ;>>?xz&jf$ z|HUO5@(X$8H2ibvsYHC5xhFS6(DwnnZcTna-oPO=JPYkxOs|Tri1^X+8z&r+#HO|= zspo^<72RvUcdKsa;!zlDyVr+_6~aTDzeqy^k6r+;_h^YfQ1~w2gxn#Gekpe?a{i-y zC_g{i^cr%bty{U}mXyof`&!oFq8B;isv*u%8X}OWh#uVBQ?i`mxuIJ9s$jDp2WK6& zD+`JkrMEr^NjqJ?YuUwfmtUB&^ZTg%56pLdm%2so5b?gO9s&+ro=#Nh-ItMSsv3iy zl<+U2^aobh?gvHiVwVxdpMt0K%ie&1Q#m4nr8MFRoS0+GiNI0-IBjevE)`Z~^FFiJ zb0?`KX?Mi!O1!}LFYMDS6tbDFlhSag^O@_C@*Xn5c;GD+eeEy%)P{7lt8Y9T?<+G`B|%O zO7=HYz^uM*k6PMyav#|HJN-R8Vr1;)P0_J5OBzT1NIHzA%DF`sazON!3x5dRx|W7X zyv^TIP;C0_l2sxDiB}hIe_z1ZkQ+cTjr>TF}>f<}9)3;wWBe9j}8-Yu-JFf0oGpM3P)TdvrWYD>G6z^J`B{F!|l|x9QQl zU%wCO{d)?y{6C#WNW96g;DvDG?LVScu}1}rHatdB$SRNUp0fa5J8|)qQ`E)`)-fxh zUA>P*!)TPYVoSy z=G9_$l45kGR%7L&I8ANoes7mPcu|~01vJZMGy5AJ{b=wTyY=~?iErzGwP|UxQljrc9&bS%y zyFm*PRbU_f8@;gHi+2sA-LnD!`|RMMH|#|6T~J4?8h9qyn*G?gFNMtd>S~-XJelvU zhvE11_PRXyOT_XOzEBW$ESx0*z7T0A4-BJxSw$`4eJ3d6E4BSfxYpc3U{SvPZK{M} z0{FLd5kIlbU6T_+?E}k+pdV1tT4Gfls9$Pwz*%Mgwou)TCjensnW!;~qjg3^%4q z&-FAB@ax%zRC%C3RSX0+do)@1XVY0Ta30qq|8t)UbQlnf>|jw_cL#7`s`Qcc!STNy zzga=demEgQ!1u3SB?-=ArnGCCP6`H|S-w%q+9G5$z~TwdkLCFPGS9R{4PAM)kWnGq zm-&f5_(n!NeecM_sB+daJ`42m{r>WIfTBh?JAENVix&<*HH772(Rb+&k*rXAX%{4) zqF0RRq^y)@o3f|Y(YNVCNQ%C|gAqt+YR#fIDDf-(h^gEC>aX?Z-F$)q#BD^$dbCgW zXrBr%F~LKlE^n1ALGrpBO?Hc@)yDkU^qSmDo0M$$0U!|+JbiwN+HRk03qP{)_SVLj zAB%s9Tg~SY<<5j;0&mnkMlb@PxJV)Qi?o5dST8W0CtY{GL`*5eW!^+0j5)R~?b_}(06@ zZ|e6rd{8ikzJRKfxSpjGUb2&fQ~-J&h&taS)`Da`9Zr=R7?OUn)lsSg-Mrj$H=FwO z5xkW3h)G$k&_i&eRlzhy`OIH?PTw5%uL9SG@b$@BpALhHpi8H7-{!jc$fhp{Xkx*U zUEk#bkD$&bE{LH{mL!@zvl}cZKH>AN!oM%zFm~eG%Sr{gUU+2kfRQ)c2qRGyIf`^-|oST`7g)ar({Q{R~ic5Fju&MyP`I;0s<4Oy_s_-(hjg~ zsrK_aIqIy?tCQvxnle8pNJYC*WT&2&Xh#|1=k#z^3t?{ce&vF^!H$Lmi- z={mnsZ#lGAp>@BjCUUQ7FvoAT&tgqoqKaoB*gtKFRd;d^kavhm(1SXVBXpb?V)!px zMO(dHXP#BnXX(<4%)n73=1l;aVx3**{n$qM(j?tYb=|SSI977TZQ3Q>Ig|zNZ6hBu z`bm2iO$1-<{VWavy3qj+Mi^@MO%rNQIa2W7j8uBna8EV>%Ip z2R1vwXO z{@Y8YuaMGbhl_$=+fqdUC*Xwk%lmJA-PtB)s`(c182jr(jTAd5F;Ww={R7VX(k}^a zFtg8!+|c=2?AnXsbhXBQC9%o{9H-iQ%w|d^i3cKbPKDi};c3BD|?Lm0eaj9C)1Jb=YPaMxT^<^?{8KeG)uaazl94wV z(i_vlFZI7sGnWho6A|8=xExM{RWEjuIj>-0x}LCbEGVNJM`l(FW(ow|%E93W!e2n7 zu^)xL)Qi(T^7 znvlaweha{IVN?lr&ULgP(OWTUQ7GO}uL*13`i)H6T2qq;4Saxqu%3Hpf^_dyNfMR!4{T*r2&KYF2ow6Vyi6=it9ggDp3 zeOESNq8_K~5`ixRj$kK((CF^<#ZdlB_J*(%@5N^`#}|@&x3ktG1r%>VjvbMcZ8ZI8 z##*pFjE$ftBiqyHBsjaY-wv@ISzU`^g+n1(*hyzk*%sw^o4;?D>5l~Oqqai*cOu0I zPQ_dEh(o`4Jl65Wj7e@g4RLI`I1#;2j`a!UtkPm@d}?=%gQK)zpTUA}&q*GVB7c-= zJcHGB!Q)<&4eHmWXsi?oTgbmI_?y=t@K7+7*}x#SSr2(2Psb6(si+Gah2U-CG5Ll> z*Qjpk_GaJd_I;)o;sHWP<$Hs&fIXV80fsli+v~|Wnu3;TmHFJY2y>>87;gpH&cn~g zekaid{KEba?9_THlG|7$o-PBPkPU}Kv6QjKk-{BU9f%fRgd|w$KUqEg3Z#JjDAc%E zd9jw#fNj2LMv4@azk*}KS4J?aJqjl9V4;hbOGLNc@VLZ2((fld2y>)lez;fG>}qkk z<%;YSwdJYhceiVodiPtI25U=b7;|pSTx9oyjkqcYA&@0n9%weoPe%#75SN^%Izva& z+3~~io6*c$za#Fq1M=XGC8sP^^pbA79xiKm+ogWm6d(hPS8;81d-^PP=H0Yd@G6ac z)u5ILl*LKk5J%y3no@&W!<%=;%PTqFX5WrWC{C_-y1hgd3;4>K&bg6xZ}~hL8S;pz zT8QNOp@+(|D_2`3?YZBp$Af?D2?7l4sk6`#KdLN}UDhNKf(eQaHa$E->jSu7Ytn;z zTIU9KJ?ko2$eC}NJJUz!H_~RLpD@JJz%6tI5fs za;>0^klu(53Y^>A&;@{QO=Z-PRjsgW?;OgxJXJGVa=(Tv`(ghd8m)%JpR%@sBs8q) zZ2RR}AJYP{2vgjU_FDADHOG`mdeXYkJsgWMhWOP4&DiEa(0t~C!oS?4*Yxs$x=?!0 zkID6_p|88pQ1G|)Ew0l|nDn9zM&R{E9V2k$el-7$y$i=KF&zJyaVh455Y z`V)wWpW9a8jIZG5dW{b~pOvTS==xxl^6Fl(+-kD71Aq1&LN9VCJc{Ep)2XWPB1yln zHke;Mfgw_$!j{D=iiLP{8G+j4#db=*`;gH-*R%! z7EksHYD5-Ky*3CSEUkTC=TmY7+c}#$xH0nEE6PCtUHVAZt!*zC@d<@shZ1snqV(W; zsW;SJHP_SLUPyH-(ia1MIdhtS&dF5Qzf9zu#G`-})dw9)A$o+35B^(Q>ux{WUR@wF zKGkoRpZV%SvuBYpY*;u>HV)KGn};E81l*-*Wx_d ztQ2$_DLF`Vk$5Jp-#4$G-vQA?q04aL9m=q?Gty!*C6wei@v9#|A(jEDU!zR@{zyuD znDMrIvHTZ*olmXre_yT2;@q#J-CEP_YHUeUOSimny1?IO_c65>v=a5I7S+>7jUU8- zxqUzNdq>VKwPaZrt%SUMG!`I%YtdU!sthvaR2v^uVxkYUdeBkjAv;q$QCZm|Wsr3Y2zIoB>0hT|!91xGd z(ymlKpdn*?#~(a{PsKwAzm-#(+E+uOYPgv+PXBuQ=E?2C>ObHF%JL&Cl|mh#VEJVn^N?|MY-P$;0v!xjdE)5vj(1 zu~x5VJZv(GT8v%6I&gRaKNbrR--XI{6Ms4O0T_xUgbXdEL4S#RQS=lPX}46=zE?Xf z;*}gCugy~B=|wBg%cb!@EfFgz?-hbLgfXRqk$dG5qy+Tld9rK6N?XhKYHa&g{2m9{ zIzr-?1HmB&^yfMZ6+>G~bEE^%Iz=6KQZ?yC$E_^XPe?bvdy;3`Mn9%byi?XeX0(Vk zO4sYIC^3s%M_fhQ-z3bOWA5=l-}@{NVY}~WO#*7_audQy%k;!c*bYU9+wxH`4^%Co z)~-{!p3sz=gY8dpt(YqmGEg+g!2FZp;~1i3S@+Le`x@0VX`t>_HJq8h>@Ql?i);Cf zd$J9T_zMw`ZD`xx*jJ$2D`e?hi}etpuBY5>j`Y05>A@f_gP4898G?@`n*NOqFpmV^ zWJ?9OyM8ik*hFDZM^4D`87<&CVPIevoJl&7Ki&%>L+_3s;dN6yA%f+84GbNTj5PG< zk1aA6Puox$v!jK;8Wu`WJ|yfQpI^+n_04bE*@4HaUPAm&B}BXThs9HceXZhCo_fzS z%Y4nogn^JUpm2q}_KJiSj9JU+ztJW`CNZ<;;IG0(75Wt=dkCAEq$8lU$;@6?_P8|R z^bMyG1LMFcH+cZr$P%y_{*zmeX*me@Dac|rLo9BU6-oGrc&WBkiMG=_pvV?fk{`l2>y&YkbN&A5tBP^quZ24n3Xzv>peoyW z3%kmLk-5`KC9Zn|skbgb_ZlK`x+9!pV*0D;Iy(j?ZzcPpJ&a`IaLManzW}_vVD{H0 zdnlWgwncTL@4193p;DZc9FZOtqrEOdmz6QtW@c~QTKnt!ro1ULfWbRwzGM4zb}llc zr|IhW$%55VLZ|blJ=X>R{e_@>m|; zs6B%d?0~vIV#0o2-JmLXDBAGxu-T*i^50OQl*zFf)XJ14oBw)?iEV>SS{Bg%^J*;g zQX_eDP_059r}t-+KtT#SrwRM4f1}|WsFQyOzWsqQ{2S~y9`Hh7*}usdnaik;pbsET zn^fUZ49g(e$v@gf?tu^Z*Ln#g1v2SL-t`@u=~acrJT&n4ojb_Z2y4lTP&a#rXiNeD zS3Lw+!hWFfAeJ>__HZ6ON@$HbIJxmPy#Ah2+00w-s_~Tarh%km z%Z#l8t_P@i-A&RiXKbwH9tg^?Jm%G^q8cdA8U`EHPC;BzQOYfoFgcUkPa|*3v-j)o za}F3J0^UMb+dk0VTI+|LZ7x4AZdfBR?~~q{A+{ja_^zCJt~o9q_pF7pD|#51oivk# zdpROOlD=9pV>*=>AikQM_oY^XD81XMW8^ElWr!8pOOR;Fjh$+GiU`0>Zki7hpH(Xl zL-ZqB^ZCir4Xn1Y;<}BlVNFdfSd4)lMyG2hXgQF*Is;8_Rg`PDoff^@;Pm6v;b7fg z*-Z~cm29p;)Pj6o8Hm8$(ERtyBPVYRk6w@NZNYM?&7vyi=-qiKa`xcW21XTE?65#N z`*^GMC|KQiAV?OR6*VL|Q){y@kJy^>^bSBcU{g~_GouB29g1P%IH3Hj*2h@d@bp?|gG4xU0s)EQe}m##c^c(5IN*xh z7n9JpjB}PVo6$V`H;tz;f>${?4g7avP;Ogz17`w72A&eNVTM-b=jI{eD3qymb?^7X z`A`S&iWC08+1>;F8X?t(f~gWwnvX!}gCg}+?ZhD#<(X$52)!gHYiv`*YalpRN(F#D zR<;vC&`xFMtz?iDmUYk$9o!!dfwz`hoG_64qv_q$R?&Dk!}O%0am1#yWX0)@e3S%EdM64RSrU<(9xFY@F|hY!Y>imVzX#o(qIzD?t}BUzPoa_{ z=(P{mc@Hh{P?l%4aG1P`YG9&rxbLA+H1fpv@SM!pJZ;}|)619NZuR`G)4MFE`#$@7 z`lwHW+n*;NVp#(drKiEgfD_93-ztk^XPOqeXq$-dHP^p={O7Wi;V!8uSzDU59&kdI zRIs|Rrim5cNuPu_@)~XCct!w~)~>J|C_BpG{Zl7!mGSL3RkKA~2kCqIypa_5i{<%j+jdH zGS!waBtL#y-QcPYo$(|sm;TNvjNCj14+owI|-CA04zVd5tjymZtnphp?5 z+B%?$$n@lVGZa)!X0|mtqryuM20dnL5)$!NsxHXcj2>%`rXA~7l|k;UhZ-YVyV`1A zK~Nj+mv@&M-0uxATrMgP%NEWkrN;wXvKLlHD;Mfci^vXf#dcj{je-LeOlE72YKzCc zfM6zCDWeOKB8eg^gCI0gOsTqA^?p$kbLyAZG5$AC`xJ!7o#im!o{t~@p?)kDX3H0A zNek!Oh~s3!5|^Jk;P0u!n45R5pZTKGL?CS&4?B~If2G1+)L;u-lYTief zIwGSMdvFUpRhK|P(sE}CPVBfrGMhmv^sfeJ2Ymj#p#1GZifM4o5qNl7Jt_f8O(Yi`51)E^V66elRFB*F9{Y6e!sF#>p7}{VWxUNznc@LJ!jy9s!LBuO{HSNXkJwETc9%KD>mmSW^ zjyc}PyU%D8y5>w`!~IA>4@?1z#Yc`DfnnOD99L5RX+638CuCzLK0etbeFd?2&Wz%{ z+TFAfzpF>&CJbl|61!yPgf?&Qvw|Ry^80q%YoDM%lzz)F#C5@!w;+UTj`Bp+NRlXvt^2 zSA}mJLBTGiLq3@wA$`i7`H~*sMZk}Z?Rd$8zn;Lcc%)m88roKf@y?&nh z480!nx$z$dw?!~m<8VkStABvtzU`$f1oj}kwkm|g0v3lT^bP(?uiX3{J{hrK4c{uo zuckh#5$Vi5uILUcT^Mr4=idFUN+R(>T4MKEUCTp*J-)gll@z{x*kw)XTb` z3ZdM)%wyZ_Lu>z>>1_zri#^*KV6^gkGrr@If_38nq8X5vC>*T)IqV(yME2L;1O9i< z-#hZ3Bfb2epWrO18X+`yzVVA?OI5JI6Z%|rWb^|U#~HeE4s=dL3cOOBdRrg`L3|Gr zu04M@@Ef7NhVZF4=DD{w$$^2E_IEiSlKF?6;g=KryZV$0q~mbZok|7vbN5x~^`QNO zKPvT(AbGd-;(3?*Mk*a7O$~W@!_vXz*8oiG!{xaV#Zxb69OS5|W-0l(XHpiyDX|zf zQrctK@!=BB_L5nq)FBE~>u%Dp^mvSI@o;KVV;ef30$Y#4Eak}>p1_>ZHW(>d<|lL7Os^Z`VG2b^ftgm4q(Rk!9bvx9im6AU8JWq-SdYtR|42Qmx*8JBKQ5W5J6BPCB;VMAet#rDr_~bqN zcJJrRxaPZ>!MCG-EhJ~lL|x!A^;e9f{Zs5UZEkK+sKtma@miPul=h`W0)r+We zTNTv>Pi`ha)G#n}6#DLtr2i{2|MRmkuXGnP1sIjJptz^+86oIYdyc>aTSOD6S*?cV%Zfz3%sHPI7vy)#jd8@lV_|sbC6R? zD=5Jz1L}(Wh|e{H(Y^?55i1;-QiYl6((sW-_Q2rAHTvHMD@4eSn1I(W&cD9IVY`F} z_9@`ff?0b`&rE+5NB__-_ul)0r{%tisFQJnsU!K z&uT!;-x}n9-ELF1@Quti)6ssHax+)3Z}BvzSkm6mef?8GcI05DclLJ9B$L22GA|oG z?hY`f4+7nOnfO_xUNDMM3wv|gRAV~kr(7b118uy$fz;T zn?cr5Yl5(BU(pjmG)jp@JTxRT*&R03Sm<30U+IsLg7|qjqiRKzxG&4M+!!s7v4Ik> zG+%EtAFQ#(+T)3#j%g8C>lfZFvbNzZ5(oCVoF&TdJ)lSwx2>E#6!j|rTb z7wZhD=^pX%0qK|0YJ?HFSSeY3H^rG!kM%aI;v#&(Pu7TehmuENxCE_9(K;T4ygf42 z&})q?a--$iZ|*8KV`o>84u&iGbDE_I)I84p@#?>`YW=yrYMQw1%cA(|uN7W0yW#wr zKZ@42C3VW`w^y|;sKlHb?smso?4tQzNj`I;Z{41)z5NUyHP9X6jdM1Q)eKE3aFp{1z8w&%7Sonb8K=mn@M#x5w)RbrL`y^BLlW1 zToi5x900qTIB3(~q3crO4;~s0!!bD>$pD}nFRvvhaw~0f2bDTMcRpa1xcuzq%my!#&k0upUaD5`(9q zw1tS-NSOf)HS<*3Isl()@+4nDXbtqkzG42=J&Yzezn(GG6ua%+Bg3gkesS$b6B6Ay zeYq~3VF2>-pvB=!)1M$h`kv_G*Lq@NAp1cam?A8EAG&{q%DF z0m~!Mf&4GXC|QFGPkxG-!A6!_#J&W~aE=|cM)Mx`A2C91J*h2OvNPFQ;@nEhEToN} zI(-ZXOK$l#^TbZeXjLf8g65GY>u-V-j6-j|7C)#st|QU>PoB}~GZKe?sGP__9o}zs zcA`Tqx&`>!o%QU{iKUh=mu5<@UixqX4GB3f9FPr@hlc-m7z&F>wc^HssPNPmYZH*D z>&wL_P#VVX{+)vl5Ietm2rBs>^Va@+d$2qybW2$|>Y@FJksN0c!^a=~;4$xN6VK#t&MP za~t=MO|HrxIsS2RJ2@aVU0pYY=YjmuItm78QF7 zfUhW6O52A&*mH8>KKJk6pKcZ^a?`Wc(cY#-z7-n4NC%FSsEGvXl_3yycWuYrZiL*9 zrb01&e|as2G1`Ll#9R0}=HlDHW45;v+FKw+hlT6SB4STEyXV>0KaG|S^!$Qx@< zDMb2?5zU8kayXa+HXcsi@iu`&9dGK=R%#X3oQ={#4vx22*6po%nI?TP3#mP_Y$=Df zXrDcom_qww8HHw2N(QBak+Y+j0RB`;@8p~GAPEuKs_eX%67TCVfAW@Br)XkUSxD}| z9Fad&GK$?%Y@uTdkK9bwZ?0s1olniY%(-{>Q=;O<>;UiSPpi#h7e=bw55>NXxA(p% z^D<3RL7^q@^!@M?odv%Y?Xih2!kr*LQ*)GANZH?(<$ooa648GG)xd9~+=5PUpzLxP z#fiZFbJ9Fqc-@Qw=iMlZ{FYde6Ca*nnK29>OfcJhM4Xc=DL>{`EHBSfM~r10wP1O?B)3`MfhMzTPd%#=Tb`?{37R6U zjp4|!Js<2kLzwqhrZRD$kNRZeDmggeT2|h~x`Lv=@T_hpKH|Ab3t zDb$plcVi@q7oGMM7Ak~kXQjEKt!;|o&+a;>CJRa;=_%j2zaKHv0y7y=31v0o_^qB} z%dMXQ0T{RU&o{0ZJb(V^PP*FHxbl(Neel`^`DZ^ubf#-1sA&uPk9e%diNwA!n{|cM zQHE@N9sXY}fT@%``>m+28`;sIy>8EjiO4R$mV8v;RvP|oQw-= zjch)iaVz*P`q8SYwDggxIYoesY)XbcH|TP!(Z&k#SG zpSF|mJ&K5J?(L$@JW3r)9}z7o{{OXe|0m^6^}Fnhe8*Y)BUOJT@YHW?B|iXz!So8p zQETR-djGzLWr%omr$QY7XXqtw4&3Wa>K+6tpaRnjai>OObHjgUIBoS^uAEINmd^s{ z&J2pQ$Xye7tPoQ=U5ggFTqh@w=8TdH@!ep#k|^|SN8_0y_Uj7!#?uXf&E;0Ek6;E# zzV4iNj~7h)IJ+bV5l$uGsmeVw5`awUBd*OkP|+wRp>ex;pr<~fJoqCHM;!*mB@1`- zJW84jf_Y8Kr>fnE3;A!POf^~`D+sF--mEHoJ3b1nEagwomZ zwSV^CPmT!)&`*p8d8H?REsbIqcf)%GW>1Zx`;w$&5A*T3RkSDFsp5a`PD1^lPnq zEY|;ltnkpes<7Rca#k4oK$!pyW54O{&Zva;h<@md=?iKZ&2=y^pX&_T|-I9W+Jk#fkpL*DE6_9=8J2n zTQxO^9t!OqdW9pc6co`^dZr#*`ZRcprHw$A2y=11I9RYW9d&TS@lQgpf{p1}lkAuZ z?HrLD^J@I-IDJyxsAA%@y_mueiGAa&r~?oDYD_xujVCoMvoSZ9UY>e$blfeo)FHWH ztwu)C-}mKz5Saf35DeMu0Zx=dBj1UX|HhuPqTk)59jx*;osrFGN4U=@jef^H8SLx7 z_O1ClQ%jM3At4M8xLaNjK4)Z0|4eR+Q}eyr1KwqADqKVbkG>Ttzy`|C?v7{87eINj zv5`i)^skgDqt-npJtC8aOPz6V(gu?xb4()}*uS9nit(#{5{be>!-#PXw@~u&aE_P- zElMXSNHMCU%Tr@Ai3pa9@`1c-*;`L*YBWipk(FZ&z#2|Kq{q?lfl1bA>^e^oSK&G5 z>Aw~gCn73#B&e}X*@WZQ|Jp}&>5jC24dnAPhWQmGXW#qG4RbOzs4jb2cW1B9T=wM94=rV+|5XF z6ChHofC7dzU3y|oT6m9HDpWNP(tCHaJ<=g@mh*eAei(1w3fpk2cOqM!##l63vUe}L zS()nMt{1uGPC`+x5+mmA;n#_e>tVAKp7!Xyt|sI%@3w+R;AX-?lcMCkTMiG}?=%Pp zT?OsdBbZ>)Wc}D!mZj2xyNL&lYwCD=ZN*=XK2n=E6Ct%4?LBtQ>axU);>xHTf@)-X z{(APo*gW^qXG*WYw_4uzbh9NrqeJgX{`c~%#Oj|w%2c<`lNyuWJL7^2-vvj>z1`hV z7;n)YJNSK)p(WV=xVLm#k;$T`xaFH|=L$D=Z z#i2Wz$f#0HP2;4|&BMPpr_Qxyw@5pcd;+xeH%QbevL4t-`n9`*w$J!}_d;}(s@g|! z4!EHm9&C7|dD{5_0oF|?4noB9K}c?(Tg(1Sqz-DQ1!n%k0Tg-pwbX0>Vq}}fJX|m5?GTO&r2M)@B$Gtyr4#D)i&hi}(ViqpA=jeadOhhfq==y1 z_nnAXzh+WOq4sVoNiH9KCvP5s8I3TIiY#R*S@&k}dhFu86;Eoi4(cED-_iQz+-kSx z)l}R5s}~;k&6+1Xcqu>qOX-T(j%>GX(J}c;(|PL66-pLY(kw)tmW~vguxCFuTGdJa zmkWD6rt(@0DnLQu8#A?qo#V?ywCCV=4nzn!Sc`KR2v`K*(1h^x5?v3=nrTFt+{UH? zC(ZmJ`L|5GiZr%~Ksg|L3g<+%j)aRwaEQ@7TOw#uf^Zu+|HD-N>~^+u*q>V`+pKL}0O z7=rn*~)bka&kpXRMceFRuuAX83_m|D*+RT6=k z`B}o1;F7MZ+G2$8jqQK|ST3=D*fNJ=A{5H)`vi$rdd9s~)<8ERffVa{=| z)0z*yWZ{Ka#;4+o6)G*xe)(?&nw$T>Qv?%hbn=&V?1u@gEF9Jx))FV}%K8va*vvAp zDwQU`?aZ@&A8;ow%_uEK+HzBMC}tlj(t$Z*%MJon!AoKr-FyFGVA9;Z5#pe1bcsa| z7z@NdycpF0_z_bd)^tL>MHROYJ7CCKk5a_3S@XX+>BCjd-Hnd+ZlYIZ3(V7NWxmdj zAheMk582I)R%MhG;VB7#{`5VrD6M0W4rt4%w3xwmDI-{$=xYL16?14=8&F%?_By8! zRuZ!ieJc@k5Mf3b&y!37af4M4>5|uL@}nND9uO|J%9GKAD{6;=M^w<-`+uOXC01sc zi@$gnbop7hR&HW=lf1sy$MF+H@4N0BGxw%D7QmHFgS?sIg`J}`#nu1X1eCn^rzDi* zqSIaz;iKzY!Zt}S^S|5jehB&HykJiC%KUuQT8c@U7OxV)(G#^9CR#vcZBqeI`p?@# z5OMcLh7Ve%v%%90g zxxjHF(xy>$7}|R?nT|J2v4g6{C9R8_=U~@$r$3$0Irygeie>`WP*X+Yk@y>x`1&}U zbt<%UMG4w>TXXi7N~a+?1+RePPxUj9+Y3AAuRON07j){~gq{l&WDiuI8Z#RVM7b zm9#QJF1!o#S?}5(!!LIPUT9eJ5l}xS#PsZIe2I`FlKs3$#;?&5m^`I`{OEZu=l`MW zt)rU!|G#lU7!sqDkg?IID5->Ogj2x+F#rW=M5IG{0~|1VAc7zzp{Ss2G)fIdskF+Z zOBl^yz!>$r-k5H*`E6w!@rah8YE+dCX;Zz8 zWI8_aKtEGA*dAbfW*FzeA_4YI#RH}2Br!EIO*ZW~9KF;Te5Q_y=@Mc1T-H&Wi2>C5 z$%eUttvP2-);@TQw;`+X*=ASxKc2dbEbW1oD)};+G`31Q;3M1Utdp@v0|dMsW~2FX zlh8q8z9_I=@}~DTgPJH85`&S~37*P~Q=s-ON8|9>LNMF7#%6$rf73b`z@>)#l~j19 zw#v!#M-3C{a9RTqaWj=cOL<0=WHCSKITHTDRq(S0^H;zq)c_2_**wa~>B(=bHjRS+F;DI4-B_viTW z>Pr+q?gm_30g^jhVyIuiwRZ=sG5F$cuz)he(3JPd_VR@4 z&}tz{7a(t_Y$^I-pDb#;|6@?+_j`FY85=KouCHUlM-_sVZZ&!v(ZlMPtWKi74DuEW zx4*J+xq(tCoQ!-ejt-a%v){b@%t}=bUCBB=H@^lF^TA^AeIQNye9TgvsTb`)yr)Q6DP?t^T_?{u3t&zt61qZy6?$CT?e|;Q^cz+bh+@rJJ_GxFuJGD+TAyG8=^s-9X9q-IrP?1fbl%LLxl# z>TG68p`vi%qi5>cXCiye^xC$D)}AF9;H(|EtwNF|Ff_VV8S2p;|l6 z1H;=v?0Nu5PC*F&8hbg{yGXR}snuKZZ&pC@79JI&hRB3HWs8BjWFv;&PYtFsxT!W%*x;j2SRJuRVCl+bFy zCg#cdBR3sF>z>ym5Lo9kVp_wLy7_ zHEx%l4Sc;F878i|hoAZ;{9>K&4Nr04yjUn1KM4568=adnwc(dS?C@JE#P^GT*1!o9 zn@4qlPwy6keZ0Y4_|eF(jWtIPJY68N#O*K6QwC(5DZW;RJooS^Vp-zdnx#1#N8o`(co@gHZ%4!^DfqXsJeX&vYsnf?;X zI8|%i7q1SE+pV$m%@C#Ml8Ock8!um3KVz!&2`;Rkb9|kRZI$gFdsK@sx5yTS$~G2v+7y1ltE1yhHGfZ}x{_&! zFq=+1k(01+9j+CfP12HSW`~Ncwfzv@d94vjqpMzNe8P*i_@~uxVRD8JE_c74G|J;X ze@waP+7%9A{`aUKw0$ApK)re1IRdLA)eY+vY=BcFSN zi?S5|5N6P(&jIFut?l!@ncHNIqJR8jY4sC(`h5=%a|0!zGLw`E@>ZUGpRj(*F(lZ} zGbw1Bj`2UQ<78)SWLnFUPLj(Bs|sJ;&1%uC&n#GSH<>?zKAO8s_YS(|M?(0{Ro0 zYYMQX$Dkg)ugv{pHLUMPFPzgs=2%Ch)D3eyMEfQnLQdZ9>~#*ri98Jc)TeC7X|pl# zBIHOSu3Xl=c|jQQ zqX<yX4NuzqpUD8!GS6*0#bZMuM*$9_cJ)M*ObX_7=2dAHK-!mgOaqIN^?UZ|- z@*qCg6V%8)e(Uz%H8(CrEZ47ZS869cdUu#FRu~7sOe?i>4LkL#sMQ2X^LPv~x`jW; z7H3m_&!O>oP8+}YC($7q;L&QT9gI4Ts6Sg=Rj)(3iV(^Y2`MXjf35+J4$fUcW_g?a zv6++%;5dXN)e49$nAznU$l=6;m#L$}B)>Xr?um?v9|9DXl(-%IZDrm+OQ#-{Vz)0g z?@IMv$rfhT68;_@C(IrgO7eq@r#DQ{c-V7*KWmFza^&v`A~q6hHpEG@XBvs-zT~ai z{5v=$*WS#`y+S2d(&8Q6Vc;Q#fH{qupCemaYn#~arzzV-lb|kg7e}-8kMi)t?tQa3 z3o3uAW!Nm^D$4yvg)%FTQa77dZHer%)tu0`F^2ysvC(|JbreGI)tYAyjma#|AQL9+QfgE9~*NYY$l3~$|1Py*M&uG+VPMMc(Tv6h)O14=;- zZMKd$OHs7M41Ij7>}CB@MR92Zqmqk@%jfL;R;sND7is@{KZ<8R>6gPL^1cr)2xrB9Vh|( zia(J}xbBorGk}i|3xevEAHw!efe>=65QnegpI&2MXdB)(vX1LMfY!U^>0Eqqu@74@k&Mxy%+2`y&5T_h&*O>>{e)AFce-j+uu(4 zLzhq_#exNRaS+{(j%=r6J1mzUxg(4+iW@pdeK^%oPue+crF+I~fp%V3=YVs#jm|N= zC?qUgJ*k*`pVV~n;2V+GRBK1n=sbMt96@qwr62(7>@WDHE+Ke*MLKM(HsapI?`OtJ zRJq?%zB+fIOzr|>!sAzbs06v5C& zA{HBu!cn_3`EXoIm)ECkTO#ew3pI!|;~(%+t@QRLtnEBPX2>4~m4ZC!VZz7|JOivg z9p^Ycos67$j9%KUvFSz9Bfn!2a1l3}<0s>2?Ifc~Y1g>8;A(b7DN*$++T1}Re3L9* z_!faxua?&*W!$3AQB;oJ7h^sMnH?Ube z*6Gd1OYGfWa7l3{t5)&qB}qZ19HDEomqJ3kB&x@OhcV6()O6re(ZqYvTdi`5i6{wq zIjqPWZ<~R;HMraS*D-`c$TGS3degavq_v6|uF1$PDRWQLxF@z}vx;{X+l5(MBL~sz z7f%g>b-@qTZW@(9tj^(A9#UCF2ZdSHr~1mximYckLcCg#jFsi&@_!*nIrS>f#5XA4 zlZpHy)6xc2so2NuO!M+9hx}E_O;Sl=!|miDpMUu!d2HJ=k(f7PYP#LJ~Q~0D-BouL;sPLE z^JdD)sK|LP=G90E1J5MkZWbwI5kwp8FckjB=9bhaT@PwW{Ro}D-h9GLYz?7Ub(I_t z_D4czYl5)B;TMAbYQAfmf{|UCq_%T*kHDQAXT_6bum%%dT>;uaW77q6HC|q}?mM4e zb2Pm;IYwL;d^{gTG*&h*!!tj(KEW$;URgwW=jEVYB)(IHMVC8MAeuQWT7@YgbZXYe zMCh;3_<6H%?d?!?3(4Jo1LHNU&fB*F*d;2S(gtbo1XO*&rjQq5`t7Ho!&X;t3pQ9k zfh?pfzA7DHF+DqE@Q|(fA$~4Zon0#ciJf_th=3owsBKqjXp+Rd2rsf81ZNY>yN+rV z@~%|wb?$Xl(BVlc7R%7aNc0bi4E zMjG0@x}<+rCeU12>0I&Ui}E@7!73r)p3F6mzgF6BvJ`Aw1k~+~RwAev`+Hb%_1Sl* zXjKEZBxGBA!=Nb67JTW3a!PJiKJHtGU#dT!56?c*It$J2iR75-$xL>9Q+& z3@T8U1ZL^0NIm)OC4BpB=jcUZ>r3^G`we1Ok0|T3wawGdgKj#f*cbhnY|`Wr`7&uF z`vTRjzRzjq9Fm-9WX_TI87}fsWjAvt2KyBiE#b?AlP{`^LzNkTM}H4Y%S|2 zD5n&KnJ(huTezO&1&ev^Tfxma5IV_7#?#GbE?A}a+u_`2YLyGA&C}TW1AMsCAHLF3 zzBgH^=|fKpLI|j_i9S(Wz2?DCrV0Y~YZ<7nLZbb+lV+7W`uxu<8x<-OPb*CKa_HYW zlad~?VscW1|EY8V6Ik1IVnE%K2-`BLp$+dX?vm~-NW0!&{Tn)2Qp-6KcpcypI}V(N z_Fqz~-Lr>-f%=2aw~R=Zs@J@9xZsIJ>EiP;d?cDVg}l>SptbeYfMr&TVw>>i^_7S3 zRFSA@5L8T=kvDj`8#v#tP9?jrb|Wp8m);YlFf_MMheg&3!VE>)Y}wP%8}T? z)TudZTjayPcA~$`*;c+fNpu+<{tPOA)3>*7Kz5IPzM6>n)HU{s2);UOFyPaSu{A%J z)tl}gcI*Qr)`r)w9~-oHb!}hY*p=IhJ_VQ#GD`#d1?}xW%^o!k2K;WC6PrEPbZ>Hp z5C*Yz7ukGZLKR4M23IpJ7LJfz;d&M?VB(~Ycs7f6`1pSyg= zX%Z!QM20|KW9cnbN#0anlJxB80%fRdMglq^;|4xz>SPLFteOr>az@_RT1Z-C&A8x2 zNhs)s5MK|rkF0SQhBnkRi5rd_KQ?3=c5I`d#v+<%^h7HBF8gV=7R9ybaKR1#G9KII!0p9&TDttRj({b}Er_KimA-lL z2^I_uy{-G*1e?9}iJ~cCy5V&>O57hhIj)@)q#Y9D^T*zmE};Pq`3{sAGysffw+lPB zs+PE}RP;yuB2UF0L2b)6N|?j%yBas=6u2OZg@>t0^4nE)WZ|r&DR;}WLqUO86^^iH zl4xKs^Md(b7FbCu=g4uG?cy#h=!5f?b(>SzIrydBG(%@$=hn&B5&H4hzK%kv(Ft z?WC+5xn)`mUPt|Xoo=I9;Ne-{u+^OE2GxKpBrIeX7STd~=2urSiNoaAwM0Ajn)lKnXB;mOoSs09yc5r@+28ZY zwpNRG;?psUwdUQ=q(dtpYg3M@qn;5#h)-KKP17l?uV$9zTJ65{>)!~r4cPdhtKKo6oWL6u zLK3*HLnhhcvgkIlJ~pMgct#7aLr8$_`Q+x)C0x2ngLhXgW#6y5U(M3-;h*ekpTb>rYdgSYS&G{%kuP?~c`1hGU2UMy6U9x2` z>d#y0PjCJu$%i^Q1LZC{Ldf@j+AAGZ?B7k@aWiP)QAJU^V z`$4)(FNPuF?f27fOv7jQ`PbIDu9 zP0B2s_rGJFtzRD?#JP<{A9HzPH;t^|HH(7pKU=_FrbB=>s`n;o9AA6DnJN3>FPax~ zi{lgE!ut!2lwWl0-p(H!9^g&86b`Q={80dCWQoCihPz$>1RQdP625iY&tIai_XB>^7TB;b_7@u1+QI zPg`MQtpICol9LCN`1TVnK^&xN7mFQ)hB5%3te7A)US~owO1ECutIeM6+gJS?VC+?r zDo*^LX*L=UQJX(YAC7$ScNzO6G6}Z6Dj{P_fRDu7tSb(~)M>{^3VNt_v0PSTNwtf* z#GTQch%IM(y`d#qy}T$nWb*bexR1?EQ(z*9MAvQL`m25_ofGbRurt^AXLJ?4?Pfv; z)EZ4Y@zS5z5T`(L5WZk`S;)kEOQn4(ub=DiZr>H{z1~I!zt+|SQFM?jc})~}HO>1^ zLV)^VVc*7HXJT^xBf(ZyC%`wQkXy`iVsaNAG**`Y>uq89@8vB{r?lVgNU>O+IQIRb z=y3goEg`N9;hP!4%n%S;DzFNddnLECet!dH9!X>VRtQtNpr9kM-;6Pk7ahMRuAq{j zP-?uVIYAT-a3oDHuMCtS3q4}c=Cf3FO zr$h1|No^(22YCRIc5J^7Wi(m=_Sd4idl?Wv0_b9}M?6hv$|gYHgR^sBkN72Du;oKm z<+wf4BbFO_dlt`ezZ5(7`tCYWt|u1dmofUplC}o5Soq}Rh-nipzm=or~6p~Yoe@)f6 ziE)mbe1iG$F)%=V))BL+#|~|Vjs=jLQmPx02^tONw6(85U$HjKo0vq%jgPTBoV}RC zkZ#!35#9@%oY*5KaVel@t@oycJpgQZc~lH$JN+p?q@2_7@$kUm7aSonCcSDZl4xv0 z6>`bHGn;*-%eY1MHBaW+aXEpQ1+fe&05U=(D^35m5)$aV?=k0uANhTFhAk!g7~n5^l^!?!Y_1aGMxM6+KV&^9hwzLuxM@_UL{Ipxiz7{nkH~z zzX>_<;%3{8Ch48lCY(e3)p4O7me=yUi46hAj6@aKw%GFFm+Zx8$N0pRu7nV#KtM*^ zHDS)|KsN2!WMm1Tvt6L#pof(B9=;CxP#6Y(9_9;qt|i6VeqkO~qCRT?wd>nPo+8kh zdzh0K6My?()^#5$9#M6-#kDFcT)F-#S&{q!Z@Ah zfK!ZBCdho(9ec%6U?z(h@udZVm7nMv?s57e91JwP|8znOvnXSMEpncR+q5@iD!=_L zj@4Au8;nm7WX4m-0L`=P3n=mGOQ$Q@ARDOaB z{WJQKX(rr+%Q^hy$(cyJAu93fTHZ*0`JjgCUO+OWaE-9N%YaNpN<#ut#rdBW3Zs{5h9RNtJ zt39BH&s9=Xba$D~3{%^LvK!QVH%^Oehd?4R>f4=ElO<1B8h4_1|BK-&UUju=h|DOX zjDvxY=)q&Q_A1J;d%f;CLgmLao(t)(pb&67=-_Qd?yd^hB!#3u7Iw>5#YRzGF*Rx|Y&w4xW zv-Q|)@9;eKxRj!bICK(MUqy0K0&5vq3;Xa=>8%)D!dIf?0E|$<)-y)8^9P8f%&Ag* zz27R}qv{s`g+K9URXN7Ve2iOKdS)lIq4>PkE}&c!S1U|oF{)^a>ea7hIuUYS|HB&V zCRO8y0r`Q+g3Dk0Dzm3O&HnWY|07we;sew|E`VA{J87#;zxvAR_1x~SIW3b3fCKW) zR1jiR!ka@YLR<#ifMdn!a+IK{f2?3Or1BsM5E2%T$~AEHG2UA;wP%Yvs{o=VU@aeX z;sV@);nd$2u<3bhDsmV|UM!#AKfioBL^k}VTr8>c_Bqa}7-J!1me#YU&z&S6?lSYK zqBg4#X~J<0(c{yTKg*xXM<7_!bCUGigyVSE=fX79>*hWx`69+P+T<^+QS#7Cg*UW{ z&8KS&u<^*ov6u)&Sbi51082sDiydf!!<#tOmNphv=VX?*?9ajfjcW)(({y zWESc){;H{>Z*|Yc=mvx=WFGGaTR{}&^lnUV2e|bv-Vyb}N5=PX#yoZFzej)KUCj3c z(wrhw1;4g+OfVx-|JS#AKAR*Yq7CK2%L^4)WLszZ{SB3TQq5qa8Rl2t1I37on5vYl z+($`H7PU7E!dRRj_OX|{^Ngn-3w|IV>d+M`iF2P3K+3~{6EIIDw5p~?xo7{P*12oK z%l6KNF&k-a0~$v^Ntth(oO=s=D#gGkq$~UQ2rj0v?#|VAa5DS%u;{#j&nBbcFItQL ziOcxE98patN|<`|7ItBuPY%lj$)70txzrfnV?eeGqg4e&SHWxxK2F%#bzxX1L)jvbw`t8t#KL?-A|>23^Raaf&sq294tm=BRBTr{FNKEtGr5Z;XntDTl7z z!&Z@R2q3g;))Bl^tKC`m!XA@VuO+2cWTS>23nw8ffR<-xHmZ~=^eC!C^6GtWx1Nc2 zUd+*l?H)lKnWs1B<=HUEq8qG<+DXs5WHI7GjR%|-X+;hH%bWrD4e%_>;KJYYjg;lb zYgOG{%i61T8)HK}NfZU~4o&WhQEUheaq(bZz7QqBZ(o^hwVi=XKsKRT3~b8zTWmL4 z^oENn=4ijJ!nqiOD1^ozgFRW3;EI;*@j% zkTIxhtUk`dCmo~ycBGaYng7U;BdfAV`j1opE1N*_vE50*CmDQ30eE$P0a;%-0{0O7 zym=U3W_=0`tg|=&k9F2WmmKnP-@3Vcu4o|i{?rZtZvg7c;V3}#$f>n2$9l|^{pwj_ zRX?oUpcL|ZX6(RJ3w0JmcGU-o1=%VC?I4yrEAWVR6%phBrS~gstx$<$0XF3Fs<)NY zw$+U{*tTx?vo1^XN~g&9IjHU$0C^=Oe5ZHB+lDyQ^RAoUQ?0$nbAqVp9Ovx1UEe+R zdiu3q#~Dk!z3$gkLXbXfz}j_h64c))dD$Ph187ZN}B83$!fS+>+MgEeHo&CLHxH)b^&R=2{XuNsHTSdAdokN7IIZ*s8~o z6R~g-e*33}xnU!s)WZRVruTy5gB>R-&uetUU|S2byr$gfiOt&EMx-^!v#vU`SlkM~ ztiZ#Y3&sLDQ+hM{Cgxg<{8qm+_G>I)ly%b>d(KpF^0S++tAfyaVKM6=T1d27mFnuT z{(nXn#Buh-ktF$dXbiI4FCYE(aAUWb%6qG$SD3|fVIn<|sTdzgGx}9TaI`sJLvdGZdA}fXVxq4+HeCM5kKt)pe-uKeskzu->#%%V&NCS+~1tZ|hUKMk4i?Q-IXcb1w<_c^vM* z2BEQBvQGS@lk9r-y{_~6tI(>BQQlUtS@{09oUua|>-=w<9n^o`wxw*(@EQ8OLNJ`F`Zf z40kJjAKRR9c;92~FOg9B;V-^Eog^g@+38^hQ<02(A0{3$X2Se*%-@TyQa=dlu^{oy{ z<)kruYpJ2H54r7s-4+?@;8AP)6<%Ws{1#QBpMirl|X8r(XL6< z6|#hBkV8i$Onq`L$siYMaDxNJxNrESfwIQ-?OI(9qdw37mVN0BIEwIv*NKZ9loN2f zQEB@LT)EsUnUFHPx1x0S4iEADOOR3z=Vq{qRe8yJk(l|Pb9d;e>w$S^ZdeCEsLU9+2ZggKvbNz ztfCi$th=-u`)*g(tM`pc(`yYj_MQ*lIrFyp2EW<#4o{1L>QPFV6Eu6LbhzKU9t=0Q z-z)>7AWoW54#mg#ys-v`9&F8!A~bR;Jwr=oR#hYwrBTG_LD*}!Uk|_dk5x9SGkdR+ z#O|j!*b8`XvHfun^}Pi#Th4o>D1{~yEhIeAd3Y}>f%;BJLESM9#|w%8=CE>GgU3{6 zbb3<;l&%8VO0De4cfPPfX=_Q;m_?n=1Z(TywIy+g4p|c4u_Bs<{v~}kMngAfisn1a zHzH5y@+d(X!Koxf4`O?ys2xBXRs1@~b+FCGpojTN8Emjevjngqt#k*aUY zc8@-8TaSCDB(gkv8T8RXLtSOBOvG5%{_}}J_=Kc`a+|{+%F{MI-mz4r?GSi#l?S|e zYf(inRy#?L>FA>nAR9W~_-^_R_xE#57iCIX-ZaTnX{qlON*jrt5|u{1%!&L)W}mm^!b7m?bpho@KfkwAhb+PhO*DA*SufYvU?cnuuSc5i-hgE?I6fYt}Kmwi;3G2dxNj@6XzME}={HLM+ z24o18a_vZ>P7s~dKs7n5_8BlB z4>}k#8TQ>C?Y5AB9T|?kd4op!v-c}vg#1b-PG)A~HIpTi5p;~j-oRLr!6hdIVe?j= zx-7#Uk$a)X*m{XxynXS5eAG@%=|k~`mAK>eI>fh2${?|TqhllVi%Z`&Zh_eAaRaty zpf>tK=lHIwUNbCv=UKJ`ZHmg)_y@?){n7&D7#hV`+h*EN---F0XCx-@hJcFj&)Ey8 zzP%oDNpuMFm~0>MgXL|aGk=-iswZdb4HbhTmC$1>5VtXn-y(+}~Vun~=174Z@MlrSX|U4ly$ydgml)rN^t?ftSjKG)}d+u;J%(Ld|xQkab<_MX^} z?Zv#CzF^vPmM$qr!mFhIQ#JqK-opLC_!060*%^oW^qE3Uh*0>xxu(~#qb#hp1_ph# z6h@g{N?kOGcCk!A*t)}<+jFAJ?eO$o&mMb=Dc?$C{AN#yobzyT$+3qm(RiuB~>| zuk3hWTX?^0cup|;sBjOt)J(+|N0qvctL%+pb#+4KufH6l>>LtT5l#X0(4bp(pK51^ zCdc=GAU%#*xBU;Qv7~t1JSf6#o6@*+I6FHlDpBuV$2$MbM2TU@@C>>dbp3>eDM!6b zTl+UsxNKAdts<#`()YW#*}kB0%SguFM$Nsk^p>%T$PXJ<`Cn+=yD^KXgx|Zbk-(1* z_9DXCL0estt7FL2oxb6h!HKgP?B)SL<|hRYjFq_gpw(EtF71af$<8F8fhp-Sg-fwp zO0fG3vhbX|6|#09bZRsZZzWoB>&iTw~v~tWqVyi0ID{xH0B@ zY;x!eZm$5;Fh61fe&Mb8W7lCK%pr;}v8GQ*3bo(ov?w-JS%SKi`^;(JBd8^G^G9Mpk$~sjA2TO*h3yZO$>#ShW82uCT&5LbV zk^P4Lng=jf_m44x%bm^mYimN!5DQ4#GZ4Ry=FP~qx4FVN{TQ~Do=nR&UOy?SP1l=7 zouQGJ-4ijL%xRcOo?tHz`e)whVtd)ffMrs0I~ZI$xOm;&jX3+zXlg`E!M7J^W$plv zGn=-;8GGbLjNP-A1pOnk1L3!?ap=RuNJr3S)b!+Tx;6h*OT|(9Bfp9Z)#Yb3z;d%9Xs0i zg(nFOgoTSA3@9D$RrGurpR*OTzVScj2!nG*-0(NF^1#V#+jGH=<@)8_b;no*s&i(POaz!?w(k2>pwO{=@gkg&5lxCEzQ@z_n)Zg z6270$6h5Oqh1tmJl`L*CYWx(67vx7~ef?cSLeA|hur~DI&^KceN@cZr*YY=O&*CH7 z<-p>tL|ECx!dWRl-z4i30aS+Ix45QQT~?_4yAC?NRZm?vXoGN(w0S*0vHlBeU&|oc zJPC+Yo#SFJ%~hd+km#iqZStPClwjlb)9-k|UH(DHFMVN1{ct;l7IXo!9G z_jBD?)%)>J`(N7f!Tp8LKMA7$h(N%suAhJPqF*!c(!`^f9%^DCx2{q8qm98*eC4%4 zr+>XEbK~PRf6ltAWbIU$fw$?~x(!PeA_Cc=MBP8rF4*zq?v^}%IDQ|-DZTJ{u|c

    #T@9l37>B6*&mBooNb;As>{9WCW?kX!`}7~r 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

    cu11Ub#`i7)sI%e%&$kH*!)@!lZ&o7fBNOW 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

    IYs8ZR+F+rBG%SYbH1HSbm! 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 z

    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

    {hasChatEntry && (
    clickChat(e, item)}> - chat + {`Chat`}
    )}
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx index 590e924fa3..b2ae1031eb 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/EditContactForm/index.tsx @@ -7,7 +7,6 @@ import { useTranslation } from 'react-i18next'; import IdAndAddress from '../IdAndAddress'; import './index.less'; import { ValidData } from 'pages/Contacts/AddContact'; -import { useEffectOnce } from 'react-use'; const { Item: FormItem } = Form; From 390b4bb9e54ae16e70a0f270a864082b2a563763 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Sat, 19 Aug 2023 13:58:56 +0800 Subject: [PATCH 534/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20chat=20list=20de?= =?UTF-8?q?tail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Chat/ChatDetails/index.tsx | 17 +++++---- .../js/pages/Chat/ChatHome/index.tsx | 4 +-- .../ChatHomeListItemSwiper/index.tsx | 2 +- .../pages/Chat/components/ChatList/index.tsx | 13 ++++++- .../js/pages/Chat/components/Chats/index.tsx | 36 +++++++++++++------ .../InputToolbar/AccessoryBar/index.tsx | 4 +-- .../InputToolbar/BottomBarContainer/index.tsx | 4 +-- .../InputToolbar/ChatInput/index.tsx | 4 +-- .../js/pages/Chat/components/hooks/index.ts | 4 +-- .../{components => }/context/chatsContext.tsx | 2 +- .../Chat/{components => }/context/hooks.ts | 4 +++ 11 files changed, 64 insertions(+), 30 deletions(-) rename packages/mobile-app-did/js/pages/Chat/{components => }/context/chatsContext.tsx (68%) rename packages/mobile-app-did/js/pages/Chat/{components => }/context/hooks.ts (89%) diff --git a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx index d437e2464e..27302fe981 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx @@ -17,8 +17,9 @@ import { FontStyles } from 'assets/theme/styles'; import AddContactButton from '../components/AddContactButton'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; import { ChannelItem } from '@portkey-wallet/im/types'; -import { useChannel, useMuteChannel, usePinChannel, useHideChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { useMuteChannel, usePinChannel, useHideChannel } from '@portkey-wallet/hooks/hooks-ca/im'; import ActionSheet from 'components/ActionSheet'; +import { useCurrentChannelId } from '../context/hooks'; type RouterParams = { channelInfo?: ChannelItem; @@ -31,7 +32,11 @@ const ChatDetails = () => { const pinChannel = usePinChannel(); const muteChannel = useMuteChannel(); const hideChannel = useHideChannel(); - const { sendMessage } = useChannel(channelInfo?.channelUuid || ''); + const currentChannelId = useCurrentChannelId(); + + // useEffectOnce(() => { + // init(); + // }); const onPressMore = useCallback( (event: { nativeEvent: { pageX: any; pageY: any } }) => { @@ -47,14 +52,14 @@ const ChatDetails = () => { title: pin ? ChatOperationsEnum.UNPIN : ChatOperationsEnum.PIN, iconName: pin ? 'chat-unpin' : 'chat-pin', onPress: () => { - pinChannel(channelInfo?.channelUuid || '', !pin); + pinChannel(currentChannelId || '', !pin); }, }, { title: mute ? ChatOperationsEnum.UNMUTE : ChatOperationsEnum.MUTE, iconName: mute ? 'chat-unmute' : 'chat-mute', onPress: () => { - muteChannel(channelInfo?.channelUuid || '', !channelInfo?.channelUuid); + muteChannel(currentChannelId || '', !channelInfo?.channelUuid); }, }, { @@ -72,7 +77,7 @@ const ChatDetails = () => { title: 'Confirm', type: 'primary', onPress: () => { - hideChannel(channelInfo?.channelUuid || ''); + hideChannel(currentChannelId || ''); }, }, ], @@ -85,7 +90,7 @@ const ChatDetails = () => { position: 'left', }); }, - [channelInfo?.channelUuid, hideChannel, mute, muteChannel, pin, pinChannel], + [channelInfo?.channelUuid, currentChannelId, hideChannel, mute, muteChannel, pin, pinChannel], ); return ( diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index 3aa18e8819..26492a8179 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -83,10 +83,10 @@ export default function DiscoverHome() { const sendMess = useCallback(async () => { im.service.sendMessage({ - toRelationId: 'e7i7y-giaaa-aaaaj-2ooma-cai', + toRelationId: 'eegeb-baaaa-aaaaj-35xda-cai', type: 'TEXT', sendUuid: v4(), - content: ` hello tho--- ${formatChatListTime(Date.now())} `, + content: ` hello sa--- ${formatChatListTime(Date.now())} `, }); console.log('sendMess', v4()); }, []); diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx index 4e7fadfe05..577419206e 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatHomeListItemSwiper/index.tsx @@ -97,7 +97,7 @@ export default memo(function ChatHomeListItemSwiped(props: ChatHomeListItemSwipe {item.lastMessageType === 'TEXT' ? item.lastMessageContent : '[Image]'} - {item.pin ? ( + {item.pin && item.unreadMessageCount === 0 ? ( ) : ( { @@ -72,6 +75,14 @@ export default function ChatList(props: ChatListType) { [muteChannel, onHideChannel, pinChannel], ); + const navToDetail = useCallback( + (item: ChannelItem) => { + chatDispatch(setCurrentChannelId(item.channelUuid)); + navigationService.navigate('ChatDetails', { channelInfo: item }); + }, + [chatDispatch], + ); + return ( onHideChannel(item)} - onPress={() => navigationService.navigate('ChatDetails', { channelInfo: item })} + onPress={() => navToDetail(item)} onLongPress={event => longPress(event, item)} /> )} diff --git a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx index 2d3fb1f543..a874a43a2f 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Chats/index.tsx @@ -12,24 +12,20 @@ import initialMessages from '../messages'; import { AccessoryBar, BottomBarContainer } from '../InputToolbar'; import { randomId } from '@portkey-wallet/utils'; import { Keyboard } from 'react-native'; -import { useChatsDispatch } from '../context/hooks'; +import { useChatsDispatch, useCurrentChannelId } from '../../context/hooks'; import CustomBubble from '../CustomBubble'; -import { setBottomBarStatus, setChatText, setShowSoftInputOnFocus } from '../context/chatsContext'; +import { setBottomBarStatus, setChatText, setShowSoftInputOnFocus } from '../../context/chatsContext'; import useEffectOnce from 'hooks/useEffectOnce'; import MessageText from '../Message/MessageText'; import { destroyChatInputRecorder, initChatInputRecorder } from 'pages/Chat/utils'; import MessageImage from '../Message/MessageImage'; +import { Message as IMMessage } from '@portkey-wallet/im/types'; import { useThrottleCallback } from '@portkey-wallet/hooks'; import { StyleSheet } from 'react-native'; import { pTd } from 'utils/unit'; import Touchable from 'components/Touchable'; - -const user = { - _id: 1, - name: 'Aaron', - avatar: 'https://lmg.jj20.com/up/allimg/1111/05161Q64001/1P516164001-3-1200.jpg', -}; +import { useChannel } from '@portkey-wallet/hooks/hooks-ca/im'; const Empty = () => null; @@ -40,9 +36,27 @@ const ListViewProps = { legacyImplementation: true, }; +const format = (message: IMMessage[]): IMessage[] => { + return message.map(ele => ({ + _id: ele.sendUuid, + text: ele.content, + createdAt: Number(ele.createAt), + user: { + _id: ele.from, + }, + })); +}; + const ChatsUI = () => { - const [messages, setMessages] = useState([]); + const currentChannelId = useCurrentChannelId(); + const { list, init } = useChannel(currentChannelId || ''); + + const formattedList = format(list); + + const [, setMessages] = useState([]); + const dispatch = useChatsDispatch(); + useEffect(() => { setMessages(initialMessages as IMessage[]); }, []); @@ -52,6 +66,7 @@ const ChatsUI = () => { }; useEffectOnce(() => { + init(); initChatInputRecorder(); return () => { dispatch(setChatText('')); @@ -106,13 +121,12 @@ const ChatsUI = () => { <> state.chats.currentChannelId); +} From 6e5886cce8b00fd18f3f867ec570d4fdc24fe696 Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Sat, 19 Aug 2023 14:44:09 +0800 Subject: [PATCH 535/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20find=20channel?= =?UTF-8?q?=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../js/pages/Chat/ChatHome/index.tsx | 2 +- .../js/pages/Chat/SearchPeople/index.tsx | 53 +++++++++++++------ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index 26492a8179..e8c037ee75 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -83,7 +83,7 @@ export default function DiscoverHome() { const sendMess = useCallback(async () => { im.service.sendMessage({ - toRelationId: 'eegeb-baaaa-aaaaj-35xda-cai', + toRelationId: 'e7i7y-giaaa-aaaaj-2ooma-cai', type: 'TEXT', sendUuid: v4(), content: ` hello sa--- ${formatChatListTime(Date.now())} `, diff --git a/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx b/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx index 75f419e4ed..28217fcaf3 100644 --- a/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/SearchPeople/index.tsx @@ -6,23 +6,30 @@ import { pTd } from 'utils/unit'; import navigationService from 'utils/navigationService'; import PageContainer from 'components/PageContainer'; import InputWithCancel from 'components/InputWithCancel'; -import CommonButton from 'components/CommonButton'; import { useFocusEffect } from '@react-navigation/native'; import NoData from 'components/NoData'; -import { Image } from '@rneui/base'; -import { TextM, TextL } from 'components/CommonText'; +import { TextL } from 'components/CommonText'; import Touchable from 'components/Touchable'; import FindMoreButton from '../components/FindMoreButton'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; import CommonAvatar from 'components/CommonAvatar'; -const mock_data = [0, 1, 2]; +import { useSearchChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import useDebounce from 'hooks/useDebounce'; +import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; +import CommonToast from 'components/CommonToast'; +import { handleErrorMessage } from '@portkey-wallet/utils'; +import { ChannelItem } from '@portkey-wallet/im/types'; +import { BGStyles } from 'assets/theme/styles'; export default function SearchPeople() { const iptRef = useRef(); const timerRef = useRef(null); + const searchChannel = useSearchChannel(); + const [keyword, setKeyword] = useState(''); - const [filterList, setFilterList] = useState(mock_data); + const debounceKeyword = useDebounce(keyword, 500); + const [filterList, setFilterList] = useState([]); useFocusEffect( useCallback(() => { @@ -34,9 +41,20 @@ export default function SearchPeople() { }, []), ); + const fetchList = useLockCallback(async () => { + try { + const result = await searchChannel(debounceKeyword); + console.log('result', result); + setFilterList(result?.data?.list); + } catch (error) { + CommonToast.fail(handleErrorMessage(error)); + } + }, [debounceKeyword]); + useEffect(() => { - setFilterList(mock_data); - }, [keyword]); + if (!debounceKeyword) return setFilterList([]); + fetchList(); + }, [debounceKeyword, fetchList]); useEffect( () => () => { @@ -45,15 +63,15 @@ export default function SearchPeople() { [], ); - const renderItem = useCallback((item: any) => { + const renderItem = useCallback(({ item }: { item: ChannelItem }) => { console.log(item); return ( navigationService.navigate('ChatDetails')}> - + onPress={() => navigationService.navigate('ChatDetails', { channelInfo: item })}> + - Sally + {item?.displayName} ); @@ -69,16 +87,19 @@ export default function SearchPeople() { titleDom="Search"> setKeyword('')} onChangeText={v => setKeyword(v)} value={keyword} - clearText={() => setKeyword('')} onCancel={() => navigationService.goBack()} /> + Chats} - ListEmptyComponent={} + ListHeaderComponent={ + debounceKeyword && filterList.length > 0 ? Chats : null + } + ListEmptyComponent={debounceKeyword ? : null} renderItem={renderItem} /> @@ -87,7 +108,7 @@ export default function SearchPeople() { const styles = StyleSheet.create({ containerStyles: { - backgroundColor: defaultColors.bg4, + backgroundColor: defaultColors.bg1, paddingHorizontal: 0, flex: 1, }, @@ -107,7 +128,7 @@ const styles = StyleSheet.create({ height: pTd(72), flex: 1, borderBottomColor: defaultColors.border1, - borderBottomWidth: 1, + borderBottomWidth: StyleSheet.hairlineWidth, paddingRight: pTd(20), justifyContent: 'center', }, From 6ab604190331e3c93e97f2804f5b5ae1585c65c3 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Sat, 19 Aug 2023 15:48:29 +0800 Subject: [PATCH 536/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20im=20destr?= =?UTF-8?q?oy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/server.ts | 4 + packages/hooks/hooks-ca/im/channel.ts | 24 +--- packages/hooks/hooks-ca/im/channelList.ts | 17 ++- packages/hooks/hooks-ca/im/index.ts | 5 +- packages/im/index.ts | 132 +++++++++++------- packages/im/service/index.ts | 10 +- packages/im/types/index.ts | 1 + packages/im/types/service.ts | 4 +- packages/im/utils/sign.ts | 3 +- packages/mobile-app-did/js/hooks/network.ts | 2 + packages/mobile-app-did/js/hooks/useLogOut.ts | 4 + .../mobile-app-did/js/pages/Home/index.tsx | 7 +- packages/store/store-ca/im/slice.ts | 4 + 13 files changed, 134 insertions(+), 83 deletions(-) diff --git a/packages/api/api-did/server.ts b/packages/api/api-did/server.ts index 2faade122d..de76108ce7 100644 --- a/packages/api/api-did/server.ts +++ b/packages/api/api-did/server.ts @@ -6,6 +6,7 @@ import { getRequestConfig, spliceUrl } from '../utils'; import { isValidRefreshTokenConfig, queryAuthorization, RefreshTokenConfig } from './utils/index'; import { sleep } from '@portkey-wallet/utils'; import im from '@portkey-wallet/im'; +import { IM_TOKEN_ERROR_ARRAY } from '@portkey-wallet/im/constant'; export class DidService extends ServiceInit { protected refreshTokenConfig?: RefreshTokenConfig; protected onLockApp?: (expired?: boolean) => void; @@ -104,6 +105,9 @@ export class DidService extends ServiceInit { throw fetchResult; } return this.send(base, config, ++reCount); + } else if (fetchResult && IM_TOKEN_ERROR_ARRAY.includes(fetchResult.code)) { + await im.refreshToken(); + return this.send(base, config, ++reCount); } return fetchResult; }; diff --git a/packages/hooks/hooks-ca/im/channel.ts b/packages/hooks/hooks-ca/im/channel.ts index ba175cce91..0388426d56 100644 --- a/packages/hooks/hooks-ca/im/channel.ts +++ b/packages/hooks/hooks-ca/im/channel.ts @@ -15,9 +15,10 @@ import { updateChannelMessageAttribute, } from '@portkey-wallet/store/store-ca/im/actions'; -import { useImState } from '.'; +import { useChannelItemInfo, useImChannelMessageListNetMapState } from '.'; import s3Instance, { getThumbSize } from '@portkey-wallet/utils/s3'; import { request } from '@portkey-wallet/api/api-did'; +import { messageParser } from '@portkey-wallet/im/utils'; export type ImageMessageFileType = { body: string | File; @@ -44,15 +45,13 @@ export const useSendChannelMessage = () => { sendUuid: `${userInfo.relationId}-${channelId}-${Date.now()}-${uuid}`, }; - // TODO: parsedContent need parse - const msgObj: Message = { + const msgObj: Message = messageParser({ ...msgParams, from: userInfo.relationId, fromAvatar: userInfo.avatar, fromName: userInfo.name, createAt: `${Date.now()}`, - parsedContent: msgParams.content, - }; + }); dispatch( addChannelMessage({ network: networkType, @@ -136,7 +135,7 @@ export const useDeleteMessage = (channelId: string) => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { channelMessageListNetMap } = useImState(); + const channelMessageListNetMap = useImChannelMessageListNetMapState(); const list = useMemo( () => channelMessageListNetMap?.[networkType]?.[channelId] || [], [channelId, channelMessageListNetMap, networkType], @@ -204,7 +203,7 @@ export const useChannel = (channelId: string) => { const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); - const { channelMessageListNetMap } = useImState(); + const channelMessageListNetMap = useImChannelMessageListNetMapState(); const list = useMemo( () => channelMessageListNetMap?.[networkType]?.[channelId] || [], [channelId, channelMessageListNetMap, networkType], @@ -218,7 +217,7 @@ export const useChannel = (channelId: string) => { const { sendChannelMessage, sendChannelImage } = useSendChannelMessage(); const deleteMessage = useDeleteMessage(channelId); - const [info, setInfo] = useState(); + const info = useChannelItemInfo(channelId); const [hasNext, setHasNext] = useState(false); const [loading, setLoading] = useState(false); @@ -349,15 +348,6 @@ export const useChannel = (channelId: string) => { errorHandlerRef.current(e); }); - im.service - .getChannelInfo({ - channelUuid: channelId, - }) - .then(result => { - console.log('channelInfo', result.data); - setInfo(result.data); - }); - if (im.userInfo) { im.service.triggerMessageEvent({ channelUuid: channelId, diff --git a/packages/hooks/hooks-ca/im/channelList.ts b/packages/hooks/hooks-ca/im/channelList.ts index 98cfb66948..cec8e9e454 100644 --- a/packages/hooks/hooks-ca/im/channelList.ts +++ b/packages/hooks/hooks-ca/im/channelList.ts @@ -6,11 +6,12 @@ import { CHANNEL_LIST_LIMIT } from '@portkey-wallet/constants/constants-ca/im'; import { useCurrentNetworkInfo } from '../network'; import { useAppCommonDispatch } from '../../index'; import { nextChannelList, setChannelList, setHasNext } from '@portkey-wallet/store/store-ca/im/actions'; - -import { useImState } from '.'; +import { useImChannelListNetMapState, useImHasNextNetMapState } from '.'; export const useNextChannelList = () => { - const { channelListNetMap, hasNextNetMap } = useImState(); + const channelListNetMap = useImChannelListNetMapState(); + const hasNextNetMap = useImHasNextNetMapState(); + const { networkType } = useCurrentNetworkInfo(); const dispatch = useAppCommonDispatch(); @@ -82,7 +83,7 @@ export const useNextChannelList = () => { }; export const useChannelList = () => { - const { channelListNetMap } = useImState(); + const channelListNetMap = useImChannelListNetMapState(); const { networkType } = useCurrentNetworkInfo(); const { next, hasNext } = useNextChannelList(); @@ -100,6 +101,14 @@ export const useChannelList = () => { }; }; +export const useChannelItemInfo = (channelId: string) => { + const { list } = useChannelList(); + + return useMemo(() => { + return list.find(item => item.channelUuid === channelId); + }, [channelId, list]); +}; + export const useCreateP2pChannel = () => { const createChannel = useCallback((relationId: string) => { return im.service.createChannel({ diff --git a/packages/hooks/hooks-ca/im/index.ts b/packages/hooks/hooks-ca/im/index.ts index cb67a97ef3..a910dbe83a 100644 --- a/packages/hooks/hooks-ca/im/index.ts +++ b/packages/hooks/hooks-ca/im/index.ts @@ -8,6 +8,9 @@ import { addChannel, updateChannelAttribute } from '@portkey-wallet/store/store- import { UpdateChannelAttributeTypeEnum } from '@portkey-wallet/store/store-ca/im/type'; export const useImState = () => useAppCASelector(state => state.im); +export const useImHasNextNetMapState = () => useAppCASelector(state => state.im.hasNextNetMap); +export const useImChannelListNetMapState = () => useAppCASelector(state => state.im.channelListNetMap); +export const useImChannelMessageListNetMapState = () => useAppCASelector(state => state.im.channelMessageListNetMap); export const useUnreadCount = () => { const [unreadCount, setUnreadCount] = useState(0); @@ -32,7 +35,7 @@ export const useInitIM = () => { const isInitRef = useRef(false); - const { channelListNetMap } = useImState(); + const channelListNetMap = useImChannelListNetMapState(); const list = useMemo(() => channelListNetMap?.[networkType]?.list || [], [channelListNetMap, networkType]); const listRef = useRef(list); listRef.current = list; diff --git a/packages/im/index.ts b/packages/im/index.ts index 91fba2e4ef..30826685c5 100644 --- a/packages/im/index.ts +++ b/packages/im/index.ts @@ -8,7 +8,6 @@ import { IMConfig } from './config'; import { FetchRequest } from '@portkey/request'; import { IBaseRequest } from '@portkey/types'; import { IMService } from './service'; -import { getVerifyData } from './utils'; import { IM_TOKEN_ERROR_ARRAY } from './constant'; import { request } from '@portkey-wallet/api/api-did'; @@ -54,6 +53,7 @@ export class IM { } async init(account: AElfWallet, caHash: string) { + this.status = IMStatusEnum.INIT; this._account = account; this._caHash = caHash; @@ -69,9 +69,12 @@ export class IM { } this.status = IMStatusEnum.AUTHORIZING; try { + const account = this._account; const caHash = this._caHash; - const verifyData = utils.getVerifyData(`${Date.now()}`, this._account, this._caHash); - const { data: verifyResult } = await this.service.verifySignatureLoop(verifyData); + const { data: verifyResult } = await this.service.verifySignatureLoop(() => { + if (caHash !== this._caHash) return null; + return utils.getVerifyData(account, caHash); + }); const addressAuthToken = verifyResult.token; const { data: autoResult } = await this.service.getAuthTokenLoop({ addressAuthToken, @@ -87,7 +90,7 @@ export class IM { // TODO: remove test API_KEY const API_KEY = '295edaae67724a8ba04f4f39b9221779'; this._imInstance = RelationIM.init({ token, apiKey: API_KEY, connect: true, refresh: true }); - this.listenRelationIM(this._imInstance); + this.bindRelation(this._imInstance); this.status = IMStatusEnum.AUTHORIZED; @@ -105,31 +108,20 @@ export class IM { } } - listenRelationIM(imInstance: RelationIM) { - imInstance.bind(Im.CONNECT_OK, () => { - console.log('CONNECT_OK'); - }); - - imInstance.bind(Im.CONNECT_ERR, (e: any) => { - console.log('CONNECT_ERR msg', e); - this.status = IMStatusEnum.ERROR; - }); - imInstance.bind(Im.CONNECT_CLOSE, async (e: any) => { - console.log('CONNECT_CLOSE msg', e); - this.status = IMStatusEnum.INIT; - await sleep(1000); - this.updateErrorObservers(e); - try { - this.initRelationIM(); - } catch (error) { - console.log('initRelationIM error', error); - } - }); + bindRelation(imInstance: RelationIM) { + imInstance.bind(Im.CONNECT_OK, this.onConnectOk); + imInstance.bind(Im.CONNECT_ERR, this.onConnectErr); + imInstance.bind(Im.CONNECT_CLOSE, this.onConnectClose); + imInstance.bind(Im.RECEIVE_MSG_OK, this.onReceiveMessage); + } - imInstance.bind(Im.RECEIVE_MSG_OK, (e: any) => { - console.log('RECEIVE_MSG_OK msg', e); - this.updateMsgObservers(e); - }); + bindOffRelation() { + if (!this._imInstance) return; + const imInstance = this._imInstance; + imInstance.bindOff(Im.CONNECT_OK, this.onConnectOk); + imInstance.bindOff(Im.CONNECT_ERR, this.onConnectErr); + imInstance.bindOff(Im.CONNECT_CLOSE, this.onConnectClose); + imInstance.bindOff(Im.RECEIVE_MSG_OK, this.onReceiveMessage); } getInstance() { @@ -177,7 +169,33 @@ export class IM { }; } - updateMsgObservers(e: any) { + onConnectOk(_e: any) { + console.log('CONNECT_OK'); + } + + onConnectErr(e: any) { + console.log('CONNECT_ERR', e); + } + + onConnectClose = async (e: any) => { + console.log('CONNECT_CLOSE msg', e); + if (this.status === IMStatusEnum.DESTROY) { + console.log('CONNECT_CLOSE DESTROY'); + return; + } + this.bindOffRelation(); + this.status = IMStatusEnum.INIT; + await sleep(1000); + this.updateErrorObservers(e); + try { + this.initRelationIM(); + } catch (error) { + console.log('initRelationIM error', error); + } + }; + + onReceiveMessage = (e: any) => { + console.log('RECEIVE_MSG_OK msg', e); const rawMsg: Message = e['im-message']; const channelId = rawMsg.channelUuid; const channelObservers = this._channelMsgObservers.get(channelId); @@ -193,7 +211,7 @@ export class IM { unreadCount: this._msgCount.unreadCount + 1, }); } - } + }; registerErrorObserver(cb: (e: any) => void) { const symbol = Symbol(); @@ -239,33 +257,37 @@ export class IM { }); } + async refreshToken() { + if (!this._account || !this._caHash) { + throw new Error('no account or caHash'); + } + const account = this._account; + const caHash = this._caHash; + const { + data: { token: addressAuthToken }, + } = await this.service.verifySignatureLoop(() => { + if (caHash !== this._caHash) return null; + return utils.getVerifyData(account, caHash); + }, 5); + + const { + data: { token }, + } = await this.service.getAuthTokenLoop({ addressAuthToken }, 5); + if (caHash !== this._caHash) { + throw new Error('account changed'); + } + this.setAuthorization(token); + } + private rewriteFetch() { const send = this.fetchRequest.send; this.fetchRequest.send = async (...args) => { const result = await send.apply(this.fetchRequest, args); if (IM_TOKEN_ERROR_ARRAY.includes(result.code)) { - if (!this._account || !this._caHash) { - throw new Error('no account or caHash'); - } - try { - const caHash = this._caHash; - const verifyData = getVerifyData(`${Date.now()}`, this._account, this._caHash); - const { - data: { token: addressAuthToken }, - } = await this.service.verifySignatureLoop(verifyData, 5); - const { - data: { token }, - } = await this.service.getAuthTokenLoop({ addressAuthToken }, 5); - - if (caHash !== this._caHash) { - throw new Error('account changed'); - } - this.setAuthorization(token); - return await send.apply(this.fetchRequest, args); - } catch (error) { - throw error; - } + await this.refreshToken(); + + return await send.apply(this.fetchRequest, args); } else if (result.code?.[0] !== '2') { throw result; } @@ -305,20 +327,22 @@ export class IM { } destroy() { - this._imInstance && this._imInstance.destroy(); - this._imInstance = undefined; + console.log('destroy im', this._caHash); + this.status = IMStatusEnum.DESTROY; + this.bindOffRelation(); this._msgCount = { unreadCount: 0, mentionsCount: 0, }; this._account = undefined; this._caHash = undefined; - this.status = IMStatusEnum.INIT; this.userInfo = undefined; this._unreadMsgObservers.clear(); this._channelMsgObservers.clear(); this._errorObservers.clear(); this._msgCountObservers.clear(); + this._imInstance && this._imInstance.destroy(); + this._imInstance = undefined; } } diff --git a/packages/im/service/index.ts b/packages/im/service/index.ts index 87237e0720..80e7cf126d 100644 --- a/packages/im/service/index.ts +++ b/packages/im/service/index.ts @@ -22,6 +22,7 @@ import { TriggerMessageEvent, UpdateChannelMuteParams, UpdateChannelPinParams, + VerifySignatureLoopParams, VerifySignatureParams, VerifySignatureResult, } from '../types/service'; @@ -44,8 +45,13 @@ export class IMService extends BaseServic }, }); } - async verifySignatureLoop(params: VerifySignatureParams, times = 0): IMServiceCommon { + async verifySignatureLoop( + generateVerifyData: VerifySignatureLoopParams, + times = 0, + ): IMServiceCommon { try { + const params = generateVerifyData(); + if (params === null) throw new Error('user exit'); const result = await this.verifySignature(params); if (result.code === IM_SUCCESS_CODE) return result; if (times === 1) throw result; @@ -53,7 +59,7 @@ export class IMService extends BaseServic console.log('verifySignatureLoop: error', error); } if (times <= 0) await sleep(1000); - return this.verifySignatureLoop(params, times - 1); + return this.verifySignatureLoop(generateVerifyData, times - 1); } getAuthToken(params: GetAuthTokenParams): IMServiceCommon { diff --git a/packages/im/types/index.ts b/packages/im/types/index.ts index 8ff0d7bf19..0a0f864b7d 100644 --- a/packages/im/types/index.ts +++ b/packages/im/types/index.ts @@ -83,6 +83,7 @@ export enum IMStatusEnum { AUTHORIZED = 'authorized', CONNECTED = 'connected', ERROR = 'error', + DESTROY = 'destroy', } export type MessageCount = { diff --git a/packages/im/types/service.ts b/packages/im/types/service.ts index 6f8a433401..78ee11b8a6 100644 --- a/packages/im/types/service.ts +++ b/packages/im/types/service.ts @@ -20,6 +20,8 @@ export type VerifySignatureParams = { caHash: string; }; +export type VerifySignatureLoopParams = () => VerifySignatureParams | null; + export type VerifySignatureResult = { token: string; }; @@ -140,7 +142,7 @@ export type HideChannelParams = { export interface IIMService { verifySignature(params: VerifySignatureParams): IMServiceCommon; - verifySignatureLoop(params: VerifySignatureParams, times?: number): IMServiceCommon; + verifySignatureLoop(params: VerifySignatureLoopParams, times?: number): IMServiceCommon; getAuthToken(params: GetAuthTokenParams): IMServiceCommon; getAuthTokenLoop(params: GetAuthTokenParams, times?: number): IMServiceCommon; getUserInfo(params?: GetUserInfoParams): IMServiceCommon; diff --git a/packages/im/utils/sign.ts b/packages/im/utils/sign.ts index 6edf6f59f1..17fd1b7789 100644 --- a/packages/im/utils/sign.ts +++ b/packages/im/utils/sign.ts @@ -1,10 +1,11 @@ import AElf from 'aelf-sdk'; import { AElfWallet } from '@portkey-wallet/types/aelf'; -export const getVerifyData = (message: string, account: AElfWallet, caHash: string) => { +export const getVerifyData = (account: AElfWallet, caHash: string) => { if (!account.keyPair) { throw new Error('no keyPair'); } + const message = `${Date.now()}`; const hexMsg = AElf.utils.sha256(message); const signature = account.keyPair.sign(Buffer.from(hexMsg, 'hex'), { canonical: true, diff --git a/packages/mobile-app-did/js/hooks/network.ts b/packages/mobile-app-did/js/hooks/network.ts index 504ce88fc1..c2dbae4fda 100644 --- a/packages/mobile-app-did/js/hooks/network.ts +++ b/packages/mobile-app-did/js/hooks/network.ts @@ -10,6 +10,7 @@ import { useResetStore } from '@portkey-wallet/hooks/hooks-ca'; import { useLanguage } from 'i18n/hooks'; import ActionSheet from 'components/ActionSheet'; import { DefaultChainId } from '@portkey-wallet/constants/constants-ca/network-testnet'; +import im from '@portkey-wallet/im'; export function useChangeNetwork(route: RouteProp) { const dispatch = useAppDispatch(); @@ -22,6 +23,7 @@ export function useChangeNetwork(route: RouteProp) { let routeName: keyof RootStackParamList = 'LoginPortkey'; if (logged) routeName = 'Tab'; resetStore(); + im.destroy(); dispatch(changeNetworkType(network.networkType)); if (routeName !== route.name && !(routeName === 'LoginPortkey' && route.name === 'SignupPortkey')) navigationService.reset(routeName); diff --git a/packages/mobile-app-did/js/hooks/useLogOut.ts b/packages/mobile-app-did/js/hooks/useLogOut.ts index 015759687d..3f1555082f 100644 --- a/packages/mobile-app-did/js/hooks/useLogOut.ts +++ b/packages/mobile-app-did/js/hooks/useLogOut.ts @@ -26,6 +26,8 @@ import { ChainId } from '@portkey-wallet/types'; import { getWalletInfo, isCurrentCaHash } from 'utils/redux'; import { resetDappList } from '@portkey-wallet/store/store-ca/dapp/actions'; import { changeDrawerOpenStatus, resetDiscover } from '@portkey-wallet/store/store-ca/discover/slice'; +import im from '@portkey-wallet/im'; +import { resetIm } from '@portkey-wallet/store/store-ca/im/actions'; export default function useLogOut() { const dispatch = useAppDispatch(); @@ -39,6 +41,8 @@ export default function useLogOut() { dispatch(resetDappList(currentNetwork)); dispatch(resetDiscover(currentNetwork)); dispatch(changeDrawerOpenStatus(false)); + im.destroy(); + dispatch(resetIm(currentNetwork)); if (otherNetworkLogged) { dispatch(resetCaInfo(currentNetwork)); diff --git a/packages/mobile-app-did/js/pages/Home/index.tsx b/packages/mobile-app-did/js/pages/Home/index.tsx index cad6367661..457a736a1d 100644 --- a/packages/mobile-app-did/js/pages/Home/index.tsx +++ b/packages/mobile-app-did/js/pages/Home/index.tsx @@ -254,14 +254,15 @@ export default function HomeScreen() { - )} - {typeof props?.data?.status.loading === 'number' && props?.data?.status.loading !== 0 && ( - // TODO progress - <> - )} - - )} */} ); }; -export default PhotoMessage; +export default ImageMessage; diff --git a/packages/im-ui-web/src/InputBar/index.less b/packages/im-ui-web/src/InputBar/index.less index 812d38ed93..a0a8fa8387 100644 --- a/packages/im-ui-web/src/InputBar/index.less +++ b/packages/im-ui-web/src/InputBar/index.less @@ -43,7 +43,7 @@ height: 24px; } } - .has-show-icon.custom-svg { + .has-show-emoji-icon.custom-svg { svg { path { fill: rgba(91, 142, 244, 1); diff --git a/packages/im-ui-web/src/InputBar/index.tsx b/packages/im-ui-web/src/InputBar/index.tsx index a8b7d0400c..7238d3b397 100644 --- a/packages/im-ui-web/src/InputBar/index.tsx +++ b/packages/im-ui-web/src/InputBar/index.tsx @@ -1,4 +1,4 @@ -import { useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; import { Popover } from 'antd'; import { emojiList } from '../assets/index'; @@ -15,27 +15,29 @@ interface IInputBar { export default function InputBar({ moreData, showEmoji = true, onSendMessage, ...props }: IInputBar) { console.log(props); - const [showIcon, setShowIcon] = useState(false); + const [showEmojiIcon, setShowEmojiIcon] = useState(false); const [value, setValue] = useState(''); const inputRef = useRef(null); - - const handleShowIcon = () => { - if (showIcon) { - setShowIcon(false); - } else { - setShowIcon(true); + const [popVisible, setPopVisible] = useState(false); + const clearPop = (e: any) => { + try { + if (e.target.className.indexOf('close-show-emoji-icon') === -1) { + setShowEmojiIcon(false); + } + if (e.target.className.indexOf('close-more-file') === -1) { + setPopVisible(false); + } + } catch (e) { + console.log('e', e); } }; - const handleChange = (e: any) => { setValue(e.target.value); }; - const handleSend = () => { onSendMessage(value); setValue(''); }; - const handleEnterKeyDown = (e: any) => { if (e.keyCode === 13) { e.preventDefault(); @@ -44,11 +46,15 @@ export default function InputBar({ moreData, showEmoji = true, onSendMessage, .. } } }; + useEffect(() => { + document.addEventListener('click', clearPop); + return () => document.removeEventListener('click', clearPop); + }, []); return (
    - {showIcon && ( + {showEmojiIcon && (
    {emojiList.map(item => ( @@ -64,13 +70,16 @@ export default function InputBar({ moreData, showEmoji = true, onSendMessage, .. }> - +
    setPopVisible(!popVisible)}> + +
    ) : ( - + <> )}
    @@ -81,11 +90,17 @@ export default function InputBar({ moreData, showEmoji = true, onSendMessage, .. multiline={true} maxHeight={140} onChange={handleChange} - onFocus={() => setShowIcon(false)} + onFocus={() => setShowEmojiIcon(false)} onKeyDown={handleEnterKeyDown} /> {showEmoji && ( - +
    + setShowEmojiIcon(!showEmojiIcon)} + /> +
    )}
    {value && } diff --git a/packages/im-ui-web/src/MessageItem/index.tsx b/packages/im-ui-web/src/MessageItem/index.tsx index 2fbae5c0a7..eeb0633d1d 100644 --- a/packages/im-ui-web/src/MessageItem/index.tsx +++ b/packages/im-ui-web/src/MessageItem/index.tsx @@ -1,7 +1,7 @@ import React, { useRef } from 'react'; import clsx from 'clsx'; -import PhotoMessage from '../PhotoMessage'; +import ImageMessage from '../ImageMessage'; import TextMessage from '../TextMessage'; import SystemMessage from '../SystemMessage'; import { MessageBoxType } from '../type'; @@ -22,7 +22,7 @@ const MessageItem: React.FC = ({ styles, ...props }) => { ) : ( <> {props.type === 'text' && } - {props.type === 'photo' && } + {props.type === 'image' && } )}
    diff --git a/packages/im-ui-web/src/TextMessage/index.tsx b/packages/im-ui-web/src/TextMessage/index.tsx index 8e47f23c1a..2d28d17a06 100644 --- a/packages/im-ui-web/src/TextMessage/index.tsx +++ b/packages/im-ui-web/src/TextMessage/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Popover } from 'antd'; import { useCopyToClipboard } from 'react-use'; import './index.less'; @@ -12,6 +12,10 @@ import CustomSvg from '../components/CustomSvg'; const TextMessage: React.FC = props => { const showDate = useMemo(() => (props.dateString ? props.dateString : formatTime(props.date as any)), []); const [, setCopied] = useCopyToClipboard(); + const [popVisible, setPopVisible] = useState(false); + const hidePop = () => { + setPopVisible(false); + }; const popoverList = [ { @@ -29,12 +33,18 @@ const TextMessage: React.FC = props => { onClick: () => props?.onDelete?.(`${props.id}`), }, ]; + useEffect(() => { + document.addEventListener('click', hidePop); + return () => document.removeEventListener('click', hidePop); + }, []); return (
    setPopVisible(visible)} showArrow={false} content={}>
    diff --git a/packages/im-ui-web/src/index.ts b/packages/im-ui-web/src/index.ts index 4967536978..85e7238d0d 100644 --- a/packages/im-ui-web/src/index.ts +++ b/packages/im-ui-web/src/index.ts @@ -1,6 +1,6 @@ import ChatList from './ChatList'; import TextMessage from './TextMessage'; -import PhotoMessage from './PhotoMessage'; +import ImageMessage from './ImageMessage'; import Avatar from './Avatar'; import PopoverMenuList from './PopoverMenuList'; import SystemMessage from './SystemMessage'; @@ -14,7 +14,7 @@ export * from './type'; export { ChatList, TextMessage, - PhotoMessage, + ImageMessage, Avatar, PopoverMenuList, SystemMessage, diff --git a/packages/im-ui-web/src/type.d.ts b/packages/im-ui-web/src/type.d.ts deleted file mode 100644 index e0d1d076ef..0000000000 --- a/packages/im-ui-web/src/type.d.ts +++ /dev/null @@ -1,1112 +0,0 @@ -import React from 'react'; - -/** - * IChatItemProps Interface - * @prop id The Chat Item's id and required. - * @prop avatar The Chat Item's avatar and required. - * @prop unread The Chat Item's message unread and optional. - * @prop className The Chat Item's component className and optional. - * @prop alt The Chat Item's avatar alt and optional. - * @prop title The Chat Item's title and optional. - * @prop subtitle The Chat Item's subtitle and optional. - * @prop date The Chat Item's message date and optional. - * @prop dateString The Chat Item's message dateString and optional. - * @prop statusColor The Chat Item's statusColor and optional. - * @prop statusText The Chat Item's statusText and optional. - * @prop muted The Chat Item's muted and optional. - * @prop pin The Chat Item's pin and optional. - * @prop showMute The Chat Item's showMute icon and optional. - * @prop showVideoCall The Chat Item's showVideoCall icon and optional. - * @prop onContextMenu The Chat Item's function onContextMenu(event: React.MouseEvent) and optional. - * @prop onClick The Chat Item's function onClick(event: React.MouseEvent) and optional. - * @prop onClickMute The Chat Item's mute icon function onClickMute(event: React.MouseEvent) and optional. - * @prop onClickVideoCall The Chat Item's videoCall icon function onClickVideoCall(event: React.MouseEvent) and optional. - * @prop onDragOver The Chat Item's drag over function and optional. - * @prop onDragEnter The Chat Item's drag enter function and optional. - * @prop onDrop The Chat Item's drop function and optional. - * @prop onDragLeave The Chat Item's drop leave function and optional. - * @prop onDragComponent The Chat Item's drag component and optional. - * @prop letterItem The Chat Item's avatar letterItem and optional. - */ -export interface IChatItemProps { - id: string | number; - avatar?: string; - letterItem?: string; - unread?: number; - className?: string; - alt?: string; - title: string; - subtitle?: string; - date?: Date; - dateString?: string; - statusColor?: string; - statusText?: string; - muted?: boolean; - showMute?: boolean; - pin?: boolean; - showVideoCall?: boolean; - onContextMenu?: React.MouseEventHandler; - onClick?: React.MouseEventHandler; - onClickMute?: React.MouseEventHandler; - onClickPin?: React.MouseEventHandler; - onClickDelete?: React.MouseEventHandler; - onClickVideoCall?: React.MouseEventHandler; - onDragOver?: Function; - onDragEnter?: Function; - onDrop?: Function; - onDragLeave?: Function; - setDragStates?: Function; - onDragComponent?: any; - customStatusComponents?: React.ElementType[]; -} - -/** - * IChatListProps Interface - * @prop id The ChatList's id and required. - * @prop className The Chat List's component className and optional. - * @prop datasource The Chat List's dataSource is a IChatItemProps array and required. - * @prop cmpRef The Chat List's cmpRef and optional. - * @prop onContextMenu The Chat Item's function onContextMenu(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. - * @prop onClick The Chat Item's function onClick(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. - * @prop onClickMute The Chat Item's function onClickMute(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. - * @prop onClickVideoCall The Chat Item's function onClickVideoCall(item: IChatItemProps, index: number, event: React.MouseEvent) and optional. - * @prop onDragOver The Chat Item's drag over function and optional. - * @prop onDragEnter The Chat Item's drag enter function and optional. - * @prop onDrop The Chat Item's drop function and optional. - * @prop onDragLeave The Chat Item's drop leave function and optional. - * @prop onDragComponent The Chat Item's drag component and optional. - */ -export interface IChatListProps { - id: string | number; - className?: string; - dataSource: IChatItemProps[]; - hasMore?: boolean; - cmpRef?: React.Ref; - onContextMenu?: ChatListEvent; - onClick?: ChatListEvent; - onClickMute?: ChatListEvent; - onClickPin?: ChatListEvent; - onClickDelete?: ChatListEvent; - onClickVideoCall?: ChatListEvent; - onDragOver?: Function; - onDragEnter?: Function; - onDrop?: Function; - onDragLeave?: Function; - onDragComponent?: Function; - loadMore: () => Promise; -} - -/** - * ChatListEvent Type - * @param item The ChatListEvent's item is a IChatItemProps. - * @param index The Chat List's index. - * @param event The Chat List's event. - */ -type ChatListEvent = (item: IChatItemProps, index?: number, event?: React.MouseEvent) => any; - -/** - * - */ -export interface IDefaultProps { - style: { - [key: string]: unknown; - }; - onClick: Function; -} - -/** - * IMessage Interface - * @prop id The Message's id and requried. - * @prop position The Message's position and requried. - * @prop text The Message's text and requried. - * @prop title The Message's title and requried. - * @prop focus The Message's focus and requried. - * @prop date The Message's date and requried. - * @prop dateString The Message's dateString and optional. - * @prop avatar The Message's avatar image and optional. - * @prop titleColor The Message's titleColor and required. - * @prop forwarded The Message's forwarded and required. - * @prop replyButton The Message's replyButton icon and required. - * @prop removeButton The Message's removeButton icon and required. - * @prop status The Message's status icon and required. - * @prop statusTitle The Message's statusTitle and required. - * @prop notch The Message's notch and required. - * @prop copiableDate The Message's copiableDate and optional. - * @prop retracted The Message's retracted and required. - * @prop className The Message's className and optional. - * @prop letterItem The Message's letterItem is a string and optional. - * @prop reply The Message's reply and optional. - */ -export interface IMessage { - id: string | number; - position: string; - text: string; - title: string; - focus?: boolean; - date: number | Date; - dateString?: string; - avatar?: string; - titleColor?: string; - forwarded?: boolean; - replyButton?: boolean; - removeButton?: boolean; - status?: 'waiting' | 'sent' | 'received' | 'read'; - statusTitle?: string; - notch?: boolean; - copiableDate?: boolean; - retracted?: boolean; - className?: string; - letterItem?: string; - reply?: IReplyMessage | any; - type: string; -} - -/** - * IPhotoMessage Interface - * @prop type The Photo Message's type is "photo" and required. - * @prop width The Photo Message's width and optional. - * @prop height The Photo Message's height and optional. - * @prop uri The Photo Message's uri and required. - * @prop alt The Photo Message's alt and optional. - */ -export interface IPhotoMessage extends IMessage { - imgData?: { - status?: IMessageDataStatus; - thumbImgUrl: string; - imgUrl: string; - width?: number; - height?: number; - name?: string; - extension?: string; - size?: number; - id?: string; - alt?: string; - }; -} - -/** - * IPhotoMessageProps Interface - * @prop type The Photo Message's type is "photo" and required. - * @prop message The Photo Message's message is a IPhotoMessage and required. - * @prop onDownload The Photo Message's function onDownload(event: React.MouseEvent) and optional. - * @prop onOpen The Photo Message's function onOpen(event: React.MouseEvent) and optional. - * @prop onLoad The Photo Message's function onLoad(event: React.MouseEvent) and optional. - * @prop onError The Photo Message's function onError(event: React.MouseEvent) and optional. - */ -export interface IPhotoMessageProps extends IPhotoMessage { - onDownload?: React.MouseEventHandler; - onOpen?: React.MouseEventHandler; - onLoad?: React.ReactEventHandler; - onPhotoError?: React.ReactEventHandler; -} - -/** - * IPhotoPreviewMessage Interface - * @prop uri The Photo Preview's uri and required. - * @prop alt The Photo Preview's alt and optional. - */ -export interface IPhotoPreview { - uri: string; - alt?: string; -} - -/** - * IPhotoPreviewMessageProps Interface - * @prop onClose The Photo Preview's function onClose(event: React.MouseEvent) and required. - */ -export interface IPhotoPreviewProps extends IPhotoPreview { - onClose: () => void; -} - -/** - * IReplyMessage Interface extends IMessage - * @prop type The Reply Message's type is "reply" and required. - * @prop photoURL The Reply Message's photoURL and optional. - * @prop message The Reply Message's message and optional. - */ -export interface IReplyMessage extends IMessage { - message: string; - photoURL: string; -} - -/** - * IReplyMessageProps Interface - * @prop type The Reply Message's type is "reply" and required. - * @prop message The Reply Message's message is a IReplyMessage and required. - * @prop onClick The Reply Message's function onClick(event: React.MouseEvent) and optional. - */ -export interface IReplyMessageProps extends IReplyMessage { - onClick?: React.MouseEventHandler; -} - -/** - * IMeetingMessage Interface extens IMessage - * @prop type The Meeting Message's type is "meeting" and required. - * @prop message The Meeting Message's message and optional. - * @prop event The Meeting Message's event and optional. - * @prop record The Meeting Message's record and optional. - */ -export interface IMeetingMessage extends IMessage { - message?: string; - event?: { - title?: string; - avatars?: IAvatarProps[]; - avatarsLimit?: any; - }; - record?: { - avatar: string; - title?: string; - savedBy?: string; - time?: string; - }; -} - -/** - * IMeetingMessageProps Interface - * @prop type The Meeting Message's type is "meeting" and required. - * @prop subject The Meeting Message's subject and optional. - * @prop title The Meeting Message's title and optional. - * @prop date The Meeting Message's date and optional. - * @prop dateString The Meeting Message's dateString and optional. - * @prop collapseTitle The Meeting Message's collapseTitle and optional. - * @prop participants The Meeting Message's participants and optional. - * @prop more The Meeting Message's more and optional. - * @prop message The Meeting Message's message is a IMeetingMessage and required. - * @prop dataSource The Meeting Message's dataSource is a IMeetingMessage array and optional. - * @prop participantsLimit The Meeting Message's participantsLimit and optional. - * @prop onclick The Meeting Message's function onclick(event: React.MouseEvent) and optional. - * @prop onMeetingTitleClick The Meeting Message's function onMeetingTitleClick(item: IMeetingMessage, index: number, event: React.MouseEvent) and optional. - * @prop onMeetingVideoLinkClick The Meeting Message's function onMeetingVideoLinkClick(item: IMeetingMessage, index: number, event: React.MouseEvent) and optional. - * @prop onMeetingMoreSelect The Meeting Message's function onMeetingMoreSelect and optional. - */ -export interface IMeetingMessageProps extends IMeetingMessage { - subject?: string; - dateString?: string; - collapseTitle?: string; - participants?: Array<{ - id?: number | string; - title?: string; - }>; - moreItems?: Array<{ - text?: string; - icon?: { - component?: any; - float?: string; - color?: string; - size?: number; - }; - }>; - dataSource?: IMeetingMessage[]; - participantsLimit?: number; - onClick?: React.MouseEventHandler; - onMeetingTitleClick?: MeetingMessageEvent; - onMeetingVideoLinkClick?: MeetingMessageEvent; - onMeetingMoreSelect?: Function; -} - -/** - * IVideoMessage Interface - * @prop type The Video Message's type is "video" and required. - * @prop videoURL The Video Message's videoURL and required. - * @prop uri The Video Message's uri and required. - * @prop width The Video Message's width and optional. - * @prop height The Video Message's height and optional. - * @prop alt The Video Message's alt and optional. - */ -export interface IVideoMessage extends IMessage { - controlsList: string; - data: { - videoURL?: string; - thumbnailURL?: string; - width?: number; - height?: number; - name?: string; - extension?: string; - size?: string; - alt?: string; - id?: string; - uri?: string; - status?: IMessageDataStatus; - }; -} - -/** - * IVideoMessageProps Interface - * @prop type The Video Message's type is "video" and required. - * @prop message The Video Message's message is a IVideoMessage and required. - * @prop onDownload The Video Message's function onDownload(event: React.MouseEvent) and optional. - * @prop onOpen The Video Message's function onOpen(event: React.MouseEvent) and optional. - * @prop onLoad The Video Message's function onLoad(event: React.MouseEvent) and optional. - * @prop onPhotoError The Video Message's function onPhotoError(event: React.SyntheticEvent) and optional. - */ -export interface IVideoMessageProps extends IVideoMessage { - onDownload?: React.MouseEventHandler; - onOpen?: React.MouseEventHandler; - onLoad?: React.ReactEventHandler; - onPhotoError?: React.ReactEventHandler; -} - -/** - * ISystemMessage Interface extends IMessage - * @prop type The System Message's type is "system" and required. - * @prop text The System Message's text and requried. - */ -export interface ISystemMessage extends IMessage { - text: string; -} - -/** - * ISystemMessageProps Interface - * @prop type The System Message's type is "system" and required. - * @prop message The System Message's message is ISystemMessage and required. - * @prop className The System Message's className and optional. - */ -export interface ISystemMessageProps extends ISystemMessage { - className?: string; -} - -/** - * IAudioMessage Interface extends IMessage - * @prop type The Audio Message's type is "audio" and required. - * @prop audioURL The Audio Message's audio url and required. - * @prop audioType The Audio Message's audio type and optional. - * @prop controlsList The Audio Message's controls list and optional. - */ -export interface IAudioMessage extends IMessage { - data: { - audioURL?: string; - extension?: string; - name?: string; - size?: string; - duration?: number; - id?: string; - audioType?: 'audio/mp3' | string; - controlsList?: string; - }; -} - -/** - * IAudioMessageProps Interface - * @prop type The Audio Message's type is "audio" and required. - * @prop message The Audio Message's message is a IAudioMessage and required. - * @prop audioProps The Audio Message's audioProps and optional. - * @prop customStyle The Audio Message's customStyle and optional. - * @prop onOpen The Audio Message's function onOpen(event: React.MouseEvent) and optional. - * @prop onDownload The Audio Message's function onDownload(event: React.MouseEvent) and optional. - * @prop onLoad The Audio Message's function onLoad(event: React.SyntheticEvent) and optional. - */ -export interface IAudioMessageProps extends IAudioMessage { - audioProps?: { - [key: string]: unknown; - }; - customStyle?: any; - onOpen?: React.MouseEventHandler; - onDownload?: React.MouseEventHandler; - onLoad?: React.ReactEventHandler; -} - -/** - * IFileMessage Interface - * @prop type The File Message's type is "file" and required. - * @prop size The File Message's size and optional. - */ -export interface IFileMessage extends IMessage { - data: { - name?: string; - extension?: string; - size?: string; - id?: string; - uri?: string; - status?: IMessageDataStatus; - }; -} - -/** - * IFileMessageProps Interface - * @prop type The File Message's type is "file" and required. - * @prop message The File Message's message is a IFileMessage and required. - * @prop text The File Message's text and optional. - * @prop onDownload The File Message's function onDownload and optional. - * @prop onOpen The File Message's function onOpen(event: React.MouseEvent) and optional. - */ -export interface IFileMessageProps extends IFileMessage { - onDownload?: Function; - onOpen?: React.MouseEventHandler; -} - -/** - * ILocationMessage Interface - * @prop type The Location Message's type is "location" and required. - * @prop latitude The Location Message's latitude and required. - * @prop longitude The Location Message's longitude and required. - * @prop staticURL The Location Message's static url and required. - * @prop mapURL The Location Message's map url and optional. - */ -export interface ILocationMessage extends IMessage { - data: { - latitude: string; - longitude: string; - staticURL: string; - mapURL?: string; - }; -} - -/** - * ILocationMessageProps Interface - * @prop type The Location Message's type is "location" and required. - * @prop message The Location Message's message is a ILocationMessage and required. - * @prop marker The Location Message's marker and required. - * @prop zoom The Location Message's zoom and required. - * @prop apiKey The Location Message's api key and required. - * @prop className The Location Message's className and optional. - * @prop text The Location Message's text and optional. - * @prop src The Location Message's source and optional. - * @prop target The Location Message's target and optional. - * @prop href The Location Message's href and optional. - * @prop onOpen The Location Message's function onOpen(event: React.MouseEvent) and optional. - * @prop onError The Location Message's function onError(event: React.SyntheticEvent) and optional. - */ -export interface ILocationMessageProps extends ILocationMessage { - markerColor: string; - zoom: string; - apiKey: string; - className?: string; - src?: string; - target?: string; - href?: string; - onOpen?: React.MouseEventHandler; - onError?: React.ReactEventHandler; -} - -/** - * ISpotifyMessage Interface extends IMessage - * @prop type The Spotify Message's type is "spotify" and required. - * @prop uri The Spotify Message's uri and required. - * @prop theme The Spotify Message's theme and optional. - * @prop view The Spotify Message's view and optional. - * @prop width The Spotify Message's width and optional. - * @prop height The Spotify Message's height and optional. - * @prop text The Spotify Message's text and optional. - */ -export interface ISpotifyMessage extends IMessage { - uri: string; - theme?: string; - view?: string; - width?: number | string; - height?: number | string; -} - -/** - * ISpotifyMessageProps Interface - * @prop type The Spotify Message's type is "spotify" and required. - * @prop message The Spotify Message's message is a ISpotifyMessage and required. - */ -export interface ISpotifyMessageProps extends ISpotifyMessage {} - -/** - * IMessageBoxProps Interface - * @prop data The Message Box'es data is a MessageType and required. - * @prop onMessageFocused The Message Box'es onMessageFocused and optional. - * @prop renderAddCmp The Message Box'es renderAddCmp is a component and optional. - * @prop onClick The Message Box'es function onClick(event: React.MouseEvent) and optional. - * @prop onOpen The Message Box'es function onOpen(event: React.MouseEvent) and optional. - * @prop onPhotoError The Message Box'es function onPhotoError(event: React.MouseEvent) and optional. - * @prop onContextMenu The Message Box'es function onContextMenu(event: React.MouseEvent) and optional. - * @prop onForwardClick The Message Box'es function onForwardClick(event: React.MouseEvent) and optional. - * @prop onReplyClick The Message Box'es function onReplyClick(event: React.MouseEvent) and optional. - * @prop onRemoveMessageClick The Message Box'es function onRemoveMessageClick(event: React.MouseEvent) and optional. - * @prop onTitleClick The Message Box'es function onTitleClick(event: React.MouseEvent) and optional. - * @prop onMeetingMessageClick The Message Box'es function onMeetingMessageClick(event: React.MouseEvent) and optional. - * @prop onDownload The Message Box'es function onDownload(event: React.MouseEvent) and optional. - * @prop onMeetingMoreSelect The Message Box'es function onMeetingMoreSelect(event: React.MouseEvent) and optional. - * @prop onMeetingLinkClick The Message Box'es function onMeetingLinkClick(event: React.MouseEvent) and optional. - * @prop onMeetingTitleClick The Message Box'es function onMeetingTitleClick(event: React.MouseEvent) and optional. - * @prop onMeetingVideoLinkClick The Message Box'es function onMeetingVideoLinkClick(event: React.MouseEvent) and optional. - */ -export interface IMessageBoxProps { - onMessageFocused?: any; - renderAddCmp?: JSX.Element | (() => JSX.Element); - onClick?: React.MouseEventHandler; - onOpen?: React.MouseEventHandler; - onDelete?: (id: string) => Promise; - onPhotoError?: React.MouseEventHandler; - onContextMenu?: React.MouseEventHandler; - onForwardClick?: React.MouseEventHandler; - onReplyClick?: React.MouseEventHandler; - onRemoveMessageClick?: React.MouseEventHandler; - onTitleClick?: React.MouseEventHandler; - onReplyMessageClick?: React.MouseEventHandler; - onMeetingMessageClick?: React.MouseEventHandler; - onDownload?: React.MouseEventHandler; - onMeetingMoreSelect?: React.MouseEventHandler; - onMeetingLinkClick?: React.MouseEventHandler; - onMeetingTitleClick?: React.MouseEventHandler; - onMeetingVideoLinkClick?: React.MouseEventHandler; - styles?: React.CSSProperties; - notchStyle?: React.CSSProperties; -} - -/** - * IMessageListProps Interface - * @prop className The Message List's className and optional. - * @prop customProps The Message List's customProps and optional. - * @prop children The Message List's children and optional. - * @prop referance The Message List's referance is a ref and required. - * @prop datasource The Message List's datasource is IMessageBoxProps and required. - * @prop lockable The Message List's lockable and required. - * @prop toBottomHeight The Message List's to bottom height and optional. - * @prop down button The Message List's down button and required. - * @prop downButtonBadge The Message List's down button badge and required. - * @prop sendMessagePreview The Message List's send message preview and required. - * @prop onScroll The Message List's function onScroll(event: React.UIEvent) and optional. - * @prop onContextMenu The Message List's function onContextMenu(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onDownButtonClick The Message List's onDownButtonClick is a ref and optional. - * @prop onOpen The Message List's function onOpen(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onDownload The Message List's function onDownload(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onPhotoError The Message List's function onPhotoError(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onMeetingMoreSelect The Message List's function onMeetingMoreSelect(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onMessageFocused The Message List's function onMessageFocused(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onClick The Message List's function onClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onForwardClick The Message List's function onForwardClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onReplyClick The Message List's function onReplyClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onReplyMessageClick The Message List's function onReplyMessageClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onTitleClick The Message List's function onTitleClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onRemoveMessageClick The Message List's function onRemoveMessageClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onMeetingMessageClick The Message List's function onMeetingMessageClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - * @prop onMeetingTitleClick The MessageList's function onMeetingTitleClick(event: React.MouseEvent) and optional. - * @prop onMeetingVideoLinkClick The MessageList's function onMeetingVideoLinkClick(event: React.MouseEvent) and optional. - * @prop onMeetingLinkClick The Message List's function onMeetingLinkClick(item: IMessageBoxProps, index: number, event: React.MouseEvent) and optional. - */ -export interface IMessageListProps { - className?: string; - customProps?: { - [key: string]: unknown; - }; - children?: React.ReactNode; - isShowChild?: boolean; - referance: any; - dataSource: MessageType[]; - actionButtons?: MeetingLinkActionButtons[]; - lockable: boolean; - toBottomHeight?: String | number; - downButton?: boolean; - downButtonBadge?: number; - sendMessagePreview?: boolean; - hasNext: boolean; - loading: boolean; - next: () => any; - onScroll?: React.UIEventHandler; - onContextMenu?: MessageListEvent; - onDelete?: (id: string) => Promise; - onDownButtonClick?: React.RefObject; - onOpen?: MessageListEvent; - onDownload?: MessageListEvent; - onPhotoError?: MessageListEvent; - onMeetingMoreSelect?: MessageListEvent; - onMessageFocused?: MessageListEvent; - onClick?: MessageListEvent; - onForwardClick?: MessageListEvent; - onReplyClick?: MessageListEvent; - onReplyMessageClick?: MessageListEvent; - onTitleClick?: MessageListEvent; - onRemoveMessageClick?: MessageListEvent; - onMeetingMessageClick?: MessageListEvent; - onMeetingTitleClick?: React.MouseEventHandler; - onMeetingVideoLinkClick?: React.MouseEventHandler; - onMeetingLinkClick?: MessageListEvent; - messageBoxStyles?: React.CSSProperties; - notchStyle?: React.CSSProperties; -} - -/** - * MessageListEvent Type - * @param item The MessageListEvent's item is a IMessageBoxProps. - * @param index The MessageListEvent's index. - * @param event The MessageListEvent's event. - */ -export type MessageListEvent = (item: MessageType, index: number, event: React.MouseEvent) => any; - -/** - * IProgressOptions Interface - * @prop state The Progress Options's state is a object. - */ -export interface IProgressOptions { - state?: { - color?: string; - width?: string; - }; -} - -/** - * IMeetingLinkMessage Interface extends IMessage - * @prop meetingID The Meeting Link Message's meeting id and optional. - * @prop title The Meeting Link Message's title and optional. - */ -export interface IMeetingLinkMessage extends IMessage { - meetingID?: string; -} - -export type TActionButton = React.FunctionComponent; - -export interface MeetingLinkActionButtons { - // return meeting id - onClickButton: (id: string) => void; - Component: TActionButton; -} - -/** - * IMeetingLinkMessageProps Interface - * @prop type The Meeting Link Message's type is "meetingLink" and required. - * @prop message The Meeting Link Message's message is a IMeetingLinkMessage and required. - * @prop onMeetingLinkClick The Meeting Link Message's function onMeetingLinkClick(event: React.MouseEvent) and optional. - * @prop onMeetingMoreSelect The Meeting More Select Message's function onMeetingMoreSelect(event: React.MouseEvent) and optional. - */ -export interface IMeetingLinkMessageProps extends IMeetingLinkMessage { - actionButtons?: MeetingLinkActionButtons[]; -} - -/** - * MeetingMessageEvent Type - * @param item The MessageListEvent's item is a IMeetingMessage. - * @param index The MessageListEvent's index. - * @param event The MessageListEvent's event. - */ -type MeetingMessageEvent = (item: IMeetingMessage, index: number, event: React.MouseEvent) => any; - -/** - * ITextMessage Interface extends IMessage - * @prop type The Text Message's type is "text" and required. - */ -export interface ITextMessage extends IMessage {} - -/** - * ITextMessageProps Interface - * @prop type The Text Message's type is "text" and required. - * @prop message The Text Message's message is a ITextMessage and required. - * @prop dateString The Text Message's dateString and optional. - */ -export interface ITextMessageProps extends ITextMessage { - dateString?: string; - // copyClipboard: function; -} - -export interface IMeetingListProps { - cmpRef?: any; - className?: string; - dataSource?: IMeetingItemProps[]; - onClick?: MeetingListEvent; - onMeetingClick?: MeetingListEvent; - onShareClick?: MeetingListEvent; - onCloseClick?: MeetingListEvent; - onContextMenu?: MeetingListEvent; -} - -/** - * MeetingListEvent Type - * @param item The MessageListEvent's item is a IMeetingItemProps. - * @param index The MessageListEvent's index. - * @param event The MessageListEvent's event. - */ -type MeetingListEvent = (item: IMeetingItemProps, index: number, event: React.MouseEvent) => any; - -/** - * IMeetingItemProps Interface - * @prop id The Meeting Item's id and required. - * @prop closable The Meeting Item's closable and optional. - * @prop date The Meeting Item's date and optional. - * @prop subject The Meeting Item's subject and optional. - * @prop subjectLimit The Meeting Item's subject limit and optional. - * @prop alt The Meeting Item's alt and optional. - * @prop title The Meeting Item's title and optional. - * @prop subtitle The Meeting Item's subtitle and optional. - * @prop classname The Meeting Item's classname and optional. - * @prop dateString The Meeting Item's date string and optional. - * @prop avatarLimit The Meeting Item's avatar limit and optional. - * @prop avatars The Meeting Item's avatars array and optional. - * @prop audioMuted The Meeting Item's audio muted and optional. - * @prop audioSource The Meeting Item's audio source and optional. - * @prop onClick The Meeting Item's function onClick(event: React.MouseEvent) and optional. - * @prop onMeetingClick The Meeting Item's function onMeetingClick(event: React.MouseEvent) and optional. - * @prop onShareClick The Meeting Item's function onShareClick(event: React.MouseEvent) and optional. - * @prop onContextMenu The Meeting Item's function onContextMenu(event: React.MouseEvent) and optional. - * @prop onCloseClick The Meeting Item's function onCloseClick(event: React.MouseEvent) and optional. - */ -export interface IMeetingItemProps { - id: string | number; - closable?: boolean; - date?: any; - subject?: string; - subjectLimit?: number; - alt?: string; - title?: string; - subtitle?: string; - className?: string; - dateString?: string; - avatarLimit?: number; - avatars?: IAvatarProps[]; - audioMuted?: boolean; - audioSource?: string; - onClick?: React.MouseEventHandler; - onMeetingClick?: React.MouseEventHandler; - onShareClick?: React.MouseEventHandler; - onContextMenu?: React.MouseEventHandler; - onCloseClick?: React.MouseEventHandler; -} - -/** - * IInputProps Interface - * @prop autofocus The Input's autofocus and optional. - * @prop referance The Input's referance is a ref and optional. - * @prop clear The Input's clear and optional. - * @prop maxlength The Input's maxlength and optional. - * @prop maxHeight The Input's maxheight and optional. - * @prop onMaxLengthExceed The Input's onMaxLengthExceed function and optional. - * @prop onChange The Input's onChange function and optional. - * @prop multiline The Input's multiline and optional. - * @prop autoHeight The Input's autoHeight and optional. - * @prop minHeight The Input's minheight and optional. - * @prop className The Input's classname and optional. - * @prop leftButtons The Input's leftbuttons is a component and optional. - * @prop rightButtons The Input's rightbuttons is a component and optional. - * @prop type The Input's type and optional. - * @prop placeholder The Input's placeholder and optional. - * @prop defaultValue The Input's default value and optional. - * @prop inputStyle The Input's input style and optional. - * @prop onCopy The Input's function onCopy(event: React.ClipboardEvent) and optional. - * @prop onCut The Input's function onCut(event: React.ClipboardEvent) and optional. - * @prop onPaste The Input's function onPaste(event: React.ClipboardEvent) and optional. - * @prop onBlur The Input's function onBlur(event: React.FocusEvent) and optional. - * @prop onFocus The Input's function onFocus(event: React.FocusEvent) and optional. - * @prop onSelect The Input's function onSelect(event: React.SyntheticEvent) and optional. - * @prop onSubmit The Input's function onSubmit(event: React.FormEvent) and optional. - * @prop onReset The Input's function onReset(event: React.FormEvent) and optional. - * @prop onKeyDown The Input's function onKeyDown(event: React.KeyboardEvent) and optional. - * @prop onKeyPress The Input's function onKeyPress(event: React.KeyboardEvent) and optional. - * @prop onKeyUp The Input's function onKeyUp(event: React.KeyboardEvent) and optional. - */ -export interface IInputProps { - autofocus?: boolean; - referance?: any; // sor ve 46.satır - clear?: Function; - maxlength?: number; - maxHeight: number; - onMaxLengthExceed?: Function; - onChange?: Function; - multiline?: boolean; - autoHeight?: boolean; - minHeight?: number; - className?: string; - leftButtons?: React.ReactNode; - rightButtons?: React.ReactNode; - type?: React.HTMLInputTypeAttribute; - placeholder?: string; - defaultValue?: string; - inputStyle?: Object; - value?: string; - onCopy?: React.ClipboardEventHandler; - onCut?: React.ClipboardEventHandler; - onPaste?: React.ClipboardEventHandler; - onBlur?: React.FocusEventHandler; - onFocus?: React.FocusEventHandler; - onSelect?: React.ReactEventHandler; - onSubmit?: React.FormEventHandler; - onReset?: React.FormEventHandler; - onKeyDown?: React.KeyboardEventHandler; - onKeyPress?: React.KeyboardEventHandler; - onKeyUp?: React.KeyboardEventHandler; -} - -/** - * IMessageDataStatus Interface - * @prop error The File Message Data Status's error and optional. - * @prop download The File Message Data Status's download function and optional. - * @prop click The File Message Data Status's click function and optional. - * @prop loading The File Message Data Status's loading and optional. - */ -export interface IMessageDataStatus { - autoDownload?: boolean; - error?: boolean; - download?: Function | boolean; - click?: Function | boolean; - loading?: boolean | number; -} - -/** - * IDropdownProps Interface - * @prop className The Dropdown's className and optional. - * @prop buttonProps The Dropdown's button props and optional. - * @prop animationType The Dropdown's animation type and optional. - * @prop animationPosition The Dropdown's animation position and optional. - * @prop title The Dropdown's title and optional. - * @prop items The Dropdown's items is a IDropdownItemType array and required. - * @prop onSelect The Dropdown's onSelect function and optional. - * @prop style The Dropdown's style is an object containing color, borderColor and optional. - */ -export interface IDropdownProps { - className?: string; - buttonProps?: Object; - animationType?: string; - animationPosition?: string; - title?: string; - items: IDropdownItemType[]; - onSelect: Function; - style?: { - color?: string; - borderColor?: string; - }; -} - -/** - * ICircleProps Interface - * @prop animate The Circle's animation and required. - * @prop progressOptions The Circle's progress options and optional. - * @prop className The Circle's className and optional. - */ -export interface ICircleProps { - animate: number; - progressOptions?: Object; - className?: string; -} - -/** - * IButtonProps Interface - * @prop title The Button's title and optional. - * @prop text The Button's text and optional. - * @prop buttonRef The Button's ref and optional. - * @prop type The Button's type and optional. - * @prop className The Button's className and optional. - * @prop backgroundColor The Button's background color and optional. - * @prop color The Button's color and optional. - * @prop disabled The Button's disabled and optional. - * @prop onClick The Button's onClick function and optional. - * @prop icon The Button's icon is a IButtonIcon and optional. - */ -export interface IButtonProps { - title?: string; - text?: string; - buttonRef?: React.RefObject; - type?: string; - className?: string; - backgroundColor?: string; - color?: string; - disabled?: boolean; - onClick?: React.MouseEventHandler; - icon?: IButtonIcon; -} - -/** - * IButtonIcon Interface - * @prop float The Button Icon's float and optional. - * @prop size The Button Icon's size and optional. - * @prop components The Button Icon's components and optional. - */ -export interface IButtonIcon { - float?: any; - size?: number; - component?: React.ReactChild; -} - -/** - * IDropDownItemType Type - * @type IDropdown - * @type string - */ -type IDropdownItemType = IDropdownItem | string; - -/** - * IDropdownItem Interface - * @prop icon The Dropdown Item's icon and optional. - * @prop text The Dropdown Item's text and optional. - */ -export interface IDropdownItem { - icon?: IDropdownItemIcon; - text?: string; -} - -/** - * IDropdownItemIcon Interface - * @prop float The Dropdown Item Icon's float and optional. - * @prop color The Dropdown Item Icon's color and optional. - * @prop size The Dropdown Item Icon's size and optional. - * @prop className The Dropdown Item Icon's className and optional. - * @prop component The Dropdown Item Icon's component and optional. - */ -export interface IDropdownItemIcon { - float?: any; - color?: string; - size?: number; - className?: string; - component?: React.ReactChild; -} - -/** - * ISideBarProps Interface - * @type type The Side Bar's type and optional. - * @type data The Side Bar's data is ISideBar and optional. - */ -export interface ISideBarProps extends ISideBar { - type?: string; - data: ISideBar; -} - -/** - * ISideBar Interface - * @prop top The Side Bar's top is a component and optional. - * @prop center The Side Bar's center is a component and optional. - * @prop bottom The Side Bar's bottom is a component and optional. - * @prop className The Side Bar's className and optional. - */ -export interface ISideBar { - top?: any; - center?: any; - bottom?: any; - className?: string; -} - -/** - * IPopup Interface - * @prop show The Popup's show and optional. - * @prop header The Popup's header and optional. - * @prop text The Popup's text and optional. - * @prop footerButtons The Popup's footer buttons array and optional. - * @prop headerButtons The Popup's header buttons array and optional. - * @prop renderHeader The Popup's renderHeader function and optional. - * @prop renderContent The Popup's renderContent function and optional. - * @prop renderFooter The Popup's renderFooter function and optional. - * @prop color The Popup's color and optional. - */ -export interface IPopup { - show?: boolean; - header?: string; - text?: string; - footerButtons?: Array<{ - color?: string; - backgroundColor?: string; - text?: string; - onClick?: React.MouseEventHandler; - }>; - headerButtons?: Array<{ - type?: string; - color?: string; - icon?: { - component?: React.ReactChild; - size?: number; - }; - onClick?: React.MouseEventHandler; - }>; - renderHeader?: Function; - renderContent?: Function; - renderFooter?: Function; - color?: string; -} - -/** - * IPopupProps Interface - * @prop popup The Popup's popup is a IPopup and required. - * @prop type The Popup's type and optional. - * @prop className The Popup's className and optional. - */ -export interface IPopupProps { - popup: IPopup; - type?: string; - className?: string; -} - -/** - * IAvatarProps Interface - * @prop src The Avatar's src is an image source and required. - * @prop letterItem The Avatar's letterItem is a string and optional. - * @prop className The Avatar's className and optional. - * @prop alt The Avatar's alt and optional. - */ -export interface IAvatarProps { - src?: string; - letterItem?: string; - className?: string; - alt?: string; -} - -/** - * INavbarProps Interface - * @prop type The Navbar's type and optional. - * @prop className The Navbar's className and optional. - * @prop top The Side Bar's top is a component and optional. - * @prop center The Side Bar's center is a component and optional. - * @prop bottom The Side Bar's bottom is a component and optional. - */ -export interface INavbarProps { - type?: string; - className?: string; - left?: any; - center?: any; - right?: any; -} - -/** - * IUnreadTipProps Interface - * @prop unread The UnreadTip's unread number and required. - * @prop muted The UnreadTip's muted and optional. - * @prop bgColorString The UnreadTip's bgColorString and optional. - */ -export interface IUnreadTipProps { - unread: number; - muted?: boolean; - bgColorString?: string; -} - -/** - * MessageType Type - * @type ILocationMessageProps - * @type IPhotoMessageProps - * @type IVideoMessageProps - * @type ISpotifyMessageProps - * @type IAudioMessageProps - * @type IMeetingLinkMessageProps - * @type IFileMessageProps - * @type ITextMessageProps - * @type ISystemMessageProps - * @type IMeetingMessageProps - */ -export type MessageType = - | ({ type: 'location' } & ILocationMessageProps) - | ({ type: 'photo' } & IPhotoMessageProps) - | ({ type: 'video' } & IVideoMessageProps) - | ({ type: 'spotify' } & ISpotifyMessageProps) - | ({ type: 'audio' } & IAudioMessageProps) - | ({ type: 'meetingLink' } & IMeetingLinkMessageProps) - | ({ type: 'file' } & IFileMessageProps) - | ({ type: 'text' } & ITextMessageProps) - | ({ type: 'bookmark' } & ITextMessageProps) - | ({ type: 'system' } & ISystemMessageProps) - | ({ type: 'meeting' } & IMeetingMessageProps); - -export type MessageBoxType = MessageType & IMessageBoxProps; - -export class ChannelItem extends React.Component {} -export class ChannelList extends React.Component {} -export class MessageBox extends React.Component {} -export class LocationMessage extends React.Component {} -export class PhotoMessage extends React.Component {} -export class VideoMessage extends React.Component {} -export class SpotifyMessage extends React.Component {} -export class AudioMessage extends React.Component {} -export class MeetingLink extends React.Component {} -export class FileMessage extends React.Component {} -export class TextMessage extends React.Component {} -export class SystemMessage extends React.Component {} -export class ReplyMessage extends React.Component {} -export class MeetingMessage extends React.Component {} -export class MeetingItem extends React.Component {} -export class MeetingList extends React.Component {} -export class MessageList extends React.Component {} - -export class Popup extends React.Component {} -export class Avatar extends React.Component {} -export class Button extends React.Component {} -export class Sidebar extends React.Component {} -export class Navbar extends React.Component {} -export class Input extends React.Component {} -export class Dropdown extends React.Component {} -export class Circle extends React.Component {} -export class UnreadTip extends React.Component {} -export class PhotoPreview extends React.Component {} diff --git a/packages/im-ui-web/src/type.ts b/packages/im-ui-web/src/type.ts index 349de1c503..dda9b3dcca 100644 --- a/packages/im-ui-web/src/type.ts +++ b/packages/im-ui-web/src/type.ts @@ -162,14 +162,14 @@ export interface IMessage { } /** - * IPhotoMessage Interface - * @prop type The Photo Message's type is "photo" and required. + * IImageMessage Interface + * @prop type The Photo Message's type is "image" and required. * @prop width The Photo Message's width and optional. * @prop height The Photo Message's height and optional. * @prop uri The Photo Message's uri and required. * @prop alt The Photo Message's alt and optional. */ -export interface IPhotoMessage extends IMessage { +export interface IImageMessage extends IMessage { imgData?: { status?: IMessageDataStatus; thumbImgUrl?: string; @@ -185,15 +185,15 @@ export interface IPhotoMessage extends IMessage { } /** - * IPhotoMessageProps Interface - * @prop type The Photo Message's type is "photo" and required. - * @prop message The Photo Message's message is a IPhotoMessage and required. + * IImageMessageProps Interface + * @prop type The Photo Message's type is "image" and required. + * @prop message The Photo Message's message is a IImageMessage and required. * @prop onDownload The Photo Message's function onDownload(event: React.MouseEvent) and optional. * @prop onOpen The Photo Message's function onOpen(event: React.MouseEvent) and optional. * @prop onLoad The Photo Message's function onLoad(event: React.MouseEvent) and optional. * @prop onError The Photo Message's function onError(event: React.MouseEvent) and optional. */ -export interface IPhotoMessageProps extends IPhotoMessage { +export interface IImageMessageProps extends IImageMessage { onDownload?: React.MouseEventHandler; onOpen?: React.MouseEventHandler; onLoad?: React.ReactEventHandler; @@ -668,7 +668,7 @@ export interface IUnreadTipProps { /** * MessageType Type * @type ILocationMessageProps - * @type IPhotoMessageProps + * @type IImageMessageProps * @type IVideoMessageProps * @type ISpotifyMessageProps * @type IAudioMessageProps @@ -679,7 +679,7 @@ export interface IUnreadTipProps { * @type IMeetingMessageProps */ export type MessageType = - | ({ type: 'photo' } & IPhotoMessageProps) + | ({ type: 'image' } & IImageMessageProps) | ({ type: 'text' } & ITextMessageProps) | ({ type: 'bookmark' } & ITextMessageProps) | ({ type: 'system' } & ISystemMessageProps); @@ -689,17 +689,11 @@ export type MessageBoxType = MessageType & IMessageBoxProps; export class ChannelItem extends React.Component {} export class ChannelList extends React.Component {} export class MessageBox extends React.Component {} -export class PhotoMessage extends React.Component {} +export class PhotoMessage extends React.Component {} export class TextMessage extends React.Component {} export class SystemMessage extends React.Component {} export class MessageList extends React.Component {} - -// export class Popup extends React.Component {} export class Avatar extends React.Component {} -// export class Button extends React.Component {} -// export class Sidebar extends React.Component {} -// export class Navbar extends React.Component {} export class Input extends React.Component {} -// export class Dropdown extends React.Component {} export class UnreadTip extends React.Component {} export class PhotoPreview extends React.Component {} diff --git a/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.less b/packages/web-extension-did/app/web/pages/ChatBox/components/ImageSendModal/index.less similarity index 96% rename from packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.less rename to packages/web-extension-did/app/web/pages/ChatBox/components/ImageSendModal/index.less index a4f90d1a1b..efe2ca49cb 100644 --- a/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.less +++ b/packages/web-extension-did/app/web/pages/ChatBox/components/ImageSendModal/index.less @@ -1,6 +1,6 @@ @import '../../../../assets/theme/color.less'; -.portkey-photo-send-modal { +.portkey-image-send-modal { .portkey-modal-header { display: none; } diff --git a/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.tsx b/packages/web-extension-did/app/web/pages/ChatBox/components/ImageSendModal/index.tsx similarity index 89% rename from packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.tsx rename to packages/web-extension-did/app/web/pages/ChatBox/components/ImageSendModal/index.tsx index 54e828b06b..3dfe38fa21 100644 --- a/packages/web-extension-did/app/web/pages/ChatBox/components/PhotoSendModal/index.tsx +++ b/packages/web-extension-did/app/web/pages/ChatBox/components/ImageSendModal/index.tsx @@ -12,7 +12,7 @@ interface PhotoSendModalProps { onCancel: () => void; } -const PhotoSendModal = forwardRef(({ open, url, onConfirm, onCancel }: PhotoSendModalProps, ref) => { +const ImageSendModal = forwardRef(({ open, url, onConfirm, onCancel }: PhotoSendModalProps, ref) => { const { t } = useTranslation(); const [loading, setLoading] = useState(false); @@ -27,7 +27,7 @@ const PhotoSendModal = forwardRef(({ open, url, onConfirm, onCancel }: PhotoSend return ( { init(); }); - console.log('info', info); const relationId = useRelationId(); const messageList: MessageType[] = useMemo(() => { const formatList: MessageType[] = []; @@ -114,9 +113,12 @@ export default function Session() { centered: true, okText: t('Confirm'), cancelText: t('Cancel'), - onOk: exit, + onOk: () => { + exit(); + navigate('/chat-list'); + }, }); - }, [exit, t]); + }, [exit, navigate, t]); const handleAddContact = useCallback(async () => { try { const res = await addContactApi(info?.toRelationId || ''); @@ -243,11 +245,6 @@ export default function Session() { overlayClassName="chat-box-popover" trigger="click" showArrow={false} - // onOpenChange={(visible) => { - // console.log('visible', visible); - // // setPopVisible(visible); - // }} - // onClick={() => setPopVisible(true)} content={}>
    setPopVisible(true)}> diff --git a/packages/web-extension-did/app/web/pages/NewChat/index.tsx b/packages/web-extension-did/app/web/pages/NewChat/index.tsx index b3a9ab2166..51b003617e 100644 --- a/packages/web-extension-did/app/web/pages/NewChat/index.tsx +++ b/packages/web-extension-did/app/web/pages/NewChat/index.tsx @@ -24,11 +24,13 @@ export default function ChatListSearch() { const handleSearch = useCallback( async (keyword: string) => { if (!keyword) { - setChatList([]); - } else { const { contactFilterList = [] } = localSearch(keyword, ContactsTab.ALL); console.log('searchResult', contactFilterList); setChatList(contactFilterList); + // setChatList([]); + } else { + const { contactFilterList = [] } = localSearch(keyword, ContactsTab.Chats); + setChatList(contactFilterList); } }, [localSearch], From a54e79a923b4a9de0580c944f140d9aa741bb617 Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 22 Aug 2023 17:15:52 +0800 Subject: [PATCH 574/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20contact=20?= =?UTF-8?q?api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/api-did/contact/index.ts | 13 +++- packages/hooks/hooks-ca/contact.ts | 14 ++++ packages/types/types-ca/contact.ts | 3 +- .../app/web/hooks/useProfile.ts | 16 ++--- .../web/pages/Contacts/AddContact/index.tsx | 13 +++- .../Contacts/EditContact/Popup/index.less | 4 ++ .../Contacts/EditContact/Prompt/index.less | 5 ++ .../web/pages/Contacts/EditContact/index.tsx | 3 +- .../pages/Contacts/FindMore/Popup/index.less | 7 ++ .../pages/Contacts/FindMore/Popup/index.tsx | 4 ++ .../pages/Contacts/FindMore/Prompt/index.less | 7 ++ .../pages/Contacts/FindMore/Prompt/index.tsx | 4 ++ .../app/web/pages/Contacts/FindMore/index.tsx | 16 ++++- .../web/pages/Contacts/ViewContact/index.tsx | 66 +++++++++++++++---- .../components/FindMoreItem/index.less | 1 + 15 files changed, 145 insertions(+), 31 deletions(-) diff --git a/packages/api/api-did/contact/index.ts b/packages/api/api-did/contact/index.ts index aa0c57fdb3..fdeb806439 100644 --- a/packages/api/api-did/contact/index.ts +++ b/packages/api/api-did/contact/index.ts @@ -1,7 +1,14 @@ import { BaseConfig } from '../../types'; const BASE_URL = `/api/app/contacts`; -const KeyList = ['addContact', 'editContact', 'deleteContact', 'checkContactName', 'readImputation'] as const; +const KeyList = [ + 'addContact', + 'editContact', + 'deleteContact', + 'checkContactName', + 'readImputation', + 'profile', +] as const; const ApiObject: Record = { addContact: { @@ -24,6 +31,10 @@ const ApiObject: Record = { target: `${BASE_URL}/read`, config: { method: 'POST' }, }, + profile: { + target: `/api/v1/contacts/profile`, + config: { method: 'GET' }, + }, }; export default ApiObject; diff --git a/packages/hooks/hooks-ca/contact.ts b/packages/hooks/hooks-ca/contact.ts index 3e1a8ba369..5f19f17c52 100644 --- a/packages/hooks/hooks-ca/contact.ts +++ b/packages/hooks/hooks-ca/contact.ts @@ -259,3 +259,17 @@ export const useIsMyContact = () => { [contactPortkeyIdMap, contactRelationIdMap], ); }; + +export const useGetProfile = () => { + const currentNetworkInfo = useCurrentNetworkInfo(); + return useCallback( + async ({ id, relationId }: { id: string; relationId: string }): Promise => { + const response = await request.contact.profile({ + baseURL: currentNetworkInfo.apiUrl, + params: { id, relationId }, + }); + return response; + }, + [currentNetworkInfo.apiUrl], + ); +}; diff --git a/packages/types/types-ca/contact.ts b/packages/types/types-ca/contact.ts index 13255e1767..b2563cae29 100644 --- a/packages/types/types-ca/contact.ts +++ b/packages/types/types-ca/contact.ts @@ -29,8 +29,9 @@ export interface ContactItemType { export interface EditContactItemApiType { name: string; - id: string; + id?: string; relationId?: string; + addresses?: AddressItem[]; } export interface AddContactItemApiType { diff --git a/packages/web-extension-did/app/web/hooks/useProfile.ts b/packages/web-extension-did/app/web/hooks/useProfile.ts index cf28ce2a0e..4987de1783 100644 --- a/packages/web-extension-did/app/web/hooks/useProfile.ts +++ b/packages/web-extension-did/app/web/hooks/useProfile.ts @@ -22,8 +22,8 @@ export const useGoProfileEdit = () => { const navigate = useNavigate(); return useCallback( - (chat: ExtraType, state: any) => { - navigate(`/setting/contacts/edit/${chat}`, { state }); + (extra: ExtraType, state: any) => { + navigate(`/setting/contacts/edit/${extra}`, { state }); }, [navigate], ); @@ -33,8 +33,8 @@ export const useGoMyProfileEdit = () => { const navigate = useNavigate(); return useCallback( - (chat: ExtraType, state: any) => { - navigate(`/setting/wallet/edit/${chat}`, { state }); + (extra: ExtraType, state: any) => { + navigate(`/setting/wallet/edit/${extra}`, { state }); }, [navigate], ); @@ -44,17 +44,13 @@ export const useGoAddNewContact = () => { const navigate = useNavigate(); return useCallback( - (chat: ExtraType, state: any) => { - navigate(`/setting/contacts/add/${chat}`, { state }); + (extra: ExtraType, state: any) => { + navigate(`/setting/contacts/add/${extra}`, { state }); }, [navigate], ); }; -export const useGoAddContact = () => { - // TODO api -}; - export const useProfileChat = () => { const navigate = useNavigate(); const { isPrompt } = useCommonState(); diff --git a/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx index 6b69b8af23..0824383d38 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/AddContact/index.tsx @@ -7,7 +7,7 @@ import { fetchContactListAsync } from '@portkey-wallet/store/store-ca/contact/ac import { useAppDispatch, useLoading } from 'store/Provider/hooks'; import { getAelfAddress, isAelfAddress } from '@portkey-wallet/utils/aelf'; import { isValidCAWalletName } from '@portkey-wallet/utils/reg'; -import { useAddContact, useCheckContactName } from '@portkey-wallet/hooks/hooks-ca/contact'; +import { useAddContact, useCheckContactName, useEditContact } from '@portkey-wallet/hooks/hooks-ca/contact'; import { transNetworkText } from '@portkey-wallet/utils/activity'; import { useIsTestnet } from 'hooks/useNetwork'; import { IAddContactFormProps } from '../components/AddContactForm'; @@ -57,6 +57,7 @@ export default function AddContact() { const [validName, setValidName] = useState({ validateStatus: '', errorMsg: '' }); const [addressArr, setAddressArr] = useState(state?.addresses); const addContactApi = useAddContact(); + const editContactApi = useEditContact(); const checkExistNameApi = useCheckContactName(); const { setLoading } = useLoading(); @@ -190,7 +191,15 @@ export default function AddContact() { const handleView = useGoProfile(); const requestAddContact = useCallback( async (name: string, addresses: AddressItem[]) => { - const contactDetail = await addContactApi({ name: name.trim(), addresses }); + let contactDetail = {} as ContactItemType; + if (extra === '2') { + // edit + contactDetail = await editContactApi({ name: name.trim(), addresses }); + } else { + // add extra === '3' + contactDetail = await addContactApi({ name: name.trim(), addresses }); + } + appDispatch(fetchContactListAsync()); if (contactDetail?.imInfo?.relationId) { diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.less b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.less index 868db24117..1350f923f1 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Popup/index.less @@ -14,5 +14,9 @@ .edit-contact-form { overflow-y: overlay; height: calc(100% - 96px); + + .id-and-address .info-section .title-did { + margin-top: 24px; + } } } diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.less b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.less index e9a7dfce07..448a5411e8 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/Prompt/index.less @@ -4,6 +4,11 @@ height: 100%; .edit-contact-form { height: calc(100% - 73px); + + .id-and-address .info-section .title-did { + margin-top: 24px; + } + .form-btn { padding: 16px 24px; } diff --git a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx index 12d11888f6..038bc5688e 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/EditContact/index.tsx @@ -63,6 +63,7 @@ export default function EditContact() { const contactDetail = await editContactApi({ name: remark.trim(), id: state.id, + relationId: state?.imInfo?.relationId, }); appDispatch(fetchContactListAsync()); @@ -96,7 +97,7 @@ export default function EditContact() { setLoading(false); } }, - [appDispatch, editContactApi, handleView, setLoading, state.id, t], + [appDispatch, editContactApi, handleView, setLoading, state, t], ); // go back previous page diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less index f200b8256b..4883c048e7 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.less @@ -24,6 +24,13 @@ .find-more-body { overflow-y: hidden; + .no-search-result { + margin-top: 60px; + line-height: 22px; + color: @font-12; + font-size: 16px; + } + .find-more-body-contact { height: 62px; padding: 0 24px; diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx index 075c1bb77a..7dca8f14a5 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Popup/index.tsx @@ -11,6 +11,7 @@ export default function FindMorePopup({ contact, showChat, isAdded, + isSearch = false, goBack, handleSearch, clickItem, @@ -28,6 +29,9 @@ export default function FindMorePopup({
    My Portkey ID: {myPortkeyId}
    + {(!contact || !contact.name) && isSearch && ( +
    No Search Result
    + )} {contact && contact.name && contact.index && (
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less index fb92aff088..b30245d9f7 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.less @@ -25,6 +25,13 @@ } } .find-more-body { + + .no-search-result { + margin-top: 60px; + line-height: 22px; + color: @font-12; + font-size: 16px; + } .find-more-body-contact { height: 62px; diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx index 5213642591..ddd11a8eed 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/Prompt/index.tsx @@ -10,6 +10,7 @@ export default function FindMorePrompt({ contact, showChat, isAdded, + isSearch = false, goBack, handleSearch, clickItem, @@ -27,6 +28,9 @@ export default function FindMorePrompt({
    My Portkey ID: {myPortkeyId}
    + {(!contact || !contact.name) && isSearch && ( +
    No Search Result
    + )} {contact && contact.name && contact.index && (
    diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx index 8d6f6b8e7d..aa5a84aabd 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx @@ -12,12 +12,14 @@ import { message } from 'antd'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { useContactRelationIdMap } from '@portkey-wallet/hooks/hooks-ca/contact'; import { useIsChatShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { getAddressInfo } from '@portkey-wallet/utils/aelf'; export interface IFindMoreProps extends BaseHeaderProps { myPortkeyId: string; contact: Partial; showChat: boolean; isAdded?: boolean; + isSearch?: boolean; goBack: () => void; handleSearch: ChangeEventHandler; clickItem: () => void; @@ -31,6 +33,7 @@ export default function FindMore() { const { userId } = useWalletInfo(); const contactRelationIdMap = useContactRelationIdMap(); const [isAdded, setIsAdded] = useState(false); + const [isSearch, setIsSearch] = useState(false); const headerTitle = 'Find More'; const [contact, setContact] = useState({}); @@ -46,23 +49,30 @@ export default function FindMore() { // }, const handleSearch = useDebounceCallback(async (e: ChangeEvent) => { - const value = e.target.value; + const value = e.target.value.trim(); if (!value) { setContact({}); setIsAdded(false); + setIsSearch(false); + return; } try { - const res = await im.service.getUserInfo({ address: value }); + const addressTrans = getAddressInfo(value.trim()); + const res = await im.service.getUserInfo({ address: addressTrans.address }); if (res?.data?.portkeyId === userId) { message.error('Unable to add yourself as a contact'); } else { setContact({ ...res?.data, index: res?.data?.name?.substring(0, 1).toLocaleUpperCase() }); setIsAdded(!!contactRelationIdMap?.[res?.data?.relationId]); + setIsSearch(true); } } catch (error) { const err = handleErrorMessage(error, 'handle display error'); message.error(err); + setContact({}); + setIsAdded(false); + setIsSearch(false); } }, []); @@ -94,6 +104,7 @@ export default function FindMore() { contact={contact} showChat={showChat} isAdded={isAdded} + isSearch={isSearch} goBack={goBack} handleSearch={handleSearch} clickItem={() => { @@ -108,6 +119,7 @@ export default function FindMore() { contact={contact} showChat={showChat} isAdded={isAdded} + isSearch={isSearch} goBack={goBack} handleSearch={handleSearch} clickItem={() => { diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index 2512a251dd..d52d4d4ecb 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -2,17 +2,24 @@ import ViewContactPrompt from './Prompt'; import ViewContactPopup from './Popup'; import { useLocation, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; -import { useCallback, useEffect } from 'react'; -import { useCommonState } from 'store/Provider/hooks'; +import { useCallback, useEffect, useState } from 'react'; +import { useCommonState, useWalletInfo } from 'store/Provider/hooks'; import { useProfileChat, useProfileCopy, useGoProfileEdit } from 'hooks/useProfile'; import CustomModal from 'pages/components/CustomModal'; -import { useReadImputation } from '@portkey-wallet/hooks/hooks-ca/contact'; +import { useGetProfile, useIsMyContact, useReadImputation } from '@portkey-wallet/hooks/hooks-ca/contact'; +import { useAddStranger } from '@portkey-wallet/hooks/hooks-ca/im'; +import { handleErrorMessage } from '@portkey-wallet/utils'; +import { message } from 'antd'; export default function ViewContact() { const { isNotLessThan768 } = useCommonState(); const { state } = useLocation(); const navigate = useNavigate(); const { t } = useTranslation(); + const isMyContactFn = useIsMyContact(); + const { userId } = useWalletInfo(); + const getProfile = useGetProfile(); + const [data, setData] = useState(state); const title = t('Details'); const editText = t('Edit'); @@ -20,6 +27,28 @@ export default function ViewContact() { const addedText = t('Added'); const addContactText = t('Add Contact'); + useEffect(() => { + const isMyContact = isMyContactFn({ + userId: data?.userId || data?.portkeyId, + relationId: data?.relationId || data?.imInfo?.relationId, + }); + const isMy = (data?.portkeyId && data.portkeyId === userId) || (data?.userId && data.userId === userId); + if (!isMy && !isMyContact) { + // need fetch profile + const res = getProfile({ id: data?.id, relationId: data?.relationId || data?.imInfo?.relationId }); + console.log('🌈 🌈 🌈 🌈 🌈 🌈 need fetch profile', res); + } + }, [ + getProfile, + isMyContactFn, + data?.id, + data?.imInfo?.relationId, + data.portkeyId, + data?.relationId, + data.userId, + userId, + ]); + const goBack = useCallback(() => { navigate('/setting/contacts'); }, [navigate]); @@ -27,15 +56,24 @@ export default function ViewContact() { const handleEdit = useGoProfileEdit(); const handleChat = useProfileChat(); const handleCopy = useProfileCopy(); - const handleAdd = () => { - console.log('add'); + + const addStranger = useAddStranger(); + const handleAdd = async () => { + try { + const res = await addStranger(data?.imInfo?.relationId || data?.relationId); + console.log('🌈 🌈 🌈 🌈 🌈 🌈 res', res); + setData(res.data); + } catch (error) { + const err = handleErrorMessage(error, 'add stranger error'); + message.error(err); + } }; const readImputationApi = useReadImputation(); useEffect(() => { - if (state?.isImputation) { + if (data?.isImputation) { // imputation from unread to read - readImputationApi(state); + readImputationApi(data); CustomModal({ content: ( @@ -49,7 +87,7 @@ export default function ViewContact() { okText: 'OK', }); } - }, [readImputationApi, state]); + }, [readImputationApi, data]); // TODO btn show logic return isNotLessThan768 ? ( @@ -59,11 +97,11 @@ export default function ViewContact() { chatText={chatText} addedText={addedText} addContactText={addContactText} - data={state} + data={data} goBack={goBack} - handleEdit={() => handleEdit('1', state)} // TODO add or edit 1 2 + handleEdit={() => handleEdit('1', data)} // TODO add or edit 1 2 handleAdd={handleAdd} - handleChat={() => handleChat(state)} + handleChat={() => handleChat(data)} handleCopy={handleCopy} /> ) : ( @@ -73,11 +111,11 @@ export default function ViewContact() { chatText={chatText} addedText={addedText} addContactText={addContactText} - data={state} + data={data} goBack={goBack} - handleEdit={() => handleEdit('1', state)} // TODO add or edit 1 2 + handleEdit={() => handleEdit('1', data)} // TODO add or edit 1 2 handleAdd={handleAdd} - handleChat={() => handleChat(state)} + handleChat={() => handleChat(data)} handleCopy={handleCopy} /> ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/FindMoreItem/index.less b/packages/web-extension-did/app/web/pages/Contacts/components/FindMoreItem/index.less index c2f441b2df..69689ef785 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/FindMoreItem/index.less +++ b/packages/web-extension-did/app/web/pages/Contacts/components/FindMoreItem/index.less @@ -1,6 +1,7 @@ @import '../../../../assets/theme/color.less'; .find-more-item { + width: 100%; padding-right: 24px; .find-more-item-right { .find-more-index-logo { From f1bd43dab1b0bbf00e53f0eee269dd7a323b147b Mon Sep 17 00:00:00 2001 From: Portkey-David Date: Tue, 22 Aug 2023 17:17:02 +0800 Subject: [PATCH 575/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20caht=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/contact.ts | 15 ++++++ .../js/components/ContactList/index.tsx | 41 +++++++-------- .../js/pages/Chat/ChatDetails/index.tsx | 32 ++++++++---- .../js/pages/Chat/ChatHome/index.tsx | 28 ++--------- .../js/pages/Chat/NewChatHome/index.tsx | 50 +++++++++++-------- .../pages/Chat/components/ChatList/index.tsx | 35 ++++++++++--- .../js/pages/Chat/components/Chats/index.tsx | 21 ++++++-- .../components/Message/MessageText/index.tsx | 11 +++- .../Chat/components/SystemTime/index.tsx | 37 ++++++++++++++ .../pages/My/Contacts/ContactsHome/index.tsx | 10 ++-- .../SearchContactListSection/index.tsx | 15 ++++-- 11 files changed, 203 insertions(+), 92 deletions(-) create mode 100644 packages/mobile-app-did/js/pages/Chat/components/SystemTime/index.tsx diff --git a/packages/hooks/hooks-ca/contact.ts b/packages/hooks/hooks-ca/contact.ts index 3e1a8ba369..154d03764d 100644 --- a/packages/hooks/hooks-ca/contact.ts +++ b/packages/hooks/hooks-ca/contact.ts @@ -242,6 +242,21 @@ export const useLocalContactSearch = () => { ); }; +export const useChatContactFlatList = () => { + const { contactIndexList } = useContact(false, false); + return useMemo(() => { + const contactFlatList: ContactItemType[] = []; + contactIndexList.forEach(({ contacts }) => { + contacts.map(contact => { + if (contact.imInfo?.relationId) { + contactFlatList.push(contact); + } + }); + }); + return contactFlatList; + }, [contactIndexList]); +}; + export const useIsMyContact = () => { const { contactPortkeyIdMap, contactRelationIdMap } = useContact(false, false); diff --git a/packages/mobile-app-did/js/components/ContactList/index.tsx b/packages/mobile-app-did/js/components/ContactList/index.tsx index e7e8b361f0..2a5a085594 100644 --- a/packages/mobile-app-did/js/components/ContactList/index.tsx +++ b/packages/mobile-app-did/js/components/ContactList/index.tsx @@ -46,7 +46,6 @@ const ContactsList: React.FC = ({ const [list, setList] = useState([]); const chatContactIndexList = useMemo(() => { - return contactIndexList; const _chatContactIndexList: ContactIndexType[] = []; contactIndexList.map(ele => { @@ -67,27 +66,27 @@ const ContactsList: React.FC = ({ list.forEach(contactIndex => { if (!contactIndex.contacts.length) return; - // if (justChatContact) { - // // just chatContact - // const indexContactList = contactIndex.contacts.filter(ele => !!ele.imInfo); - // if (indexContactList.length > 0) { - // _flashListData.push({ - // contacts: indexContactList, - // index: contactIndex.index, - // }); - // _flashListData = _flashListData.concat(indexContactList); - // } - // } else { - if (!_flashListData) console.log('uuuuuu'); - - _flashListData.push({ - ...contactIndex, - }); - _flashListData = _flashListData.concat(contactIndex.contacts); - // } + if (justChatContact) { + // just chatContact + const indexContactList = contactIndex.contacts.filter(ele => !!ele.imInfo); + if (indexContactList.length > 0) { + _flashListData.push({ + contacts: indexContactList, + index: contactIndex.index, + }); + _flashListData = _flashListData.concat(indexContactList); + } + } else { + if (!_flashListData) console.log('uuuuuu'); + + _flashListData.push({ + ...contactIndex, + }); + _flashListData = _flashListData.concat(contactIndex.contacts); + } }); return _flashListData; - }, [list]); + }, [justChatContact, list]); const [keyWord, setKeyWord] = useState(''); @@ -150,9 +149,11 @@ const ContactsList: React.FC = ({ { navigationService.navigate('NoChatContactProfile', { contact: item }); }} + onPressChat={() => navigationService.navigate('ChatDetails')} /> ); }; diff --git a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx index 8a94595ce8..f28b612342 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatDetails/index.tsx @@ -1,11 +1,10 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; import { TextL } from 'components/CommonText'; - import Chats from '../components/Chats'; import Svg from 'components/Svg'; import Touchable from 'components/Touchable'; @@ -17,7 +16,13 @@ import { FontStyles } from 'assets/theme/styles'; import AddContactButton from '../components/AddContactButton'; import useRouterParams from '@portkey-wallet/hooks/useRouterParams'; import { ChannelItem } from '@portkey-wallet/im/types'; -import { useMuteChannel, usePinChannel, useHideChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { + useMuteChannel, + usePinChannel, + useHideChannel, + useChannelItemInfo, + useIsStranger, +} from '@portkey-wallet/hooks/hooks-ca/im'; import ActionSheet from 'components/ActionSheet'; import { useCurrentChannelId } from '../context/hooks'; @@ -26,17 +31,23 @@ type RouterParams = { }; const ChatDetails = () => { - const { channelInfo } = useRouterParams(); - const { mute, pin, displayName } = channelInfo || {}; + const { channelInfo } = useRouterParams() || {}; const pinChannel = usePinChannel(); const muteChannel = useMuteChannel(); const hideChannel = useHideChannel(); const currentChannelId = useCurrentChannelId(); + const currentChannelInfo = useChannelItemInfo(currentChannelId || ''); - // useEffectOnce(() => { - // init(); - // }); + const displayName = useMemo( + () => currentChannelInfo?.displayName || channelInfo?.displayName, + [channelInfo?.displayName, currentChannelInfo?.displayName], + ); + const pin = useMemo(() => currentChannelInfo?.pin || channelInfo?.pin, [channelInfo?.pin, currentChannelInfo?.pin]); + const mute = useMemo( + () => currentChannelInfo?.mute || channelInfo?.mute, + [channelInfo?.mute, currentChannelInfo?.mute], + ); const onPressMore = useCallback( (event: { nativeEvent: { pageX: any; pageY: any } }) => { @@ -59,7 +70,7 @@ const ChatDetails = () => { title: mute ? ChatOperationsEnum.UNMUTE : ChatOperationsEnum.MUTE, iconName: mute ? 'chat-unmute' : 'chat-mute', onPress: () => { - muteChannel(currentChannelId || '', !channelInfo?.channelUuid); + muteChannel(currentChannelId || '', !mute); }, }, { @@ -90,7 +101,7 @@ const ChatDetails = () => { position: 'left', }); }, - [channelInfo?.channelUuid, currentChannelId, hideChannel, mute, muteChannel, pin, pinChannel], + [currentChannelId, hideChannel, mute, muteChannel, pin, pinChannel], ); return ( @@ -100,6 +111,7 @@ const ChatDetails = () => { safeAreaColor={['blue', 'gray']} scrollViewProps={{ disabled: true }} containerStyles={styles.container} + leftCallback={() => navigationService.navigate('ChatHome')} leftDom={ diff --git a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx index e8c037ee75..3c8c0a350b 100644 --- a/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/ChatHome/index.tsx @@ -12,22 +12,15 @@ import ChatOverlay from '../components/ChatOverlay'; import Touchable from 'components/Touchable'; import ChatList from '../components/ChatList'; import CommonButton from 'components/CommonButton'; -import { useChannelList, useCreateP2pChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { useCreateP2pChannel } from '@portkey-wallet/hooks/hooks-ca/im'; import { pTd } from 'utils/unit'; import im from '@portkey-wallet/im'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; import { v4 } from 'uuid'; -import useEffectOnce from 'hooks/useEffectOnce'; import { formatChatListTime } from '@portkey-wallet/utils/chat'; export default function DiscoverHome() { const createChannel = useCreateP2pChannel(); - const { - list: channelList, - init: initChannelList, - next: nextChannelList, - hasNext: hasNextChannelList, - } = useChannelList(); const RightDom = useMemo(() => { return ( @@ -48,9 +41,9 @@ export default function DiscoverHome() { onPress: () => navigationService.navigate('NewChatHome'), }, { - title: 'Add Contact', + title: 'Find More', iconName: 'chat-add-contact', - onPress: () => navigationService.navigate('ContactEdit'), + onPress: () => navigationService.navigate('FindMorePeople'), }, ], formatType: 'dynamicWidth', @@ -64,10 +57,6 @@ export default function DiscoverHome() { ); }, []); - useEffect(() => { - console.log('channelList', channelList); - }, [channelList]); - const createCha = useCallback(async () => { try { const result = await createChannel('5h7d6-liaaa-aaaaj-vgmya-cai'); @@ -77,10 +66,6 @@ export default function DiscoverHome() { } }, [createChannel]); - const initChannel = useCallback(async () => { - initChannelList(); - }, [initChannelList]); - const sendMess = useCallback(async () => { im.service.sendMessage({ toRelationId: 'e7i7y-giaaa-aaaaj-2ooma-cai', @@ -91,16 +76,11 @@ export default function DiscoverHome() { console.log('sendMess', v4()); }, []); - useEffectOnce(() => { - initChannelList(); - }); - return ( - + - ); diff --git a/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx b/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx index d426a1ad21..6f7cbf9a8d 100644 --- a/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/NewChatHome/index.tsx @@ -1,36 +1,42 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { FlatList, StyleSheet, View } from 'react-native'; import PageContainer from 'components/PageContainer'; import { defaultColors } from 'assets/theme'; import GStyles from 'assets/theme/GStyles'; import { pTd } from 'utils/unit'; -import Touchable from 'components/Touchable'; -import { TextL, TextM, TextS } from 'components/CommonText'; import navigationService from 'utils/navigationService'; import NoData from 'components/NoData'; -import Svg from 'components/Svg'; import CommonInput from 'components/CommonInput'; -import { BGStyles, FontStyles } from 'assets/theme/styles'; -import CommonAvatar from 'components/CommonAvatar'; +import { BGStyles } from 'assets/theme/styles'; import { screenWidth } from '@portkey-wallet/utils/mobile/device'; - -const mock_data = new Array(100).map(() => ({ id: 1 })); +import { useChatContactFlatList, useLocalContactSearch } from '@portkey-wallet/hooks/hooks-ca/contact'; +import useDebounce from 'hooks/useDebounce'; +import { ContactsTab } from '@portkey-wallet/constants/constants-ca/assets'; +import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; +import ContactItem from 'components/ContactItem'; const NewChatHome = () => { const [keyword, setKeyword] = useState(''); - const [filterList, setFilterList] = useState(mock_data); + const debounceKeyword = useDebounce(keyword, 500); + const allChatList = useChatContactFlatList(); + const searchContact = useLocalContactSearch(); + const [filterList, setFilterList] = useState(allChatList); + + useEffect(() => { + if (!debounceKeyword) return setFilterList(allChatList); - const renderItem = useCallback((item: any) => { + const { contactFilterList } = searchContact(debounceKeyword, ContactsTab.Chats); + setFilterList(contactFilterList); + }, [allChatList, debounceKeyword, searchContact]); + + const renderItem = useCallback(({ item }: { item: ContactItemType }) => { return ( - navigationService.navigate('ChatDetails')}> - - - Sally - navigationService.navigate('ChatDetails')}> - Chat - - - + navigationService.navigate('ChatContactProfile', { contactInfo: item })} + onPressChat={() => navigationService.navigate('ChatDetails', { channelInfo: item })} + /> ); }, []); @@ -52,7 +58,9 @@ const NewChatHome = () => { } + ListEmptyComponent={ + + } renderItem={renderItem} /> @@ -63,7 +71,7 @@ export default NewChatHome; const styles = StyleSheet.create({ containerStyles: { - backgroundColor: defaultColors.bg4, + backgroundColor: defaultColors.bg1, paddingHorizontal: 0, flex: 1, }, diff --git a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx index 4ae56bf0f2..ad07f8957e 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/ChatList/index.tsx @@ -6,18 +6,22 @@ import ChatOverlay from '../ChatOverlay'; import ChatHomeListItemSwiped from '../ChatHomeListItemSwiper'; import { ChannelItem } from '@portkey-wallet/im/types'; import NoData from 'components/NoData'; -import { useHideChannel, useMuteChannel, usePinChannel } from '@portkey-wallet/hooks/hooks-ca/im'; +import { useChannelList, useHideChannel, useMuteChannel, usePinChannel } from '@portkey-wallet/hooks/hooks-ca/im'; import CommonToast from 'components/CommonToast'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { useChatsDispatch } from '../../context/hooks'; import { setCurrentChannelId } from '../../context/chatsContext'; +import useLockCallback from '@portkey-wallet/hooks/useLockCallback'; +import useEffectOnce from 'hooks/useEffectOnce'; -type ChatListType = { - chatList: ChannelItem[]; -}; +export default function ChatList() { + const { + list: channelList, + init: initChannelList, + next: nextChannelList, + hasNext: hasNextChannelList, + } = useChannelList(); -export default function ChatList(props: ChatListType) { - const { chatList = [] } = props; const pinChannel = usePinChannel(); const muteChannel = useMuteChannel(); const hideChannel = useHideChannel(); @@ -83,11 +87,28 @@ export default function ChatList(props: ChatListType) { [chatDispatch], ); + const onEndReached = useLockCallback(async () => { + if (hasNextChannelList) await nextChannelList(); + }, []); + + // useFocusEffect( + // useCallback(() => { + // initChannelList(); + // }, [initChannelList]), + // ); + + useEffectOnce(() => { + initChannelList(); + }); + + console.log('channelList', channelList); + return ( } + onEndReached={onEndReached} renderItem={({ item }) => ( null; @@ -49,7 +53,6 @@ const ChatsUI = () => { const dispatch = useChatsDispatch(); useEffectOnce(() => { - init(); initChatInputRecorder(); const timer = setTimeout(() => { setLoading(false); @@ -79,6 +82,11 @@ const ChatsUI = () => { [], ); + const renderDay: GiftedChatProps['renderDay'] = useCallback( + (props: DayProps) => , + [], + ); + const renderBubble = useCallback((data: any) => { return ; }, []); @@ -117,6 +125,7 @@ const ChatsUI = () => { ) : ( { minInputToolbarHeight={0} renderUsernameOnMessage={false} renderInputToolbar={Empty} + renderDay={renderDay} renderBubble={renderBubble} renderMessage={renderMessage} listViewProps={listViewProps} - messageIdGenerator={randomId} showAvatarForEveryMessage={true} isKeyboardInternallyHandled={true} + messagesContainerStyle={styles.messagesContainerStyle} renderMessageText={renderMessageText} renderMessageImage={renderMessageImage} /> @@ -148,3 +158,8 @@ const ChatsUI = () => { export default function Chats() { return ; } +const styles = StyleSheet.create({ + messagesContainerStyle: { + backgroundColor: defaultColors.bg1, + }, +}); diff --git a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx index fce33437c5..e52c402719 100644 --- a/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx +++ b/packages/mobile-app-did/js/pages/Chat/components/Message/MessageText/index.tsx @@ -14,6 +14,7 @@ import { useDeleteMessage } from '@portkey-wallet/hooks/hooks-ca/im'; import { ChatMessage } from 'pages/Chat/types'; import { ShowChatPopoverParams } from '../../ChatOverlay/chatPopover'; import isEqual from 'lodash/isEqual'; +import { copyText } from 'utils'; const UNICODE_SPACE = isIOS ? '\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0' @@ -41,7 +42,15 @@ function MessageText(props: MessageTextProps) { { const { pageX, pageY } = event.nativeEvent; - const list: ShowChatPopoverParams['list'] = [{ title: 'Copy', iconName: 'copy' }]; + const list: ShowChatPopoverParams['list'] = [ + { + title: 'Copy', + iconName: 'copy', + onPress: async () => { + await copyText(currentMessage?.content || ''); + }, + }, + ]; if (position === 'right') list.push({ title: 'Delete', diff --git a/packages/mobile-app-did/js/pages/Chat/components/SystemTime/index.tsx b/packages/mobile-app-did/js/pages/Chat/components/SystemTime/index.tsx new file mode 100644 index 0000000000..76690b57f4 --- /dev/null +++ b/packages/mobile-app-did/js/pages/Chat/components/SystemTime/index.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { DayProps, IMessage } from 'react-native-gifted-chat'; +import { StyleSheet, View } from 'react-native'; +import { pTd } from 'utils/unit'; +import { TextS } from 'components/CommonText'; +import { formatMessageTime } from '@portkey-wallet/utils/chat'; +import { isSameDay } from '@portkey-wallet/utils/time'; +import GStyles from 'assets/theme/GStyles'; +import { defaultColors } from 'assets/theme'; + +function SystemTime(_props: DayProps) { + const sameDay = isSameDay(_props.previousMessage?.createdAt, _props.currentMessage?.createdAt); + if (sameDay && !!_props?.previousMessage?.createdAt) return null; + return ( + + {formatMessageTime(_props.currentMessage?.createdAt)} + + ); +} + +export default SystemTime; + +const styles = StyleSheet.create({ + wrap: { + width: '100%', + marginVertical: pTd(12), + }, + textStyles: { + textAlign: 'center', + color: defaultColors.font3, + backgroundColor: defaultColors.bg6, + paddingHorizontal: pTd(8), + paddingVertical: pTd(2), + borderRadius: pTd(10), + overflow: 'hidden', + }, +}); diff --git a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx index 91da5914b6..9be18bfcd6 100644 --- a/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx +++ b/packages/mobile-app-did/js/pages/My/Contacts/ContactsHome/index.tsx @@ -10,17 +10,19 @@ import ContactsList from 'components/ContactList'; import CommonTopTab from 'components/CommonTopTab'; import { BGStyles } from 'assets/theme/styles'; import CommonInput from 'components/CommonInput'; -import { useContactList } from '@portkey-wallet/hooks/hooks-ca/contact'; +import { useContactList, useLocalContactSearch } from '@portkey-wallet/hooks/hooks-ca/contact'; import useDebounce from 'hooks/useDebounce'; import SearchContactListSection from '../SearchContactListSection'; import { StyleSheet } from 'react-native'; import GStyles from 'assets/theme/GStyles'; import FindMoreButton from 'pages/Chat/components/FindMoreButton'; import ContactUpdateWarning from 'pages/My/components/ContactUpdateWarning'; +import { ContactsTab } from '@portkey-wallet/constants/constants-ca/assets'; const ContactsHome: React.FC = () => { const { t } = useLanguage(); const contactList = useContactList(); + const searchContact = useLocalContactSearch(); const [keyword, setKeyword] = useState(''); const [filerList, setFilterList] = useState([]); @@ -43,8 +45,10 @@ const ContactsHome: React.FC = () => { ); useEffect(() => { - setFilterList(debounceKeyword ? contactList : []); - }, [contactList, debounceKeyword]); + const { contactFilterList } = searchContact(debounceKeyword, ContactsTab.ALL); + console.log('searchContact', contactFilterList); + setFilterList(contactFilterList); + }, [contactList, debounceKeyword, searchContact]); return ( = (props: SearchContactListSectionType) => { const { list } = props; - const renderItem = useCallback(({ item }: any) => { - return ; + const renderItem = useCallback(({ item }: { item: ContactItemType }) => { + return ( + navigationService.navigate('ChatContactProfile')} + onPressChat={() => navigationService.navigate('ChatDetails')} + /> + ); }, []); return ; From ff92a4a72e46294c2fe0827fb16795e1feedb107 Mon Sep 17 00:00:00 2001 From: thomas-portkey Date: Tue, 22 Aug 2023 17:26:23 +0800 Subject: [PATCH 576/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20thumb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/cms.ts | 12 ++++-- packages/hooks/hooks-ca/im/channel.ts | 37 ++++++++++++------- packages/hooks/hooks-ca/im/channelList.ts | 8 +++- packages/hooks/hooks-ca/im/index.ts | 4 +- packages/im/utils/parser.ts | 4 +- .../js/components/ContactList/index.tsx | 2 +- packages/mobile-app-did/js/navigation/Tab.tsx | 8 ++-- packages/store/store-ca/im/slice.ts | 5 ++- 8 files changed, 52 insertions(+), 28 deletions(-) diff --git a/packages/hooks/hooks-ca/cms.ts b/packages/hooks/hooks-ca/cms.ts index 411ec8f4a6..de0e53580e 100644 --- a/packages/hooks/hooks-ca/cms.ts +++ b/packages/hooks/hooks-ca/cms.ts @@ -1,7 +1,12 @@ import { useCallback, useEffect, useMemo } from 'react'; import { useAppCASelector } from '.'; import { useAppCommonDispatch } from '../index'; -import { useCurrentNetworkInfo, useIsMainnet, useNetworkList } from '@portkey-wallet/hooks/hooks-ca/network'; +import { + useCurrentNetworkInfo, + useIsIMServiceExist, + useIsMainnet, + useNetworkList, +} from '@portkey-wallet/hooks/hooks-ca/network'; import { getDiscoverGroupAsync, getSocialMediaAsync, @@ -207,12 +212,13 @@ export const useCheckSiteIsInBlackList = () => { export const useIsChatShow = () => { const { tabMenuListNetMap } = useCMS(); const { networkType } = useCurrentNetworkInfo(); + const isIMServiceExist = useIsIMServiceExist(); const IsChatShow = useMemo(() => { const tabMenuList = tabMenuListNetMap[networkType]; if (!tabMenuList) return false; - return !!tabMenuList.find(item => item.type.value === ChatTabName); - }, [networkType, tabMenuListNetMap]); + return isIMServiceExist && !!tabMenuList.find(item => item.type.value === ChatTabName); + }, [isIMServiceExist, networkType, tabMenuListNetMap]); return IsChatShow; }; diff --git a/packages/hooks/hooks-ca/im/channel.ts b/packages/hooks/hooks-ca/im/channel.ts index e67be2a21d..b0920bd159 100644 --- a/packages/hooks/hooks-ca/im/channel.ts +++ b/packages/hooks/hooks-ca/im/channel.ts @@ -15,9 +15,10 @@ import { updateChannelMessageAttribute, } from '@portkey-wallet/store/store-ca/im/actions'; import { useChannelItemInfo, useIMChannelMessageListNetMapState, useRelationId } from '.'; -import s3Instance, { UploadFileType } from '@portkey-wallet/utils/s3'; +import s3Instance, { getThumbSize, UploadFileType } from '@portkey-wallet/utils/s3'; import { messageParser } from '@portkey-wallet/im/utils'; import { useContactRelationIdMap } from '../contact'; +import { request } from '@portkey-wallet/api/api-did'; export type ImageMessageFileType = { body: string | File; @@ -110,19 +111,28 @@ export const useSendChannelMessage = () => { const sendChannelImageByS3Result = useCallback( async (channelId: string, s3Result: UploadFileType & ImageMessageFileType) => { try { - // const { thumbWidth, thumbHeight } = getThumbSize(file.width, file.height); - // const thumbResult = await request.im.getImageThumb({ - // params: { - // imageUrl: s3Result.url, - // width: thumbWidth, - // height: thumbHeight, - // }, - // }); + const { thumbWidth, thumbHeight } = getThumbSize(s3Result.width, s3Result.height); const p1Url = encodeURIComponent(s3Result.url); const p1Key = s3Result.key; - const p2Url = encodeURIComponent(s3Result.url); - const p2Key = s3Result.key; + let p2Url = p1Url; + let p2Key = p1Key; + + try { + const thumbResult = await request.im.getImageThumb({ + params: { + imageUrl: s3Result.url, + width: thumbWidth, + height: thumbHeight, + }, + }); + if (thumbResult?.thumbnailUrl) { + p2Url = encodeURIComponent(thumbResult.thumbnailUrl); + p2Key = ''; + } + } catch (error) { + console.log('sendChannelImage: error', error); + } const content = `type:image;action:localImage;p1(Text):${p1Url},p2(Text):${p1Key},p3(Text):${p2Url},p4(Text):${p2Key},p5(Text):${s3Result.width},p6(Text):${s3Result.height}`; @@ -185,8 +195,9 @@ export const useDeleteMessage = (channelId: string) => { network: networkType, channelId: channelId, value: { - lastMessageType: 'TEXT', - lastMessageContent: '', + lastMessageType: null, + lastMessageContent: null, + lastPostAt: null, }, }), ); diff --git a/packages/hooks/hooks-ca/im/channelList.ts b/packages/hooks/hooks-ca/im/channelList.ts index 18676daebd..b4f1b7a944 100644 --- a/packages/hooks/hooks-ca/im/channelList.ts +++ b/packages/hooks/hooks-ca/im/channelList.ts @@ -93,7 +93,10 @@ export const useChannelList = () => { const { networkType } = useCurrentNetworkInfo(); const { next, hasNext } = useNextChannelList(); - const list = useMemo(() => channelListNetMap?.[networkType]?.list || [], [channelListNetMap, networkType]); + const list = useMemo( + () => channelListNetMap?.[networkType]?.list?.filter(item => !!item.lastPostAt) || [], + [channelListNetMap, networkType], + ); const init = useCallback(() => { return next(true); @@ -160,7 +163,7 @@ export const useCreateP2pChannel = () => { network: networkType, channelId: channelUuid, value: { - displayName: channelInfo.name, + displayName: channelInfo.members.find(item => item.relationId === relationId)?.name || '', channelIcon: channelInfo.icon, mute: channelInfo.mute, pin: channelInfo.pin, @@ -170,6 +173,7 @@ export const useCreateP2pChannel = () => { } catch (error) { console.log('createChannel error: ', error); } + return result; }, [dispatch, networkType], ); diff --git a/packages/hooks/hooks-ca/im/index.ts b/packages/hooks/hooks-ca/im/index.ts index 47fa77418c..1a647eee52 100644 --- a/packages/hooks/hooks-ca/im/index.ts +++ b/packages/hooks/hooks-ca/im/index.ts @@ -53,7 +53,7 @@ export const useInitIM = () => { channel: { status: ChannelStatusEnum.NORMAL, channelUuid: rawMsg.channelUuid, - displayName: rawMsg.fromName || '', + displayName: '', channelIcon: rawMsg.fromAvatar || '', channelType: ChannelTypeEnum.P2P, unreadMessageCount: 1, @@ -72,13 +72,13 @@ export const useInitIM = () => { const { data: channelInfo } = await im.service.getChannelInfo({ channelUuid: rawMsg.channelUuid, }); - console.log('channelInfo', channelInfo); dispatch( updateChannelAttribute({ network: networkType, channelId: rawMsg.channelUuid, value: { + displayName: channelInfo.members.find(item => item.relationId === rawMsg.from)?.name || '', pin: channelInfo.pin, channelType: channelInfo.type, }, diff --git a/packages/im/utils/parser.ts b/packages/im/utils/parser.ts index f6e15d81d7..93eacbbec6 100644 --- a/packages/im/utils/parser.ts +++ b/packages/im/utils/parser.ts @@ -14,8 +14,8 @@ const imageMessageParser = (str: string): ParsedImage => { action: result['action'] || '', imgUrl: result['p1(Text)'] || '', s3Key: result['p2(Text)'] || '', - thumbImgUrl: result['p1(Text)'], - thumbS3Key: result['p2(Text)'], + thumbImgUrl: result['p3(Text)'], + thumbS3Key: result['p4(Text)'], width: result['p5(Text)'], height: result['p6(Text)'], }; diff --git a/packages/mobile-app-did/js/components/ContactList/index.tsx b/packages/mobile-app-did/js/components/ContactList/index.tsx index 2a5a085594..9339d722aa 100644 --- a/packages/mobile-app-did/js/components/ContactList/index.tsx +++ b/packages/mobile-app-did/js/components/ContactList/index.tsx @@ -42,7 +42,7 @@ const ContactsList: React.FC = ({ ListFooterComponent, }) => { const { t } = useLanguage(); - const { contactIndexList, contactMap } = useContact(); + const { contactIndexList, contactMap } = useContact(!justChatContact); const [list, setList] = useState([]); const chatContactIndexList = useMemo(() => { diff --git a/packages/mobile-app-did/js/navigation/Tab.tsx b/packages/mobile-app-did/js/navigation/Tab.tsx index 06492b9432..41f173bb3f 100644 --- a/packages/mobile-app-did/js/navigation/Tab.tsx +++ b/packages/mobile-app-did/js/navigation/Tab.tsx @@ -33,6 +33,7 @@ export interface IRenderTabMenuItem { index: number; icon: IconName; component: React.FC; + isDefault?: boolean; } export const tabMenuTypeMap: Record = { @@ -41,6 +42,7 @@ export const tabMenuTypeMap: Record = { index: 0, label: 'Wallet', icon: 'logo-icon', + isDefault: true, component: DashBoard, }, [TabRouteNameEnum.DISCOVER]: { @@ -59,6 +61,7 @@ export const tabMenuTypeMap: Record = { }, [TabRouteNameEnum.SETTINGS]: { name: TabRouteNameEnum.SETTINGS, + isDefault: true, index: 3, label: 'My', icon: 'my', @@ -66,7 +69,7 @@ export const tabMenuTypeMap: Record = { }, }; -export const defaultTabMenuList = Object.values(tabMenuTypeMap); +export const defaultTabMenuList = Object.values(tabMenuTypeMap).filter(item => item.isDefault); export default function TabRoot() { const { t } = useLanguage(); @@ -75,7 +78,6 @@ export default function TabRoot() { const unreadCount = useUnreadCount(); const tabMenuList = useMemo(() => { - if (__DEV__) return defaultTabMenuList; const _tabMenuListStore = tabMenuListStore.reduce((acc: typeof tabMenuListStore, cur) => { if (!acc.find(item => item.type.value === cur.type.value)) { acc.push(cur); @@ -83,7 +85,7 @@ export default function TabRoot() { return acc; }, []); - if (_tabMenuListStore.length) return defaultTabMenuList; + if (!_tabMenuListStore.length) return defaultTabMenuList; return _tabMenuListStore .map(item => ({ diff --git a/packages/store/store-ca/im/slice.ts b/packages/store/store-ca/im/slice.ts index 8160ad3906..56eefe74e7 100644 --- a/packages/store/store-ca/im/slice.ts +++ b/packages/store/store-ca/im/slice.ts @@ -44,16 +44,17 @@ export const imSlice = createSlice({ ...state, channelListNetMap: { ...state.channelListNetMap, - [action.payload.network]: action.payload.channelList, + [action.payload.network]: formatChannelList(action.payload.channelList), }, }; }) .addCase(nextChannelList, (state, action) => { const originList = state.channelListNetMap[action.payload.network]?.list || []; - const channelList = { + let channelList = { list: [...originList, ...action.payload.channelList.list], cursor: action.payload.channelList.cursor, }; + channelList = formatChannelList(channelList); return { ...state, channelListNetMap: { From e72589e5752950001fafda26a520f5ebdc50785e Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 22 Aug 2023 18:13:00 +0800 Subject: [PATCH 577/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20contact=20?= =?UTF-8?q?todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/Contacts/ContactDetail/index.tsx | 3 ++ .../app/web/pages/Contacts/FindMore/index.tsx | 9 +++- .../web/pages/Contacts/ViewContact/index.tsx | 43 +++++++++++++------ .../components/ViewContactBody/index.tsx | 14 +++--- .../components/SetWalletNameForm/index.tsx | 12 ++++-- 5 files changed, 56 insertions(+), 25 deletions(-) diff --git a/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx index 3c42905319..f3cb44f0ee 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ContactDetail/index.tsx @@ -12,8 +12,11 @@ export default function ContactDetail() { return (
    {type === 'view' && } + {/* can chat edit */} {type === 'edit' && extra === '1' && } + {/* cant chat edit */} {type === 'edit' && extra === '2' && } + {/* add new contact */} {type === 'add' && }
    ); diff --git a/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx index aa5a84aabd..a4f78d889a 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/FindMore/index.tsx @@ -1,5 +1,5 @@ import { ChangeEvent, ChangeEventHandler, useCallback, useState } from 'react'; -import { useNavigate } from 'react-router'; +import { useLocation, useNavigate } from 'react-router'; import CustomModal from 'pages/components/CustomModal'; import { useCommonState, useWalletInfo } from 'store/Provider/hooks'; import { ContactItemType } from '@portkey-wallet/types/types-ca/contact'; @@ -29,6 +29,7 @@ export interface IFindMoreProps extends BaseHeaderProps { export default function FindMore() { const navigate = useNavigate(); const { isPrompt, isNotLessThan768 } = useCommonState(); + const { state } = useLocation(); const showChat = useIsChatShow(); const { userId } = useWalletInfo(); const contactRelationIdMap = useContactRelationIdMap(); @@ -77,7 +78,11 @@ export default function FindMore() { }, []); const goBack = () => { - navigate(-1); + if (state?.from === 'chat-search') { + navigate('/chat-list-search', { state }); + } else { + navigate(-1); + } }; const handleChat = useCallback( diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index d52d4d4ecb..55780486dd 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -2,7 +2,7 @@ import ViewContactPrompt from './Prompt'; import ViewContactPopup from './Popup'; import { useLocation, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCommonState, useWalletInfo } from 'store/Provider/hooks'; import { useProfileChat, useProfileCopy, useGoProfileEdit } from 'hooks/useProfile'; import CustomModal from 'pages/components/CustomModal'; @@ -26,32 +26,49 @@ export default function ViewContact() { const chatText = t('Chat'); const addedText = t('Added'); const addContactText = t('Add Contact'); + const portkeyId = useMemo( + () => data?.userId || (data?.portkeyId && data.portkeyId) || (data?.imInfo && data.imInfo?.portkeyId), + [data.imInfo, data.portkeyId, data?.userId], + ); + + const relationId = useMemo( + () => (data?.relationId && data.relationId) || (data?.imInfo && data.imInfo?.relationId), + [data.imInfo, data.relationId], + ); useEffect(() => { const isMyContact = isMyContactFn({ - userId: data?.userId || data?.portkeyId, - relationId: data?.relationId || data?.imInfo?.relationId, + userId: portkeyId, + relationId: relationId, }); - const isMy = (data?.portkeyId && data.portkeyId === userId) || (data?.userId && data.userId === userId); + const isMy = portkeyId === userId; if (!isMy && !isMyContact) { // need fetch profile - const res = getProfile({ id: data?.id, relationId: data?.relationId || data?.imInfo?.relationId }); - console.log('🌈 🌈 🌈 🌈 🌈 🌈 need fetch profile', res); + const res = getProfile({ id: data?.id, relationId: relationId }); + setData(res); } }, [ getProfile, isMyContactFn, data?.id, - data?.imInfo?.relationId, + data.imInfo.relationId, data.portkeyId, - data?.relationId, + data.relationId, data.userId, userId, + portkeyId, + relationId, ]); const goBack = useCallback(() => { - navigate('/setting/contacts'); - }, [navigate]); + if (state?.from === 'new-chat') { + navigate('/new-chat', { state }); + } else if (state?.from === 'chat-list') { + navigate('/chat-list'); + } else { + navigate('/setting/contacts'); + } + }, [navigate, state]); const handleEdit = useGoProfileEdit(); const handleChat = useProfileChat(); @@ -61,7 +78,6 @@ export default function ViewContact() { const handleAdd = async () => { try { const res = await addStranger(data?.imInfo?.relationId || data?.relationId); - console.log('🌈 🌈 🌈 🌈 🌈 🌈 res', res); setData(res.data); } catch (error) { const err = handleErrorMessage(error, 'add stranger error'); @@ -89,7 +105,6 @@ export default function ViewContact() { } }, [readImputationApi, data]); - // TODO btn show logic return isNotLessThan768 ? ( handleEdit('1', data)} // TODO add or edit 1 2 + handleEdit={() => handleEdit(portkeyId ? '1' : '2', data)} handleAdd={handleAdd} handleChat={() => handleChat(data)} handleCopy={handleCopy} @@ -113,7 +128,7 @@ export default function ViewContact() { addContactText={addContactText} data={data} goBack={goBack} - handleEdit={() => handleEdit('1', data)} // TODO add or edit 1 2 + handleEdit={() => handleEdit(portkeyId ? '1' : '2', data)} handleAdd={handleAdd} handleChat={() => handleChat(data)} handleCopy={handleCopy} diff --git a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx index 53fb18345b..c4871c0ebe 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/components/ViewContactBody/index.tsx @@ -83,12 +83,14 @@ export default function ViewContactBody({ />
    - {/* TODO No edit button for strangers */} -
    - -
    + {/* stranger cant edit */} + {!isShowAddContactBtn && ( +
    + +
    + )}
    ); } diff --git a/packages/web-extension-did/app/web/pages/Wallet/components/SetWalletNameForm/index.tsx b/packages/web-extension-did/app/web/pages/Wallet/components/SetWalletNameForm/index.tsx index fe1d42fe4e..89bf216b92 100644 --- a/packages/web-extension-did/app/web/pages/Wallet/components/SetWalletNameForm/index.tsx +++ b/packages/web-extension-did/app/web/pages/Wallet/components/SetWalletNameForm/index.tsx @@ -8,11 +8,17 @@ import { useWalletInfo } from 'store/Provider/hooks'; import './index.less'; import IdAndAddress from 'pages/Contacts/components/IdAndAddress'; import { useIsChatShow } from '@portkey-wallet/hooks/hooks-ca/cms'; +import { IProfileDetailDataProps } from 'types/Profile'; type ValidateStatus = Parameters[0]['validateStatus']; -// TODO any -export default function SetWalletNameForm({ data, handleCopy, saveCallback }: any) { +export interface ISetWalletNameFormProps { + data: IProfileDetailDataProps; + handleCopy: (v: string) => void; + saveCallback?: () => void; +} + +export default function SetWalletNameForm({ data, handleCopy, saveCallback }: ISetWalletNameFormProps) { const [form] = Form.useForm(); const { t } = useTranslation(); const showChat = useIsChatShow(); @@ -43,7 +49,7 @@ export default function SetWalletNameForm({ data, handleCopy, saveCallback }: an async (walletName: string) => { try { await setWalletName(walletName); - saveCallback(); + saveCallback?.(); message.success(t('Saved Successful')); } catch (error) { message.error('set wallet name error'); From c84e3306b533dc69cdd6221329415e5a37ca272d Mon Sep 17 00:00:00 2001 From: ykx Date: Tue, 22 Aug 2023 18:17:31 +0800 Subject: [PATCH 578/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20after=20add=20st?= =?UTF-8?q?ranger,=20refresh=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/contact.ts | 2 +- .../app/web/pages/Contacts/ViewContact/index.tsx | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/hooks/hooks-ca/contact.ts b/packages/hooks/hooks-ca/contact.ts index 5f19f17c52..889afc3e18 100644 --- a/packages/hooks/hooks-ca/contact.ts +++ b/packages/hooks/hooks-ca/contact.ts @@ -19,7 +19,7 @@ import { useAppCASelector, useAppCommonDispatch, useAppCommonSelector } from '.. import { getAelfAddress, isAelfAddress } from '@portkey-wallet/utils/aelf'; import { ContactsTab } from '@portkey-wallet/constants/constants-ca/assets'; -const REFRESH_DELAY_TIME = 1.5 * 1000; +export const REFRESH_DELAY_TIME = 1.5 * 1000; export const useAddContact = () => { const dispatch = useAppCommonDispatch(); diff --git a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx index 55780486dd..202d330786 100644 --- a/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx +++ b/packages/web-extension-did/app/web/pages/Contacts/ViewContact/index.tsx @@ -6,13 +6,21 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCommonState, useWalletInfo } from 'store/Provider/hooks'; import { useProfileChat, useProfileCopy, useGoProfileEdit } from 'hooks/useProfile'; import CustomModal from 'pages/components/CustomModal'; -import { useGetProfile, useIsMyContact, useReadImputation } from '@portkey-wallet/hooks/hooks-ca/contact'; +import { + REFRESH_DELAY_TIME, + useGetProfile, + useIsMyContact, + useReadImputation, +} from '@portkey-wallet/hooks/hooks-ca/contact'; import { useAddStranger } from '@portkey-wallet/hooks/hooks-ca/im'; import { handleErrorMessage } from '@portkey-wallet/utils'; import { message } from 'antd'; +import { fetchContactListAsync } from '@portkey-wallet/store/store-ca/contact/actions'; +import { useAppCommonDispatch } from '@portkey-wallet/hooks'; export default function ViewContact() { const { isNotLessThan768 } = useCommonState(); + const dispatch = useAppCommonDispatch(); const { state } = useLocation(); const navigate = useNavigate(); const { t } = useTranslation(); @@ -79,6 +87,10 @@ export default function ViewContact() { try { const res = await addStranger(data?.imInfo?.relationId || data?.relationId); setData(res.data); + + setTimeout(() => { + dispatch(fetchContactListAsync()); + }, REFRESH_DELAY_TIME); } catch (error) { const err = handleErrorMessage(error, 'add stranger error'); message.error(err); From 89d0ba8c808ab427326e4cae58d181f035f537ac Mon Sep 17 00:00:00 2001 From: sarah-portkey Date: Tue, 22 Aug 2023 18:43:03 +0800 Subject: [PATCH 579/893] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dev=20chat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/hooks/hooks-ca/contact.ts | 6 +- packages/im-ui-web/src/ChatInput/index.tsx | 1 + packages/im-ui-web/src/ImageMessage/index.tsx | 7 +- packages/im-ui-web/src/InputBar/index.tsx | 1 + packages/im-ui-web/src/MessageItem/index.less | 3 + packages/im-ui-web/src/TextMessage/index.less | 4 + packages/im-ui-web/src/TextMessage/index.tsx | 21 +++- .../im-ui-web/src/assets/theme/color.less | 2 +- .../src/components/ParsedText/constants.ts | 16 +++ .../src/components/ParsedText/index.tsx | 52 ++++++++ .../src/components/ParsedText/types.ts | 39 ++++++ .../src/components/ParsedText/utils.ts | 115 ++++++++++++++++++ packages/im-ui-web/src/type.ts | 8 +- packages/im-ui-web/src/utils/index.ts | 2 +- .../app/web/pages/ChatBox/index.less | 3 + .../app/web/pages/ChatBox/index.tsx | 88 ++++++++------ .../app/web/pages/ChatList/index.tsx | 7 +- .../app/web/pages/ChatListSearch/index.tsx | 2 +- .../app/web/pages/NewChat/index.tsx | 36 ++++-- .../web-extension-did/app/web/types/im.ts | 8 ++ 20 files changed, 363 insertions(+), 58 deletions(-) create mode 100644 packages/im-ui-web/src/components/ParsedText/constants.ts create mode 100644 packages/im-ui-web/src/components/ParsedText/index.tsx create mode 100644 packages/im-ui-web/src/components/ParsedText/types.ts create mode 100644 packages/im-ui-web/src/components/ParsedText/utils.ts create mode 100644 packages/web-extension-did/app/web/types/im.ts diff --git a/packages/hooks/hooks-ca/contact.ts b/packages/hooks/hooks-ca/contact.ts index 3e1a8ba369..afa7cc3231 100644 --- a/packages/hooks/hooks-ca/contact.ts +++ b/packages/hooks/hooks-ca/contact.ts @@ -173,7 +173,11 @@ export const useLocalContactSearch = () => { return useCallback( (value: string, type: ContactsTab) => { if (!value) { - return { contactFilterList: [], contactIndexFilterList: [] }; + const temp: ContactItemType[] = []; + contactIndexList.forEach(({ contacts }) => { + temp.push(...contacts); + }); + return { contactFilterList: temp, contactIndexFilterList: contactIndexList }; } // STEP 1 diff --git a/packages/im-ui-web/src/ChatInput/index.tsx b/packages/im-ui-web/src/ChatInput/index.tsx index 2ed1c3b03e..04453b711a 100644 --- a/packages/im-ui-web/src/ChatInput/index.tsx +++ b/packages/im-ui-web/src/ChatInput/index.tsx @@ -99,6 +99,7 @@ const Input: React.FC = ({ /> ) : ( - )} - {props.rightButtons &&
    {props.rightButtons}
    } -
    - ); -}; - -export default Input; diff --git a/packages/im-ui-web/src/ChatItem/index.less b/packages/im-ui-web/src/ChatItem/index.less index e2a977b482..27e8136963 100644 --- a/packages/im-ui-web/src/ChatItem/index.less +++ b/packages/im-ui-web/src/ChatItem/index.less @@ -58,10 +58,6 @@ } } } - .body-top-time { - font-size: 12px; - color: rgba(0, 0, 0, 0.4); - } } .body-bottom { .body-bottom-title { @@ -89,3 +85,27 @@ } } } + +.portkey-chat-item-popover { + .portkey-popover-content { + min-width: 120px; + .portkey-popover-inner { + border-radius: 6px; + .portkey-popover-inner-content { + padding: 0 12px; + } + } + } +} + +.portkey-chat-item-popover.portkey-popover-placement-top { + .portkey-popover-content { + margin-bottom: -72px; + } +} + +.portkey-chat-item-popover.portkey-popover-placement-bottom { + .portkey-popover-content { + margin-top: -36px; + } +} diff --git a/packages/im-ui-web/src/ChatItem/index.tsx b/packages/im-ui-web/src/ChatItem/index.tsx index e4b90ab5cc..2c7678be3a 100644 --- a/packages/im-ui-web/src/ChatItem/index.tsx +++ b/packages/im-ui-web/src/ChatItem/index.tsx @@ -1,7 +1,6 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Popover } from 'antd'; import clsx from 'clsx'; - import Avatar from '../Avatar'; import UnreadTip from '../UnreadTip'; import CustomSvg from '../components/CustomSvg'; @@ -10,7 +9,7 @@ import { formatChatListTime } from '../utils'; import PopoverMenuList from '../PopoverMenuList'; import './index.less'; -const ChannelItem: React.FC = ({ +const ChatItem: React.FC = ({ date = new Date().getTime(), unread = 0, alt = 'portkey', @@ -25,36 +24,38 @@ const ChannelItem: React.FC = ({ props.onClick?.(e); }; const [popVisible, setPopVisible] = useState(false); - - const popList = [ - { - key: 'pin', - leftIcon: , - children: props.pin ? 'Unpin' : 'Pin', - onClick: (e: any) => { - hidePop(); - onClickPin?.(e); + const popList = useMemo( + () => [ + { + key: 'pin', + leftIcon: , + children: props.pin ? 'Unpin' : 'Pin', + onClick: (e: any) => { + hidePop(); + onClickPin?.(e); + }, }, - }, - { - key: 'mute', - leftIcon: , - children: props.muted ? 'Unmute' : 'Mute', - onClick: (e: any) => { - hidePop(); - onClickMute?.(e); + { + key: 'mute', + leftIcon: , + children: props.muted ? 'Unmute' : 'Mute', + onClick: (e: any) => { + hidePop(); + onClickMute?.(e); + }, }, - }, - { - key: 'delete', - leftIcon: , - children: 'Delete', - onClick: (e: any) => { - hidePop(); - onClickDelete?.(e); + { + key: 'delete', + leftIcon: , + children: 'Delete', + onClick: (e: any) => { + hidePop(); + onClickDelete?.(e); + }, }, - }, - ]; + ], + [onClickDelete, onClickMute, onClickPin, props.muted, props.pin], + ); const hidePop = () => { setPopVisible(false); }; @@ -66,19 +67,14 @@ const ChannelItem: React.FC = ({ return ( setPopVisible(visible)} + onOpenChange={(visible) => setPopVisible(visible)} showArrow={false} content={}> -
    +
    @@ -89,7 +85,7 @@ const ChannelItem: React.FC = ({ {props.title} {showMute && props.muted === true && }
    -
    {props.dateString || formatChatListTime(`${date}`)}
    +
    {props.dateString || formatChatListTime(`${date}`)}
    @@ -101,7 +97,6 @@ const ChannelItem: React.FC = ({ ) : null}
    - {props.customStatusComponents !== undefined ? props.customStatusComponents.map(Item => ) : null}
    @@ -110,4 +105,4 @@ const ChannelItem: React.FC = ({ ); }; -export default ChannelItem; +export default ChatItem; diff --git a/packages/im-ui-web/src/ChatList/index.less b/packages/im-ui-web/src/ChatList/index.less index 9b999cd940..5299fc0cf3 100644 --- a/packages/im-ui-web/src/ChatList/index.less +++ b/packages/im-ui-web/src/ChatList/index.less @@ -3,27 +3,3 @@ overflow: auto; width: 100%; } - -.chat-item-popover { - .portkey-popover-content { - width: 120px; - .portkey-popover-inner { - border-radius: 6px; - .portkey-popover-inner-content { - padding: 0 12px; - } - } - } -} - -.chat-item-popover.portkey-popover-placement-top { - .portkey-popover-content { - margin-bottom: -72px; - } -} - -.chat-item-popover.portkey-popover-placement-bottom { - .portkey-popover-content { - margin-top: -36px; - } -} diff --git a/packages/im-ui-web/src/ChatList/index.tsx b/packages/im-ui-web/src/ChatList/index.tsx index 808a9731ba..b5a32d7d9a 100644 --- a/packages/im-ui-web/src/ChatList/index.tsx +++ b/packages/im-ui-web/src/ChatList/index.tsx @@ -1,40 +1,33 @@ import React from 'react'; import clsx from 'clsx'; - import ChatItem from '../ChatItem'; import { IChatListProps, ChatListEvent } from '../type'; import LoadingMore from '../components/LoadMore'; import './index.less'; -const ChannelList: React.FC = ({ dataSource, hasMore = false, loadMore, ...props }) => { - const onClick: ChatListEvent = item => { +const ChatList: React.FC = ({ dataSource, hasMore = false, loadMore, ...props }) => { + const onClick: ChatListEvent = (item) => { if (props.onClick instanceof Function) props.onClick(item); }; - const onClickMute: ChatListEvent = item => { + const onClickMute: ChatListEvent = (item) => { if (props.onClickMute instanceof Function) props.onClickMute(item); }; - const onClickPin: ChatListEvent = item => { + const onClickPin: ChatListEvent = (item) => { if (props.onClickPin instanceof Function) props.onClickPin(item); }; - const onClickDelete: ChatListEvent = item => { + const onClickDelete: ChatListEvent = (item) => { if (props.onClickDelete instanceof Function) props.onClickDelete(item); }; - const onContextMenu: ChatListEvent = (item, index, event) => { - event?.preventDefault(); - if (props.onContextMenu instanceof Function) props.onContextMenu(item, index, event); - }; - return (
    {dataSource.map((x, i: number) => ( ) => onContextMenu(x, i, e)} onClick={(e: React.MouseEvent) => onClick(x, i, e)} onClickPin={(e: React.MouseEvent) => onClickPin(x, i, e)} onClickMute={(e: React.MouseEvent) => onClickMute(x, i, e)} @@ -46,4 +39,4 @@ const ChannelList: React.FC = ({ dataSource, hasMore = false, lo ); }; -export default ChannelList; +export default ChatList; diff --git a/packages/im-ui-web/src/ImageMessage/index.less b/packages/im-ui-web/src/ImageMessage/index.less index 0aa727fca7..dcb58c408e 100644 --- a/packages/im-ui-web/src/ImageMessage/index.less +++ b/packages/im-ui-web/src/ImageMessage/index.less @@ -11,13 +11,16 @@ position: relative; justify-content: flex-start; padding: 4px; - border-radius: 2px 20px 20px 20px; + border-radius: 2px 20px 20px; background: @bg-22; object-fit: contain; .portkey-image { - width: 100%; + display: flex; + justify-content: center; + align-items: center; img { - border-radius: 2px 20px 20px 20px; + object-fit: contain; + border-radius: 2px 20px 20px; } } .portkey-image-mask { @@ -39,7 +42,7 @@ line-height: 14px; color: @font-5; border-radius: 8px; - background-color: rgba(81, 90, 98, 0.8); + background-color: @bg-28; } .image-error { padding: 8px; @@ -48,7 +51,7 @@ height: 32px; svg { path { - fill: rgba(37,39,42,0.2); + fill: @svg-2; } } } @@ -61,6 +64,7 @@ width: 120px; .portkey-popover-inner { border-radius: 6px; + box-shadow: 0 2px 12px 0 @shadow-1; .portkey-popover-inner-content { padding: 0 12px; } diff --git a/packages/im-ui-web/src/ImageMessage/index.tsx b/packages/im-ui-web/src/ImageMessage/index.tsx index af4614b39c..731bd6bc63 100644 --- a/packages/im-ui-web/src/ImageMessage/index.tsx +++ b/packages/im-ui-web/src/ImageMessage/index.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Image, Popover } from 'antd'; import clsx from 'clsx'; - import { IImageMessageProps } from '../type'; import { formatTime } from '../utils'; import CustomSvg from '../components/CustomSvg'; @@ -9,21 +8,25 @@ import { formatImageSize } from '@portkey-wallet/utils/img'; import PopoverMenuList from '../PopoverMenuList'; import './index.less'; -const ImageMessage: React.FC = props => { +const ImageMessage: React.FC = (props) => { const showDate = useMemo( () => (props.dateString ? props.dateString : formatTime(props.date)), [props.dateString, props.date], ); const [loadErr, setLoadErr] = useState(false); const { thumbImgUrl, width, height, imgUrl } = props.imgData || {}; - const imageSize = useMemo(() => formatImageSize({ width, height, maxWidth: 280, maxHeight: 280 }), [width, height]); + const imageSize = useMemo( + () => formatImageSize({ width, height, maxWidth: 272, maxHeight: 272, minHeight: 92, minWidth: 92 }), + [width, height], + ); const [popVisible, setPopVisible] = useState(false); + const popoverList = [ { key: 'delete', leftIcon: , children: 'Delete', - onClick: () => props?.onDelete?.(`${props.id}`), + onClick: (e: React.MouseEvent) => props?.onDeleteMsg?.(e), }, ]; const hidePop = () => { @@ -33,6 +36,22 @@ const ImageMessage: React.FC = props => { document.addEventListener('click', hidePop); return () => document.removeEventListener('click', hidePop); }, []); + const renderImage = useMemo( + () => ( + <> + setLoadErr(true)} + /> +
    {showDate}
    + + ), + [imageSize, imgUrl, showDate, thumbImgUrl], + ); return (
    @@ -44,31 +63,17 @@ const ImageMessage: React.FC = props => { <> setPopVisible(v)} + onOpenChange={(v) => setPopVisible(v)} overlayClassName={clsx(['message-image-popover', props.position])} placement="bottom" trigger="contextMenu" showArrow={false} content={}> - setLoadErr(true)} - /> -
    {showDate}
    + {renderImage}
    ) : ( - <> - setLoadErr(true)} - /> -
    {showDate}
    - + renderImage )}
    diff --git a/packages/im-ui-web/src/InputBar/index.less b/packages/im-ui-web/src/InputBar/index.less index a0a8fa8387..2ba71cd361 100644 --- a/packages/im-ui-web/src/InputBar/index.less +++ b/packages/im-ui-web/src/InputBar/index.less @@ -8,14 +8,14 @@ color: @font-1; .input-emoji { position: absolute; - top: -176px; + top: -177px; width: 100%; height: 176px; padding: 16px; overflow-y: auto; background-color: @bg-11; border-radius: 8px 8px 0 0; - box-shadow: 0 -4px 10px 0 rgba(77, 78, 89, 0.01); + box-shadow: 0 -4px 10px 0 @shadow-1; .show-icon { flex-wrap: wrap; gap: 12px; @@ -30,11 +30,17 @@ .input-box { align-items: flex-end; max-height: 160px; - padding: 10px; + padding: 10px 16px; + gap: 8px; background-color: @bg-13; + .more-file-container, + .show-send-container { + height: 40px; + width: 24px; + } + .custom-svg { - margin: 8px; width: 24px; height: 24px; cursor: pointer; @@ -43,10 +49,11 @@ height: 24px; } } + .has-show-more-icon.custom-svg, .has-show-emoji-icon.custom-svg { svg { path { - fill: rgba(91, 142, 244, 1); + fill: @svg-1; } } } @@ -60,14 +67,14 @@ bottom: 8px; } } - .rce-container-input { + .portkey-container-input { min-width: calc(100% - 120px); border-radius: 20px; - .rce-input-textarea { + .portkey-im-input-textarea { height: 40px; padding: 10px 40px 10px 16px; } - .rce-input { + .portkey-im-input { border-radius: 20px; } } @@ -104,10 +111,10 @@ background-color: @bg-11; .portkey-popover-inner { border-radius: 6px; - box-shadow: 0 2px 12px 0 rgba(77, 78, 89, 0.02); + box-shadow: 0 2px 12px 0 @shadow-1; .portkey-popover-inner-content { padding: 0 12px; } } - + } diff --git a/packages/im-ui-web/src/InputBar/index.tsx b/packages/im-ui-web/src/InputBar/index.tsx index 50440d77ce..660fc5dfeb 100644 --- a/packages/im-ui-web/src/InputBar/index.tsx +++ b/packages/im-ui-web/src/InputBar/index.tsx @@ -1,37 +1,45 @@ -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; import { Popover } from 'antd'; -import { emojiList } from '../assets/index'; +import { emojiList } from '../assets/emoji/index'; import CustomSvg from '../components/CustomSvg'; import PopoverMenuList, { IPopoverMenuListData } from '../PopoverMenuList'; -import Input from '../ChatInput'; +import CustomInput from '../components/CustomInput'; import './index.less'; interface IInputBar { - maxlength?: number; + maxLength?: number; moreData?: IPopoverMenuListData[]; showEmoji?: boolean; onSendMessage: (v: string) => void; } -export default function InputBar({ moreData, showEmoji = true, onSendMessage, maxlength = 300, ...props }: IInputBar) { - console.log(props); +export default function InputBar({ moreData, showEmoji = true, onSendMessage, maxLength = 300 }: IInputBar) { const [showEmojiIcon, setShowEmojiIcon] = useState(false); const [value, setValue] = useState(''); const inputRef = useRef(null); const [popVisible, setPopVisible] = useState(false); - const clearPop = (e: any) => { + const formatMoreData = moreData?.map((item) => ({ + ...item, + onClick: () => { + setPopVisible(false); + item?.onClick?.(); + }, + })); + const hidePop = useCallback((e: any) => { try { - if (e.target.className.indexOf('close-show-emoji-icon') === -1) { + const _t = e?.target?.className; + const isFun = _t.includes instanceof Function; + if (isFun && !_t.includes('show-emoji-icon-container')) { setShowEmojiIcon(false); } - if (e.target.className.indexOf('close-more-file') === -1) { + if (isFun && !_t.includes('more-file-container')) { setPopVisible(false); } } catch (e) { - console.log('e', e); + console.log('===input bar hidePop error', e); } - }; + }, []); const handleChange = (e: any) => { setValue(e.target.value); }; @@ -40,17 +48,20 @@ export default function InputBar({ moreData, showEmoji = true, onSendMessage, ma setValue(''); }; const handleEnterKeyDown = (e: any) => { - if (e.keyCode === 13) { + if (e.keyCode === 13 && e.shiftKey) { e.preventDefault(); - if (value) { + setValue(e.target.value + '\n'); + } else if (e.keyCode === 13) { + e.preventDefault(); + if (value?.trim()) { handleSend(); } } }; useEffect(() => { - document.addEventListener('click', clearPop); - return () => document.removeEventListener('click', clearPop); - }, []); + document.addEventListener('click', hidePop); + return () => document.removeEventListener('click', hidePop); + }, [hidePop]); return (
    @@ -58,7 +69,7 @@ export default function InputBar({ moreData, showEmoji = true, onSendMessage, ma {showEmojiIcon && (
    - {emojiList.map(item => ( + {emojiList.map((item) => (
    }> -
    setPopVisible(!popVisible)}> - + content={}> +
    { + setShowEmojiIcon(false); + setPopVisible(!popVisible); + }}> +
    ) : ( <> )} -
    - setShowEmojiIcon(false)} onKeyDown={handleEnterKeyDown} /> {showEmoji && ( -
    +
    { + setPopVisible(false); + setShowEmojiIcon(!showEmojiIcon); + }} className={clsx([showEmojiIcon && 'has-show-emoji-icon'])} type="Emoji" - onClick={() => setShowEmojiIcon(!showEmojiIcon)} />
    )}
    - {value && } + {value?.trim() && ( +
    + +
    + )}
    diff --git a/packages/im-ui-web/src/MessageItem/index.less b/packages/im-ui-web/src/MessageItem/index.less index 4f7a955716..b009929a08 100644 --- a/packages/im-ui-web/src/MessageItem/index.less +++ b/packages/im-ui-web/src/MessageItem/index.less @@ -1,9 +1,10 @@ @import '../assets/theme/color.less'; .portkey-message-item { + position: relative; width: 100%; overflow-x: hidden; } -.showMargin { +.show-margin { margin-top: 8px; } diff --git a/packages/im-ui-web/src/MessageItem/index.tsx b/packages/im-ui-web/src/MessageItem/index.tsx index eeb0633d1d..d81f46639a 100644 --- a/packages/im-ui-web/src/MessageItem/index.tsx +++ b/packages/im-ui-web/src/MessageItem/index.tsx @@ -4,27 +4,24 @@ import clsx from 'clsx'; import ImageMessage from '../ImageMessage'; import TextMessage from '../TextMessage'; import SystemMessage from '../SystemMessage'; -import { MessageBoxType } from '../type'; +import { MessageType } from '../type'; import './index.less'; -const MessageItem: React.FC = ({ styles, ...props }) => { +const MessageItem: React.FC = ({ ...props }) => { const messageRef = useRef(null); return (
    - {props.type === 'system' ? ( - - ) : ( - <> - {props.type === 'text' && } - {props.type === 'image' && } - - )} + // onClick={props?.onClick} + > + <> + {props.type === 'system' && } + {props.type === 'text' && } + {props.type === 'image' && } +
    ); }; diff --git a/packages/im-ui-web/src/MessageList/index.less b/packages/im-ui-web/src/MessageList/index.less index 28bd77ef8b..12f6873730 100644 --- a/packages/im-ui-web/src/MessageList/index.less +++ b/packages/im-ui-web/src/MessageList/index.less @@ -4,7 +4,7 @@ position: relative; width: 100%; height: 100%; - padding: 0 16px 8px; + padding: 0 16px; overflow-y: auto; .loading-more { margin-top: 16px; @@ -17,20 +17,27 @@ position: relative; overflow: auto; flex: 1; + + .portkey-message-item:last-child { + margin-bottom: 16px; + } } .message-list-down-button { position: absolute; - right: 10px; - bottom: 15px; - width: 40px; - height: 40px; + right: 16px; + bottom: 16px; + width: 48px; + height: 48px; background: @bg-11; - box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 5px 0 rgba(0, 0, 0, 0.1); + border: 1px solid @border-2; + box-shadow: 0 2px 2px 0 @shadow-1; border-radius: 100%; cursor: pointer; transition: 200ms; - .leftarrow-icon { - rotate: -90deg; + .doubledown-icon { + width: 24px; + height: 24px; + } } } diff --git a/packages/im-ui-web/src/MessageList/index.tsx b/packages/im-ui-web/src/MessageList/index.tsx index c24fcee3b7..1b35ee176c 100644 --- a/packages/im-ui-web/src/MessageList/index.tsx +++ b/packages/im-ui-web/src/MessageList/index.tsx @@ -1,15 +1,14 @@ -import React, { FC, useEffect, useMemo, useRef, useState } from 'react'; +import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import clsx from 'clsx'; import MessageItem from '../MessageItem'; import CustomSvg from '../components/CustomSvg'; import CircleLoading from '../components/CircleLoading'; import { IMessageListProps, MessageListEvent } from '../type'; - import './index.less'; const MessageList: FC = ({ - referance = null, + reference = null, lockable = false, toBottomHeight = 30, downButton = true, @@ -22,8 +21,8 @@ const MessageList: FC = ({ const [_downButton, setDownButton] = useState(false); const prevProps = useRef(props); - const checkScroll = () => { - var e = referance; + const checkScroll = useCallback(() => { + const e = reference; if (!e || !e.current) return; if (toBottomHeight === '100%' || (toBottomHeight && scrollBottom < (toBottomHeight as number))) { @@ -33,38 +32,47 @@ const MessageList: FC = ({ e.current.scrollTop = e.current.scrollHeight - e.current.offsetHeight - scrollBottom; } } - }; + }, [lockable, reference, scrollBottom, toBottomHeight]); useEffect(() => { - if (!referance) return; + if (!reference) return; if (prevProps.current.dataSource.length !== props.dataSource.length) { - setScrollBottom(getBottom(referance)); + setScrollBottom(getBottom(reference)); checkScroll(); } prevProps.current = props; - }, [prevProps, props]); + }, [checkScroll, prevProps, props, reference]); const getBottom = (e: any) => { if (e.current) return e.current.scrollHeight - e.current.scrollTop - e.current.offsetHeight; return e.scrollHeight - e.scrollTop - e.offsetHeight; }; - const onDownload: MessageListEvent = (item, index, event) => { - if (props.onDownload instanceof Function) props.onDownload(item, index, event); - }; + const onDownload: MessageListEvent = useCallback( + (item, index, event) => { + if (props.onDownload instanceof Function) props.onDownload(item, index, event); + }, + [props], + ); - const onPhotoError: MessageListEvent = (item, index, event) => { - if (props.onPhotoError instanceof Function) props.onPhotoError(item, index, event); - }; + const onPhotoError: MessageListEvent = useCallback( + (item, index, event) => { + if (props.onPhotoError instanceof Function) props.onPhotoError(item, index, event); + }, + [props], + ); - const onDelete = (id: string) => { - if (props.onDelete instanceof Function) props.onDelete(`${id}`); - }; + const onDeleteMsg: MessageListEvent = useCallback( + (item, index, event) => { + if (props.onDeleteMsg instanceof Function) props.onDeleteMsg(item, index, event); + }, + [props], + ); const onScroll = (e: React.UIEvent): void => { - var bottom = getBottom(e.currentTarget); + const bottom = getBottom(e.currentTarget); setScrollBottom(bottom); if (toBottomHeight === '100%' || (toBottomHeight && bottom > (toBottomHeight as number))) { if (_downButton !== true) { @@ -77,7 +85,7 @@ const MessageList: FC = ({ setScrollBottom(bottom); } } - if (referance.current.scrollTop === 0) { + if (reference.current.scrollTop === 0) { if (hasNext) { next(); } @@ -88,44 +96,52 @@ const MessageList: FC = ({ }; const toBottom = (e?: any) => { - if (!referance) return; - referance.current.scrollTop = referance.current.scrollHeight; + if (!reference) return; + reference.current.scrollTop = reference.current.scrollHeight; if (props.onDownButtonClick instanceof Function) { props.onDownButtonClick(e); } }; useEffect(() => { - if (!referance) return; - referance.current.scrollTop = referance.current.scrollHeight; - }, []); + if (!reference) return; + reference.current.scrollTop = reference.current.scrollHeight; + }, [reference]); const renderMessageItem = useMemo(() => { - let prev = 'left'; - let isShowMargin = false; + let prev: any = {}; return props.dataSource.map((x, i: number) => { - if (i === 0) { - prev = x.position; + let isShowMargin = false; + if (x.type === 'system' || prev?.type === 'system') { + isShowMargin = true; } else { - isShowMargin = prev !== x.position; - prev = x.position; + isShowMargin = prev.position !== x.position; } + prev = x; return ( ) => onPhotoError(x, i, e))} onDownload={props.onDownload && ((e: React.MouseEvent) => onDownload(x, i, e))} - onDelete={() => onDelete(`${x.id}`)} + onDeleteMsg={props.onDeleteMsg && ((e: React.MouseEvent) => onDeleteMsg(x, i, e))} /> ); }); - }, [props.dataSource]); + }, [ + onDeleteMsg, + onDownload, + onPhotoError, + props.dataSource, + props.onDeleteMsg, + props.onDownload, + props.onPhotoError, + ]); return (
    -
    +
    {loading && (
    @@ -135,7 +151,7 @@ const MessageList: FC = ({
    {downButton === true && _downButton && toBottomHeight !== '100%' && (
    - +
    )}
    diff --git a/packages/im-ui-web/src/SystemMessage/index.tsx b/packages/im-ui-web/src/SystemMessage/index.tsx index 77d2273efc..96219621b5 100644 --- a/packages/im-ui-web/src/SystemMessage/index.tsx +++ b/packages/im-ui-web/src/SystemMessage/index.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import './index.less'; import { ISystemMessageProps } from '../type'; +import './index.less'; -const SystemMessage: React.FC = props => { +const SystemMessage: React.FC = (props) => { return (
    diff --git a/packages/im-ui-web/src/TextMessage/index.less b/packages/im-ui-web/src/TextMessage/index.less index c954f8c6c7..16b18080c5 100644 --- a/packages/im-ui-web/src/TextMessage/index.less +++ b/packages/im-ui-web/src/TextMessage/index.less @@ -13,7 +13,7 @@ position: relative; flex-direction: column; padding: 8px 12px; - border-radius: 2px 20px 20px 20px; + border-radius: 2px 20px 20px; background: @bg-22; max-width: calc(100% - 30px); &.right { @@ -24,6 +24,7 @@ display: inline-block; font-size: 15px; word-break: break-all; + white-space: pre-wrap; .text-date-hidden { visibility: hidden; } @@ -31,7 +32,7 @@ color: @font-9; cursor: pointer; } - .non-text { + .non-support-msg { color: @font-12; } } @@ -48,9 +49,10 @@ .message-text-popover { .portkey-popover-content { - width: 120px; + min-width: 120px; .portkey-popover-inner { border-radius: 6px; + box-shadow: 0 2px 12px 0 @shadow-1; .portkey-popover-inner-content { padding: 0 12px; } @@ -63,19 +65,19 @@ left: 20px; } } -.message-text-popover.right.portkey-popover-placement-bottom { +.message-text-popover.portkey-popover-placement-top { .portkey-popover-content { - top: -30px; + bottom: -30px; left: -4px; } } -.message-text-popover.portkey-popover-placement-top { +.message-text-popover.right.portkey-popover-placement-bottom { .portkey-popover-content { - bottom: -30px; + top: -30px; left: -4px; } } -.message-text-popover.right.portkey-popover-placement-bottom { +.message-text-popover.right.portkey-popover-placement-top { .portkey-popover-content { bottom: -30px; left: -10px; diff --git a/packages/im-ui-web/src/TextMessage/index.tsx b/packages/im-ui-web/src/TextMessage/index.tsx index 56776ac9dd..661660269f 100644 --- a/packages/im-ui-web/src/TextMessage/index.tsx +++ b/packages/im-ui-web/src/TextMessage/index.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useState, useCallback } from 'react'; import { Popover, message } from 'antd'; import { useCopyToClipboard } from 'react-use'; -import ParsedText from '../components/ParsedText'; +import { ParsedText, ParseShape } from 'react-parsed-text'; import clsx from 'clsx'; import { ITextMessageProps } from '../type'; @@ -10,18 +10,13 @@ import PopoverMenuList from '../PopoverMenuList'; import CustomSvg from '../components/CustomSvg'; import './index.less'; -const TextMessage: React.FC = props => { - const showDate = useMemo(() => (props.dateString ? props.dateString : formatTime(props.date as any)), []); +const TextMessage: React.FC = (props) => { + const showDate = useMemo( + () => (props.dateString ? props.dateString : formatTime(props.date as any)), + [props.date, props.dateString], + ); const [, setCopied] = useCopyToClipboard(); const [popVisible, setPopVisible] = useState(false); - const handleDelMsg = useCallback(async () => { - try { - await props?.onDelete?.(`${props.id}`); - } catch (e) { - message.error('delete message error'); - console.log('===delete message error', e); - } - }, []); const hidePop = () => { setPopVisible(false); }; @@ -32,61 +27,71 @@ const TextMessage: React.FC = props => { children: 'Copy', onClick: () => { setCopied(props.text); + message.success('Copy Success'); }, }, { key: 'delete', leftIcon: , - children: 'Delelte', - onClick: handleDelMsg, + children: 'Delete', + onClick: (e: React.MouseEvent) => props?.onDeleteMsg?.(e), }, ]; + const handleUrlPress: ParseShape['onClick'] = useCallback((url: string) => { + const WWW_URL_PATTERN = /^www\./i; + if (WWW_URL_PATTERN.test(url)) url = `https://${url}`; + const openWinder = window.open(url, '_blank'); + if (openWinder) { + openWinder.opener = null; + } + }, []); useEffect(() => { document.addEventListener('click', hidePop); return () => document.removeEventListener('click', hidePop); }, []); return (
    - setPopVisible(visible)} - showArrow={false} - content={ - props.position === 'right' || (pop.key !== 'delete' && props.position === 'left'), - )} - /> - }> + {props.subType === 'non-support-msg' ? (
    - {props.subType === 'non-text' ? ( - [Not supported message] - ) : ( + [Unsupported format] + {showDate} +
    +
    {showDate}
    +
    + ) : ( + setPopVisible(visible)} + showArrow={false} + content={ + props.position === 'right' || (props.position === 'left' && pop.key !== 'delete'), + )} + /> + }> +
    +
    { - const openWinder = window.open(url, '_blank'); - if (openWinder) { - openWinder.opener = null; - } - }, + onClick: handleUrlPress, }, ]}> {props.text} - )} - {showDate} + {showDate} +
    +
    {showDate}
    -
    {showDate}
    -
    - + + )}
    ); }; diff --git a/packages/im-ui-web/src/assets/emoticonData.json b/packages/im-ui-web/src/assets/emoji/emoticonData.json similarity index 100% rename from packages/im-ui-web/src/assets/emoticonData.json rename to packages/im-ui-web/src/assets/emoji/emoticonData.json diff --git a/packages/im-ui-web/src/assets/index.ts b/packages/im-ui-web/src/assets/emoji/index.ts similarity index 100% rename from packages/im-ui-web/src/assets/index.ts rename to packages/im-ui-web/src/assets/emoji/index.ts diff --git a/packages/im-ui-web/src/assets/svgIcon/Album.svg b/packages/im-ui-web/src/assets/svgIcon/Album.svg index 75aceb02c2..e8bd162bcf 100644 --- a/packages/im-ui-web/src/assets/svgIcon/Album.svg +++ b/packages/im-ui-web/src/assets/svgIcon/Album.svg @@ -1,7 +1,5 @@ - - - - - + + + diff --git a/packages/im-ui-web/src/assets/svgIcon/DoubleDown.svg b/packages/im-ui-web/src/assets/svgIcon/DoubleDown.svg new file mode 100644 index 0000000000..f65abbd24f --- /dev/null +++ b/packages/im-ui-web/src/assets/svgIcon/DoubleDown.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/im-ui-web/src/assets/svgs.ts b/packages/im-ui-web/src/assets/svgs.ts index fec24ad3ea..3edbd1934d 100644 --- a/packages/im-ui-web/src/assets/svgs.ts +++ b/packages/im-ui-web/src/assets/svgs.ts @@ -2,7 +2,7 @@ export default { AddContact: '\n\n\n', Album: - '\n\n\n\n\n\n\n', + '\n\n\n\n\n', Bookmark: '\n\n\n', Close: @@ -10,6 +10,8 @@ export default { Copy: '\n\n\n', Delete: '\n\n\n', + DoubleDown: + '\n\n\n\n', Emoji: '\n\n\n\n\n\n', File: '\n\n\n\n\n\n\n\n\n\n\n\n', diff --git a/packages/im-ui-web/src/assets/theme/color.less b/packages/im-ui-web/src/assets/theme/color.less index 5b0279af74..dee0f15913 100644 --- a/packages/im-ui-web/src/assets/theme/color.less +++ b/packages/im-ui-web/src/assets/theme/color.less @@ -5,10 +5,6 @@ @bg1: ~'var(--@{app-prefix}-bg1)'; @font-1: #262626; -@font-11: #25272A; -@font-9: #5B8EF4; -@font-12: #B6BABF; -@font-13: #515A62; @font-2: #464b53; @font-3: #8f97a6; @font-4: #c7cbd1; @@ -18,14 +14,16 @@ @font-8: #faad14; @font-9: #5b8ef4; @font-10: #252525; +@font-11: #25272A; +@font-12: #B6BABF; +@font-13: #515A62; @font-14: #e7383a; @font-15: #00ab34; @font-16: #595959; +@border-1: #c5cbd5; @border-2: #DEE2E8; @border-3: #5B8EF4; -@border-12: #FFFFFF; -@border-1: #c5cbd5; @border-4: #68aafd; @border-5: #ff4d4f; @border-6: #e3ebfa; @@ -34,38 +32,45 @@ @border-9: #00b9b0; @border-10: #f0f2f5; @border-11:#00AB34; +@border-12: #FFFFFF; -@bg-10: #F7F7F9; -@bg-11: #FFFFFF; -@bg-22: #F0F1F4; -@bg-27: #E7F0FC; -@bg-15: #DEE2E8; -@bg-13: #F7F8F9; -@bg-7: #5B8EF4; -@bg-24: #EA4F45; -@bg-26: #B6BABF; @bg-1: #52c41a; @bg-2: #edf9e8; @bg-3: #faad14; @bg-4: #fef6e7; @bg-5: #ff4d4f; @bg-6: #ffeded; +@bg-7: #5B8EF4; @bg-8: #68aafd; @bg-9: #eef3fd; +@bg-10: #F7F7F9; +@bg-11: #FFFFFF; @bg-12: rgb(0 0 0 / 30%); +@bg-13: #F7F8F9; @bg-14: #2bb6af; +@bg-15: #DEE2E8; @bg-16: #e7383a; @bg-17: #c2c2c2; @bg-18: #c5cbd5; @bg-19: #515a62; @bg-20: #f2f4f6; @bg-21: #00AB34; +@bg-22: #F0F1F4; @bg-23: #68737E; +@bg-24: #EA4F45; @bg-25: #68737E; +@bg-26: #B6BABF; +@bg-27: #E7F0FC; +@bg-28: rgba(81, 90, 98, 0.8); + +@svg-1: rgba(91, 142, 244, 1); +@svg-2: rgba(37,39,42,0.2); @hover1: @border-4; @hover2: @border-3; -@shadow-1: rgba(77, 78, 89, 0.13); +@shadow-1: rgba(77, 78, 89, 0.1); +@shadow-2: rgba(0, 0, 0, 0.05); +@shadow-3: rgba(0, 0, 0, 0.1); @disabled1: @bg-10; diff --git a/packages/im-ui-web/src/components/CommonModal/index.tsx b/packages/im-ui-web/src/components/CommonModal/index.tsx deleted file mode 100644 index 898634b3fc..0000000000 --- a/packages/im-ui-web/src/components/CommonModal/index.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Col, Modal, ModalProps, Row } from 'antd'; -import clsx from 'clsx'; -import { LeftOutlined } from '@ant-design/icons'; -import { ReactNode } from 'react'; -import './styles.less'; - -export interface CommonModalProps extends ModalProps { - className?: string; - leftCallBack?: () => void; - leftElement?: ReactNode; - transitionName?: string; -} - -export default function CommonModal(props: CommonModalProps) { - const { leftCallBack, width, title, leftElement, transitionName } = props; - return ( - - {leftCallBack || leftElement ? ( - - {leftElement || } - - ) : null} - - {title} - - {leftCallBack || leftElement ? : null} - - } - /> - ); -} diff --git a/packages/im-ui-web/src/components/CommonModal/styles.less b/packages/im-ui-web/src/components/CommonModal/styles.less deleted file mode 100644 index bd89fbfac2..0000000000 --- a/packages/im-ui-web/src/components/CommonModal/styles.less +++ /dev/null @@ -1,47 +0,0 @@ -@import '../../assets/theme/color.less'; - -.common-modals { - .@{app-prefix}-modal-content { - border-radius: 8px; - box-shadow: none; - } - .@{app-prefix}-modal-header { - border: none; - border-radius: 8px; - padding: 0; - .@{app-prefix}-modal-title { - line-height: 22px; - font-size: 18px; - padding: 24px 24px 16px; - color: @font-10; - font-weight: 400; - } - } - .@{app-prefix}-modal-body { - padding: 0; - line-height: 20px; - font-size: 14px; - .modal-content { - color: @font-13; - padding: 0 24px 24px; - text-align: center; - border-bottom: 1px solid @border-2; - } - .btn-wrapper { - padding: 15px 16px 17px; - display: grid; - grid-template-columns: 1fr 1fr; - column-gap: 16px; - line-height: 20px; - button { - border-radius: 24px; - line-height: 48px; - height: 48px; - font-size: 16px; - } - } - } - .@{app-prefix}-btn { - line-height: 48px; - } -} diff --git a/packages/im-ui-web/src/components/CustomInput/index.less b/packages/im-ui-web/src/components/CustomInput/index.less new file mode 100644 index 0000000000..6c41c9937d --- /dev/null +++ b/packages/im-ui-web/src/components/CustomInput/index.less @@ -0,0 +1,47 @@ +@import '../../assets/theme/color.less'; + +.portkey-container-input { + display: flex; + min-width: 100%; + box-sizing: border-box; + flex-direction: row; + background: @bg-11; + align-items: center; + + .portkey-im-input { + flex: 1; + height: 40px; + padding: 0 5px; + border: none; + border-radius: 20px; + color: @font-1; + font-size: 14px; + line-height: 20px; + box-sizing: border-box; + outline: none; + } + + .portkey-im-input-textarea { + padding: 10px 5px; + resize: none; + } + + .portkey-im-input:focus { + box-shadow: none; + } + + ::-webkit-scrollbar { + width:3px; + height: 40px; + } + ::-webkit-scrollbar-track { + margin: 20px; + } + ::-webkit-scrollbar-thumb { + border-radius:2px; + background-color:@bg-15; + } + +} + + diff --git a/packages/im-ui-web/src/components/CustomInput/index.tsx b/packages/im-ui-web/src/components/CustomInput/index.tsx new file mode 100644 index 0000000000..4286e5720e --- /dev/null +++ b/packages/im-ui-web/src/components/CustomInput/index.tsx @@ -0,0 +1,128 @@ +import React, { useCallback, useEffect } from 'react'; +import classNames from 'classnames'; +import { IInputProps } from '../../type'; +import './index.less'; + +const CustomInput: React.FC = ({ + type = 'text', + multiline = false, + minHeight = 40, + maxHeight = 140, + autoHeight = true, + autofocus = false, + ...props +}) => { + const onChangeEvent = useCallback( + (e: any) => { + if (props.maxLength && (e.target.value || '').length > props.maxLength) { + if (props.onMaxLengthExceed instanceof Function) props.onMaxLengthExceed(); + + if (props.reference?.current?.value == (e.target.value || '').substring(0, props.maxLength)) return; + } + + if (props.onChange instanceof Function) props.onChange(e); + + if (multiline === true) { + if (!e.target.value) { + e.target.style.height = minHeight + 'px'; + e.target.style.scrollTop = minHeight + 'px'; + } + if (autoHeight === true) { + if (e.target.style.height !== minHeight + 'px') { + e.target.style.height = minHeight + 'px'; + e.target.style.scrollTop = minHeight + 'px'; + } + + let height; + if (e.target.scrollHeight <= maxHeight) height = e.target.scrollHeight + 'px'; + else height = maxHeight + 'px'; + + if (e.target.style.height !== height) { + e.target.style.height = height; + e.target.style.scrollTop = height; + } + } + } + }, + [autoHeight, maxHeight, minHeight, multiline, props], + ); + + const clear = useCallback(() => { + const _event = { + FAKE_EVENT: true, + target: props.reference?.current, + }; + if (props.reference?.current?.value) { + props.reference.current.value = ''; + } + onChangeEvent(_event); + }, [onChangeEvent, props.reference]); + + useEffect(() => { + const _event = { + FAKE_EVENT: true, + target: props.reference?.current, + }; + onChangeEvent(_event); + }, [props.value, props.reference, onChangeEvent]); + + useEffect(() => { + if (autofocus === true) props.reference?.current?.focus(); + + if (props.clear instanceof Function) { + props.clear(clear); + } + }, [autofocus, clear, props]); + + return ( +
    + {multiline === false ? ( + + ) : ( +