From 819e2e98a0ffc1ef2b9dfaf82b2fb1633c39208c Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Thu, 26 May 2022 14:24:22 +0300 Subject: [PATCH 01/99] MILLIX-176: remove zero to bool formatting, remove console.log (#56) Co-authored-by: AShulpekov --- src/js/helper/format.js | 2 +- src/js/redux/reducers/index.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/js/helper/format.js b/src/js/helper/format.js index 188dfc69..aa60f403 100644 --- a/src/js/helper/format.js +++ b/src/js/helper/format.js @@ -15,7 +15,7 @@ export function millix(amount, append_name = true) { export function fiat(amount) { const param = { value : amount, - format_zero: true + format_zero: false }; if (amount > 1) { diff --git a/src/js/redux/reducers/index.js b/src/js/redux/reducers/index.js index 1a41e9a9..98c3c809 100644 --- a/src/js/redux/reducers/index.js +++ b/src/js/redux/reducers/index.js @@ -9,7 +9,6 @@ import { UPDATE_NOTIFICATION_VOLUME, UPDATE_CURRENCY_PAIR_SUMMARY, UPDATE_MESSAGE_STAT } from '../constants/action-types'; import config from '../../../config.js'; -import _ from 'lodash'; const initialState = { network : { @@ -287,7 +286,6 @@ function rootReducer(state = initialState, action) { }); } else if (action.type === UPDATE_MESSAGE_STAT) { - console.log('message_stat assign'); return Object.assign({}, state, { message_stat: { ...action.payload From 086b81d49ff7999ec4903c2abcc250fd8df9fb7b Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Mon, 30 May 2022 20:41:05 +0300 Subject: [PATCH 02/99] MILLIX-190: changes in received(deposit) list (#57) * MILLIX-190: advertisement deposits and event log ui Co-authored-by: AShulpekov Co-authored-by: crank --- src/css/advertisement.scss | 28 +++++++++++ src/index.js | 5 +- ...sement-consumer-settlement-ledger-view.jsx | 47 +++++++++++++++---- .../advertisement/advertisement-list-view.jsx | 2 +- .../advertisement/advertisement-preview.jsx | 2 + src/js/components/app-container.jsx | 2 +- src/js/components/event-log-view.jsx | 4 -- src/js/components/sidebar.jsx | 8 ++-- 8 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/css/advertisement.scss b/src/css/advertisement.scss index 5773430f..aa66970b 100644 --- a/src/css/advertisement.scss +++ b/src/css/advertisement.scss @@ -32,3 +32,31 @@ font-size: 14px; } } + +.hidden_data_search_column { + display: none; +} + +.advertisement_consumer_settlement_ledger_list { + .p-datatable .p-datatable-tbody > tr > td.advertisement_preview_datatable_column { + padding: 1rem; + max-width: 50rem; + } + + .advertisement-slider { + padding: 0; + display: contents; + background-color: transparent; + } + + .advertisement-preview { + overflow: hidden; + margin: 0; + line-height: 1rem; + max-width: none; + } + + a { + color: $color-body-font !important; + } +} diff --git a/src/index.js b/src/index.js index ece5ae45..3bb72fb2 100644 --- a/src/index.js +++ b/src/index.js @@ -50,7 +50,8 @@ import { faCheckCircle, faCodeMerge, faReply, - faEnvelope + faEnvelope, + faLink } from '@fortawesome/free-solid-svg-icons'; import './css/bootstrap/bootstrap.scss'; @@ -76,7 +77,7 @@ library.add(faArrowCircleLeft, faWallet, faKey, faHome, faFingerprint, faLock, faLockOpen, faTimes, faEye, faList, faBars, faSignInAlt, faFileImport, faChevronDown, faChevronUp, faPencilAlt, faSync, faPlusCircle, faPlay, faPause, faQuestionCircle, faThList, faRedo, faEllipsisV, - faRotateLeft, faCodeMerge, faCheckCircle, faReply, faEnvelope); + faRotateLeft, faCodeMerge, faCheckCircle, faReply, faEnvelope, faLink); let apiInfo = { diff --git a/src/js/components/advertisement/advertisement-consumer-settlement-ledger-view.jsx b/src/js/components/advertisement/advertisement-consumer-settlement-ledger-view.jsx index a2e12180..a6cc7627 100644 --- a/src/js/components/advertisement/advertisement-consumer-settlement-ledger-view.jsx +++ b/src/js/components/advertisement/advertisement-consumer-settlement-ledger-view.jsx @@ -6,6 +6,7 @@ import API from '../../api'; import AdvertisementPreview from './advertisement-preview'; import * as format from '../../helper/format'; import moment from 'moment'; +import DatatableActionButtonView from '../utils/datatable-action-button-view'; class AdvertisementConsumerSettlementLedgerView extends Component { @@ -39,12 +40,23 @@ class AdvertisementConsumerSettlementLedgerView extends Component { let total_paid_amount = 0; response.ledger_list?.forEach(item_ledger => { total_paid_amount += item_ledger.deposit; - ledger_list.push({ - payment_date : format.date(item_ledger.payment_date), - presentation_date: format.date(item_ledger.presentation_date), - amount : format.millix(item_ledger.deposit), - preview : this.advertisementPreview(item_ledger) + payment_date : format.date(item_ledger.payment_date), + presentation_date : format.date(item_ledger.presentation_date), + amount : format.millix(item_ledger.deposit), + preview : this.advertisementPreview(item_ledger), + advertisement_url : item_ledger.advertisement_url, + advertisement_headline: item_ledger.advertisement_headline, + advertisement_deck : item_ledger.advertisement_deck, + action : <> + + this.openAdvertisementLink(callback_args)} + callback_args={item_ledger.advertisement_url} + icon={'link'}/> }); }); @@ -57,9 +69,14 @@ class AdvertisementConsumerSettlementLedgerView extends Component { }); } + openAdvertisementLink(data) { + window.open(data.callback_args, '_blank'); + } + advertisementPreview(item_ledger) { return (<> @@ -70,7 +87,7 @@ class AdvertisementConsumerSettlementLedgerView extends Component { render() { return (
-
advertisements deposits +
received advertisements deposits
@@ -83,7 +100,7 @@ class AdvertisementConsumerSettlementLedgerView extends Component {

you have received {format.millix(this.state.total_paid_amount)} in the past 24 hours.
- + this.reloadDatatable()} datatable_reload_timestamp={this.state.datatable_reload_timestamp} @@ -91,9 +108,23 @@ class AdvertisementConsumerSettlementLedgerView extends Component { sortField={'payment_date'} sortOrder={-1} loading={this.state.datatable_loading} + showActionColumn={true} resultColumn={[ { - field: 'preview' + field: 'preview', + class_name: 'advertisement_preview_datatable_column' + }, + { + field : 'advertisement_deck', + class_name: 'hidden_data_search_column' + }, + { + field : 'advertisement_headline', + class_name: 'hidden_data_search_column' + }, + { + field : 'advertisement_url', + class_name: 'hidden_data_search_column' }, { field : 'amount', diff --git a/src/js/components/advertisement/advertisement-list-view.jsx b/src/js/components/advertisement/advertisement-list-view.jsx index 7de6f504..5a7ad31e 100644 --- a/src/js/components/advertisement/advertisement-list-view.jsx +++ b/src/js/components/advertisement/advertisement-list-view.jsx @@ -175,7 +175,7 @@ class AdvertisementListView extends Component { render() { return (
-
advertisements +
advertisements list
diff --git a/src/js/components/advertisement/advertisement-preview.jsx b/src/js/components/advertisement/advertisement-preview.jsx index 8b8bb1f1..1fd5c7fd 100644 --- a/src/js/components/advertisement/advertisement-preview.jsx +++ b/src/js/components/advertisement/advertisement-preview.jsx @@ -29,12 +29,14 @@ class AdvertisementPreview extends Component {
{this.props.headline ? this.props.headline : ''} {(this.props.url || this.props.deck) && ( {this.props.deck ? this.props.deck : ''} - {this.props.url ? this.props.url : ''})} diff --git a/src/js/components/app-container.jsx b/src/js/components/app-container.jsx index a1bcc62e..bcdd2108 100644 --- a/src/js/components/app-container.jsx +++ b/src/js/components/app-container.jsx @@ -58,7 +58,7 @@ class AppContainer extends Component { component={AdvertisementFormView}/> - diff --git a/src/js/components/event-log-view.jsx b/src/js/components/event-log-view.jsx index b8f91e96..f533f789 100644 --- a/src/js/components/event-log-view.jsx +++ b/src/js/components/event-log-view.jsx @@ -65,10 +65,6 @@ class EventsLogView extends Component { sortField={'timestamp'} sortOrder={1} resultColumn={[ - { - field : 'idx', - header: 'id' - }, { field : 'timestamp', header: 'date' diff --git a/src/js/components/sidebar.jsx b/src/js/components/sidebar.jsx index 5fe71d14..32e940ba 100644 --- a/src/js/components/sidebar.jsx +++ b/src/js/components/sidebar.jsx @@ -110,7 +110,7 @@ class Sidebar extends Component { else if (section === 'advertisement' && ( (defaultSelected === '/advertisement-list') || - (defaultSelected === '/advertisement-deposit-list') + (defaultSelected === '/advertisement-received-list') ) ) { result = true; @@ -284,10 +284,10 @@ class Sidebar extends Component { list - + - deposits + received From e2b9dd86622ee7e73427574c7b636f8f711987dc Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Mon, 30 May 2022 20:46:09 +0300 Subject: [PATCH 03/99] MILLIX-message-hotfix: remove history state from view tx button (#58) Co-authored-by: AShulpekov --- src/js/helper/message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/helper/message.js b/src/js/helper/message.js index a47d000f..b95b44eb 100644 --- a/src/js/helper/message.js +++ b/src/js/helper/message.js @@ -53,7 +53,7 @@ export function datatable_format(data) { icon={'reply'}/> ; From 6e7705a32a6307f30ae84cbecb80ce9155d9ada7 Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Mon, 30 May 2022 23:50:31 +0300 Subject: [PATCH 04/99] Millix 193 (#60) * MILLIX-193: support multiple recipient addresses in the compose form Co-authored-by: Eriksson Monteiro Co-authored-by: AShulpekov Co-authored-by: crank --- package-lock.json | 198 ++++++++++++++---- package.json | 2 + src/css/form.scss | 41 +++- src/js/common/transaction.js | 75 +++---- .../message/message-compose-view.jsx | 126 +++++++---- src/js/helper/validate.js | 7 + 6 files changed, 327 insertions(+), 122 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0989fc4..5460673c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1937,6 +1937,11 @@ "@babel/runtime": "^7.6.2" } }, + "@restart/context": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", + "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==" + }, "@restart/hooks": { "version": "0.4.7", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.7.tgz", @@ -2364,6 +2369,11 @@ "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==" }, + "@types/invariant": { + "version": "2.2.35", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.35.tgz", + "integrity": "sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==" + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -2402,7 +2412,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=" }, "@types/jss": { "version": "9.5.8", @@ -2539,7 +2549,7 @@ "@types/warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", - "integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==" + "integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=" }, "@types/webpack": { "version": "4.41.32", @@ -2957,7 +2967,7 @@ "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==" + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, "ansi-colors": { "version": "4.1.3", @@ -2982,7 +2992,7 @@ "ansi-html": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==" + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" }, "ansi-regex": { "version": "5.0.1", @@ -3027,12 +3037,12 @@ "arity-n": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", - "integrity": "sha512-fExL2kFDC1Q2DUOx3whE/9KoN66IzkY4b4zUHUBFM1ojEYjZZYDcUW3bek/ufGionX9giIKDC5redH2IlGqcQQ==" + "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=" }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==" + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, "arr-flatten": { "version": "1.1.0", @@ -3042,7 +3052,7 @@ "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==" + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, "array-back": { "version": "4.0.2", @@ -3075,12 +3085,12 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==" + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==" + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "array.prototype.flat": { "version": "1.3.0", @@ -3112,7 +3122,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1.js": { "version": "5.4.1", @@ -3159,12 +3169,12 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==" + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=" }, "astral-regex": { "version": "2.0.0", @@ -3172,12 +3182,9 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" }, "async-each": { "version": "1.0.3", @@ -3192,7 +3199,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "at-least-node": { "version": "1.0.0", @@ -3389,12 +3396,12 @@ "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==" + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=" }, "babel-plugin-transform-object-rest-spread": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", "requires": { "babel-plugin-syntax-object-rest-spread": "^6.8.0", "babel-runtime": "^6.26.0" @@ -3488,7 +3495,7 @@ "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -3574,7 +3581,7 @@ "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" }, "bfj": { "version": "7.0.2", @@ -3597,6 +3604,15 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3657,7 +3673,7 @@ "bonjour": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "requires": { "array-flatten": "^2.1.0", "deep-equal": "^1.0.1", @@ -3670,7 +3686,7 @@ "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, "bootstrap": { "version": "5.1.3", @@ -3707,7 +3723,7 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browser-process-hrtime": { "version": "1.0.0", @@ -3848,7 +3864,7 @@ "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, "builtin-modules": { "version": "3.3.0", @@ -3858,12 +3874,12 @@ "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==" + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "cacache": { "version": "15.3.0", @@ -3925,7 +3941,7 @@ "caller-callsite": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", "requires": { "callsites": "^2.0.0" }, @@ -3933,14 +3949,14 @@ "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==" + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" } } }, "caller-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", "requires": { "caller-callsite": "^2.0.0" } @@ -4003,7 +4019,7 @@ "chained-function": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/chained-function/-/chained-function-0.5.0.tgz", - "integrity": "sha512-Cq1nLFdK/dxHZNLjmA+LpH4ABI4knigZoCQEDzZt6AAG0GlylcRlTSu2qbwq3O5fJ8TosEJwB0YjtpHsb2FIGA==" + "integrity": "sha1-JWS73994AxlL6/daayvQQe3iOzo=" }, "chalk": { "version": "3.0.0", @@ -4017,7 +4033,7 @@ "change-emitter": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", - "integrity": "sha512-YXzt1cQ4a2jqazhcuSWEOc1K2q8g9H6eWNsyZgi640LDzRWVQ2eDe+Y/kVdftH+vYdPF2rgDb3dLdpxE1jvAxw==" + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" }, "char-regex": { "version": "1.0.2", @@ -5197,7 +5213,7 @@ "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "requires": { "array-uniq": "^1.0.1" } @@ -6830,6 +6846,12 @@ } } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -10270,6 +10292,12 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, + "nan": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", + "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==", + "optional": true + }, "nanoid": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", @@ -11083,6 +11111,14 @@ "mkdirp": "^0.5.5" }, "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -12490,6 +12526,82 @@ "prop-types": "^15.5.8" } }, + "react-chip-input": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/react-chip-input/-/react-chip-input-1.1.0.tgz", + "integrity": "sha512-/MN3juzviZOoBiPSIO3YTTvHRgLKoNIPSzCUM+Esju9y3R6yp7cfHGe5Y/zP13d01U6533rG0OdROlU57vkaYQ==", + "requires": { + "bootstrap": "^4.3.1", + "react-bootstrap": "^1.0.0" + }, + "dependencies": { + "@types/react-transition-group": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz", + "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==", + "requires": { + "@types/react": "*" + } + }, + "bootstrap": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", + "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==" + }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "react-bootstrap": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.6.5.tgz", + "integrity": "sha512-l2rm5LtDI7JMtdGrzaxNl4OJwH0fMIJDlvwQ2TMvs9h9d0E4ELLpG3J45Pox6xUkpuFfXdWUiGazZXyIuv/OKA==", + "requires": { + "@babel/runtime": "^7.14.0", + "@restart/context": "^2.1.4", + "@restart/hooks": "^0.4.7", + "@types/invariant": "^2.2.33", + "@types/prop-types": "^15.7.3", + "@types/react": ">=16.14.8", + "@types/react-transition-group": "^4.4.1", + "@types/warning": "^3.0.0", + "classnames": "^2.3.1", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "prop-types-extra": "^1.1.0", + "react-overlays": "^5.1.2", + "react-transition-group": "^4.4.1", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + } + }, + "react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "react-dev-utils": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", @@ -13357,7 +13469,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "css-select": { "version": "4.3.0", @@ -15995,7 +16107,11 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", @@ -16407,7 +16523,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", @@ -16568,7 +16684,11 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", diff --git a/package.json b/package.json index 7dcadd52..e80094c7 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@trendmicro/react-sidenav": "^0.5.0", + "async": "^3.2.3", "bootstrap": "^5.1.3", "jquery": "2.2.0", "localforage": "^1.9.0", @@ -22,6 +23,7 @@ "react": "^17.0.2", "react-bootstrap": "^2.1.0", "react-bootstrap-typeahead": "^5.2.0", + "react-chip-input": "^1.1.0", "react-dom": "^17.0.2", "react-error-overlay": "6.0.9", "react-redux": "^7.2.3", diff --git a/src/css/form.scss b/src/css/form.scss index d8ab6e7d..fbe27e60 100644 --- a/src/css/form.scss +++ b/src/css/form.scss @@ -23,7 +23,6 @@ body .section_subtitle > .fab { .form-control, .input-group-addon { align-items: center; - justify-content: center; display: inline-flex; font-size: 1em; padding: 0.5em 1em; @@ -118,3 +117,43 @@ label { .btn-outline-default:disabled { color: $border-color; } + +.chip_input { + box-shadow: none; + + &.styles_chip-input__3aumB { + padding: 0; + + > div > div { + padding: 0 !important; + margin: 0 0.1rem; + + .styles_chip__i-PT7, + .styles_chip__i-PT7:hover, + .styles_chip__i-PT7:focus, + .styles_chip__i-PT7:active { + align-items: center; + background: $color-bg-layer-2; + border: 1px solid $color-tab-hover; + display: inline-flex; + font-size: 1rem; + padding: 0 0.7rem 0 1rem; + } + } + } + + .no-gutters { + --bs-gutter-x: "1.rem"; + } + + svg { + fill: $color-body-font; + padding-left: 0.2rem; + } + + .custom-form-control > input { + background-color: transparent; + padding: 0; + margin: 0 0.2rem; + } +} diff --git a/src/js/common/transaction.js b/src/js/common/transaction.js index 8b97b79b..5a1b35ba 100644 --- a/src/js/common/transaction.js +++ b/src/js/common/transaction.js @@ -1,42 +1,42 @@ import API from '../api'; import * as text from '../helper/text'; +import async from 'async'; class Transaction { verifyAddress(transactionParams) { - let stateData = null; return new Promise((resolve, reject) => { - API.verifyAddress(transactionParams.address) - .then(data => { - if (!data.is_valid) { - stateData = { - name : 'address_invalid', - message: 'valid address is required' - }; - reject(stateData); - } - else { - const { - address_base : destinationAddress, - address_key_identifier: destinationAddressIdentifier, - address_version : destinationAddressVersion - } = data; + const verifiedAddresses = []; + async.eachSeries(transactionParams.addresses, (address, callback) => { + API.verifyAddress(address) + .then(data => { + if (!data.is_valid) { + callback({ + name : 'address_invalid', + message: 'valid address is required' + }); + } + else { + verifiedAddresses.push(data); + callback(); + } + }); + }, (err) => { + if (err) { + return reject(err); + } - stateData = { - error_list : [], - address_base : destinationAddress, - address_version : destinationAddressVersion, - address_key_identifier: destinationAddressIdentifier, - amount : transactionParams.amount, - fee : transactionParams.fee, - subject : transactionParams.subject, - message : transactionParams.message, - dns : transactionParams.dns - }; - resolve(stateData); - } - }); + resolve({ + error_list : [], + address_list: verifiedAddresses, + amount : transactionParams.amount, + fee : transactionParams.fee, + subject : transactionParams.subject, + message : transactionParams.message, + dns : transactionParams.dns + }); + }); }); } @@ -96,13 +96,14 @@ class Transaction { }); return { - sending : false, - fee_input_locked : true, - amount : '', - subject : '', - message : '', - destination_address : '', - modal_body_send_result: this.getModalBodySuccessResult(transaction.transaction_id) + sending : false, + fee_input_locked : true, + amount : '', + subject : '', + message : '', + destination_address_list: [], + address_verified_list : [], + modal_body_send_result : this.getModalBodySuccessResult(transaction.transaction_id) }; } } diff --git a/src/js/components/message/message-compose-view.jsx b/src/js/components/message/message-compose-view.jsx index bb9e818a..77aef3c7 100644 --- a/src/js/components/message/message-compose-view.jsx +++ b/src/js/components/message/message-compose-view.jsx @@ -11,6 +11,7 @@ import API from '../../api'; import ErrorList from './../utils/error-list-view'; import Transaction from '../../common/transaction'; import HelpIconView from '../utils/help-icon-view'; +import ReactChipInput from 'react-chip-input'; class MessageComposeView extends Component { @@ -22,30 +23,34 @@ class MessageComposeView extends Component { let message_body = ''; if (propsState.message) { let reply_to_message_body = propsState.message; - message_body = `\n\n______________________________\nOn ${propsState.date} ${address_value} wrote:\n\n${reply_to_message_body}`; + message_body = `\n\n______________________________\nOn ${propsState.date} ${address_value} wrote:\n\n${reply_to_message_body}`; } this.state = { - dns_valid : false, - fee_input_locked : true, - error_list : [], - modal_show_confirmation: false, - modal_show_send_result : false, - modal_body_send_result : [], - address_base : '', - address_version : '', - address_key_identifier : '', - amount : '', - fee : '', - destination_address : address_value || '', - subject : propsState.subject ? this.getReplySubjectText(propsState.subject) : '', - message : message_body, - txid : propsState.txid + dns_valid : false, + fee_input_locked : true, + error_list : [], + modal_show_confirmation : false, + modal_show_send_result : false, + modal_body_send_result : [], + address_base : '', + address_version : '', + address_key_identifier : '', + amount : '', + fee : '', + destination_address_list: address_value ? [address_value] : [], + subject : propsState.subject ? this.getReplySubjectText(propsState.subject) : '', + message : message_body, + txid : propsState.txid }; this.send = this.send.bind(this); } + componentDidMount() { + this.amount.value = format.millix(10000, false); + } + componentWillUnmount() { if (this.state.sending) { API.interruptTransaction().then(_ => _); @@ -105,12 +110,12 @@ class MessageComposeView extends Component { return; } const transaction_param = { - address: validate.required('address', this.destination_address.value, error_list), - amount : validate.amount('amount', this.amount.value, error_list), - fee : validate.amount('fee', this.fee.value, error_list), - subject: this.subject.value, - message: this.message.value, - dns : validate.domain_name('verified sender', this.dns.value, error_list) + addresses: validate.required('address', this.state.destination_address_list, error_list), + amount : validate.amount('amount', this.amount.value, error_list), + fee : validate.amount('fee', this.fee.value, error_list), + subject : this.subject.value, + message : this.message.value, + dns : validate.domain_name('verified sender', this.dns.value, error_list) }; if (error_list.length === 0) { @@ -148,10 +153,9 @@ class MessageComposeView extends Component { } clearSendForm() { - this.destination_address.value = ''; - this.amount.value = ''; - this.subject.value = ''; - this.message.value = ''; + this.amount.value = ''; + this.subject.value = ''; + this.message.value = ''; if (this.props.config.TRANSACTION_FEE_DEFAULT !== undefined) { this.fee.value = format.millix(this.props.config.TRANSACTION_FEE_DEFAULT, false); @@ -174,14 +178,12 @@ class MessageComposeView extends Component { subject: this.state.subject, message: this.state.message }, - transaction_output_list : [ - { - address_base : this.state.address_base, - address_version : this.state.address_version, - address_key_identifier: this.state.address_key_identifier, - amount : this.state.amount - } - ], + transaction_output_list : this.state.address_list.map(address => ({ + address_base : address.address_base, + address_version : address.address_version, + address_key_identifier: address.address_key_identifier, + amount : this.state.amount + })), transaction_output_fee : { fee_type: 'transaction_fee_default', amount : this.state.fee @@ -209,6 +211,30 @@ class MessageComposeView extends Component { }); } + addDestinationAddress(value) { + const chips = this.state.destination_address_list.slice(); + value.split(/\n| /).forEach(address => { + if(chips.includes(address.trim())){ + this.setState({error_list: [ + { + name : 'recipient_already_exist', + message: `recipients must contain only unique addresses. multiple entries of address ${address.trim()}` + } + ]}) + return; + } + chips.push(address.trim()); + }); + + this.setState({destination_address_list: chips}); + }; + + removeDestinationAddress(index) { + const chips = this.state.destination_address_list.slice(); + chips.splice(index, 1); + this.setState({destination_address_list: chips}); + }; + render() { return (
@@ -225,17 +251,28 @@ class MessageComposeView extends Component { + + + + { + if (ref && !ref.state.focused && ref.formControlRef.current.value !== '') { + this.addDestinationAddress(ref.formControlRef.current.value); + ref.formControlRef.current.value = ''; + } + if (!this.chipInputAddress) { + ref.formControlRef.current.placeholder = 'recipients' + this.chipInputAddress = ref; + } + }} + classes="chip_input form-control" + chips={this.state.destination_address_list} + onSubmit={value => this.addDestinationAddress(value)} + onRemove={index => this.removeDestinationAddress(index)} + /> + +
- - - - this.setState({destination_address: c.target.value})} - placeholder="address" - ref={c => this.destination_address = c}/> - - @@ -266,7 +303,6 @@ class MessageComposeView extends Component { placeholder="amount" pattern="[0-9]+([,][0-9]{1,2})?" ref={c => this.amount = c} - value={format.millix(10000, false)} onChange={validate.handleAmountInputChange.bind(this)}/> diff --git a/src/js/helper/validate.js b/src/js/helper/validate.js index f9829018..63a0191a 100644 --- a/src/js/helper/validate.js +++ b/src/js/helper/validate.js @@ -1,4 +1,5 @@ import * as format from './format'; +import _ from 'lodash'; export function required(field_name, value, error_list) { if (typeof value === 'string') { @@ -11,6 +12,12 @@ export function required(field_name, value, error_list) { message: `${field_name} is required` }); } + else if (_.isArray(value) && _.isEmpty(value)) { + error_list.push({ + name : get_error_name('required', field_name), + message: `${field_name} is required` + }); + } return value; } From 6de3bc5c33a271b88549587b8a23d8ce68b338e0 Mon Sep 17 00:00:00 2001 From: crank Date: Tue, 31 May 2022 18:33:09 +0200 Subject: [PATCH 05/99] chip_input empty style --- src/css/form.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/css/form.scss b/src/css/form.scss index fbe27e60..7f320200 100644 --- a/src/css/form.scss +++ b/src/css/form.scss @@ -153,7 +153,6 @@ label { .custom-form-control > input { background-color: transparent; - padding: 0; margin: 0 0.2rem; } } From 4dd84998d242569a11ad6a92a14d03361c32598a Mon Sep 17 00:00:00 2001 From: crank Date: Wed, 1 Jun 2022 16:12:41 +0200 Subject: [PATCH 06/99] send transaction switch to post --- src/js/api/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/api/index.js b/src/js/api/index.js index 704aed45..e4127747 100644 --- a/src/js/api/index.js +++ b/src/js/api/index.js @@ -158,14 +158,14 @@ class API { else { return this.fetchApiMillix(`/XPzc85T3reYmGro1`, { p0: JSON.stringify(transactionOutputPayload) - }); + }, 'POST'); } } sendTransactionWithData(transactionOutputPayload) { return this.fetchApiMillix(`/XQmpDjEVF691r2gX`, { p0: JSON.stringify(transactionOutputPayload) - }); + }, 'POST'); } listTransactionWithDataSent(addressKeyIdentifier) { From 5e9f39a433a8840179fc17ab1bb0536e2f465009 Mon Sep 17 00:00:00 2001 From: crank Date: Wed, 1 Jun 2022 16:17:29 +0200 Subject: [PATCH 07/99] send transaction switch to post --- src/js/api/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/js/api/index.js b/src/js/api/index.js index e4127747..0b0e0a77 100644 --- a/src/js/api/index.js +++ b/src/js/api/index.js @@ -157,14 +157,14 @@ class API { } else { return this.fetchApiMillix(`/XPzc85T3reYmGro1`, { - p0: JSON.stringify(transactionOutputPayload) + p0: transactionOutputPayload }, 'POST'); } } sendTransactionWithData(transactionOutputPayload) { return this.fetchApiMillix(`/XQmpDjEVF691r2gX`, { - p0: JSON.stringify(transactionOutputPayload) + p0: transactionOutputPayload }, 'POST'); } @@ -175,6 +175,7 @@ class API { p11: 'Adl87cz8kC190Nqc' }); } + getStatsTransactionWithDataReceived(addressKeyIdentifier, dateBegin) { return this.fetchApiMillix(`/wWo8DCcoXVlpczoP`, { p0 : dateBegin, From 6d3f99b3bfd68d803159cfcfe7b7dd217e469cbc Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Fri, 3 Jun 2022 13:21:12 +0300 Subject: [PATCH 08/99] Millix 189 (#59) * MILLIX-189: convert /report-issue into send message form Co-authored-by: AShulpekov Co-authored-by: crank --- src/config.js | 5 +- src/css/form.scss | 7 + src/js/components/app-container.jsx | 7 +- src/js/components/{ => help}/faq-view.jsx | 7 +- src/js/components/help/report-issue-view.jsx | 85 ++++ .../system-info-view.jsx} | 63 +-- .../message/message-compose-form.jsx | 411 ++++++++++++++++++ .../message/message-compose-view.jsx | 373 +--------------- src/js/components/sidebar.jsx | 8 +- 9 files changed, 545 insertions(+), 421 deletions(-) rename src/js/components/{ => help}/faq-view.jsx (94%) create mode 100644 src/js/components/help/report-issue-view.jsx rename src/js/components/{report-issue-view.jsx => help/system-info-view.jsx} (84%) create mode 100644 src/js/components/message/message-compose-form.jsx diff --git a/src/config.js b/src/config.js index c3ad2c6b..a5e7471f 100644 --- a/src/config.js +++ b/src/config.js @@ -2,11 +2,14 @@ export const WALLET_LOG_SIZE_MAX = 1000; export const GENESIS_TRANSACTION_ID = '2VngVznbdiQ5tqfWqn2NMP8DijqCbLX79Gygo9yYRVFU6iN35h'; export const GENESIS_SHARD_ID = 'qGuUgMMVmaCvqrvoWG6zARjkrujGMpzJmpNhBgz1y3RjBG7ZR'; export const CURRENCY_PAIR_SUMMARY_REFRESH_INTERVAL_MS = 10 * 60000; +export const DISCORD_URL = 'https://discord.com/invite/nb5uaKq4yX'; +export const REPORT_ISSUE_ADDRESS = 'tcQAg1vsahrrdzA8KM3SmUqbt5KNV2pz1aVsqdPZ1kxw0b013NGMRi19V46EDABTn8vcy5Jg7NjefhYpJ'; const config = { WALLET_LOG_SIZE_MAX, GENESIS_TRANSACTION_ID, GENESIS_SHARD_ID, - CURRENCY_PAIR_SUMMARY_REFRESH_INTERVAL_MS + CURRENCY_PAIR_SUMMARY_REFRESH_INTERVAL_MS, + DISCORD_URL }; export default config; diff --git a/src/css/form.scss b/src/css/form.scss index 513265de..10d67cdb 100644 --- a/src/css/form.scss +++ b/src/css/form.scss @@ -121,6 +121,10 @@ label { .chip_input { box-shadow: none !important; + > div { + width: 100%; + } + &.styles_chip-input__3aumB { padding: 0; @@ -132,10 +136,13 @@ label { .styles_chip__i-PT7:hover, .styles_chip__i-PT7:focus, .styles_chip__i-PT7:active { + margin-left: 0.5rem; align-items: center; background: $color-bg-layer-2; border: 1px solid $color-tab-hover; + border-radius: 1rem; display: inline-flex; + height: 2rem; font-size: 1rem; padding: 0 0.7rem 0 1rem; } diff --git a/src/js/components/app-container.jsx b/src/js/components/app-container.jsx index bcdd2108..8d993246 100644 --- a/src/js/components/app-container.jsx +++ b/src/js/components/app-container.jsx @@ -18,8 +18,9 @@ import NewWalletView from './new-wallet-view'; import ManageWalletView from './manage-wallet-view'; import StatsView from './stats-view'; import BacklogView from './backlog-view'; -import ReportIssueView from './report-issue-view'; -import FaqView from './faq-view'; +import ReportIssueView from './help/report-issue-view'; +import FaqView from './help/faq-view'; +import SystemInfoView from './help/system-info-view'; import AddressListView from './address-list-view'; import MessageComposeView from './message/message-compose-view'; import MessageInboxView from './message/message-inbox-view'; @@ -105,6 +106,8 @@ class AppContainer extends Component { component={TransactionHistoryView}/> + diff --git a/src/js/components/faq-view.jsx b/src/js/components/help/faq-view.jsx similarity index 94% rename from src/js/components/faq-view.jsx rename to src/js/components/help/faq-view.jsx index 0d0a639e..59399d11 100644 --- a/src/js/components/faq-view.jsx +++ b/src/js/components/help/faq-view.jsx @@ -1,7 +1,8 @@ import React, {Component} from 'react'; import {withRouter} from 'react-router-dom'; -import {Button, Col, Row, Table} from 'react-bootstrap'; +import {Col, Row} from 'react-bootstrap'; import {connect} from 'react-redux'; +import {DISCORD_URL} from '../../../config.js'; class FaqView extends Component { @@ -43,7 +44,7 @@ class FaqView extends Component { if you haven't find an answer on faq page or in other site sections you are always welcome to ask a question on discord
@@ -71,7 +72,7 @@ class FaqView extends Component { if you haven't find an answer on faq page or in other site sections you are always welcome to ask a question on discord
diff --git a/src/js/components/help/report-issue-view.jsx b/src/js/components/help/report-issue-view.jsx new file mode 100644 index 00000000..f013bbe8 --- /dev/null +++ b/src/js/components/help/report-issue-view.jsx @@ -0,0 +1,85 @@ +import React, {Component} from 'react'; +import {withRouter} from 'react-router-dom'; +import {connect} from 'react-redux'; +import os from 'os'; +import API from '../../api'; +import {updateNetworkState} from '../../redux/actions'; +import * as format from '../../helper/format'; +import {Col, Row} from 'react-bootstrap'; +import MessageComposeForm from '../message/message-compose-form'; +import {DISCORD_URL, REPORT_ISSUE_ADDRESS} from '../../../config.js'; + + +class ReportIssueView extends Component { + constructor(props) { + super(props); + this.state = { + message: 'loading' + }; + } + + componentDidMount() { + API.getNodeOsInfo().then(node_os_info => { + this.setReportMessage(node_os_info); + }); + } + + setReportMessage(node_os_info) { + this.setState({ + message: `node: + node id - ${this.props.network.node_id}; + key identifier - ${this.props.wallet.address_key_identifier} + build - ${node_os_info.node_millix_version} + build date - ${format.date(node_os_info.node_millix_build_date)} + browser - ${os.release()} +os: + platform - ${node_os_info.platform} + type - ${node_os_info.type} + release - ${node_os_info.release} +hardware: + architecture - ${node_os_info.arch} + memory total - ${node_os_info.memory.total} + memory free - ${node_os_info.memory.free} (${node_os_info.memory.freePercent}) + cpu - ${node_os_info.cpu.model} + cpu load average - ${node_os_info.cpu.loadavg.join(' ')} + ` + }); + } + + render() { + return ( + +
+ + +
+
report issue
+
+

+ to report about an issue please describe it in the message below. + it already contain your system info for your convenience. + if you can't send us a message for any reason please send it to us on discord +

+ +
+
+ +
+
+ ); + } +} + + +export default connect( + state => ({ + network: state.network, + wallet : state.wallet, + config : state.config + }), { + updateNetworkState + })(withRouter(ReportIssueView)); diff --git a/src/js/components/report-issue-view.jsx b/src/js/components/help/system-info-view.jsx similarity index 84% rename from src/js/components/report-issue-view.jsx rename to src/js/components/help/system-info-view.jsx index 46aa89a5..bc701a2b 100644 --- a/src/js/components/report-issue-view.jsx +++ b/src/js/components/help/system-info-view.jsx @@ -2,18 +2,18 @@ import React, {Component} from 'react'; import {withRouter} from 'react-router-dom'; import {Col, Row, Table} from 'react-bootstrap'; import {connect} from 'react-redux'; -import {updateNetworkState} from '../redux/actions'; +import {updateNetworkState} from '../../redux/actions'; import os from 'os'; -import API from '../api'; -import * as format from '../helper/format'; -import HelpIconView from './utils/help-icon-view'; +import API from '../../api'; +import * as format from '../../helper/format'; +import HelpIconView from './../utils/help-icon-view'; -class ReportIssueView extends Component { +class SystemInfoView extends Component { constructor(props) { super(props); this.state = { - nodeOsInfo : { + node_os_info : { arch : '', memory : { free : '', @@ -39,7 +39,7 @@ class ReportIssueView extends Component { componentDidMount() { API.getNodeOsInfo().then(response => { this.setState({ - nodeOsInfo: response + node_os_info: response }); }); } @@ -50,30 +50,11 @@ class ReportIssueView extends Component { return (
- report problem + system info
-
- instructions -
-
-
to report about a problem please follow - these steps: -
-
    -
  • describe current behavior
  • -
  • describe expected behavior
  • -
  • add information provided on this page -
  • -
  • send it to us on discord
  • -
-
-
-
@@ -115,7 +96,7 @@ class ReportIssueView extends Component { build - {this.state.nodeOsInfo.node_millix_version} + {this.state.node_os_info.node_millix_version} @@ -123,7 +104,7 @@ class ReportIssueView extends Component { build date - {format.date(this.state.nodeOsInfo.node_millix_build_date)} + {format.date(this.state.node_os_info.node_millix_build_date)} @@ -151,7 +132,7 @@ class ReportIssueView extends Component { platform - {this.state.nodeOsInfo.platform} + {this.state.node_os_info.platform} @@ -159,7 +140,7 @@ class ReportIssueView extends Component { type - {this.state.nodeOsInfo.type} + {this.state.node_os_info.type} @@ -167,15 +148,13 @@ class ReportIssueView extends Component { release - {this.state.nodeOsInfo.release} + {this.state.node_os_info.release} - -
@@ -189,7 +168,7 @@ class ReportIssueView extends Component { architecture - {this.state.nodeOsInfo.arch} + {this.state.node_os_info.arch} @@ -197,7 +176,7 @@ class ReportIssueView extends Component { memory total - {this.state.nodeOsInfo.memory.total} + {this.state.node_os_info.memory.total} @@ -205,7 +184,7 @@ class ReportIssueView extends Component { memory free - {this.state.nodeOsInfo.memory.free} ({this.state.nodeOsInfo.memory.freePercent}) + {this.state.node_os_info.memory.free} ({this.state.node_os_info.memory.freePercent}) @@ -213,7 +192,7 @@ class ReportIssueView extends Component { cpu - {this.state.nodeOsInfo.cpu.model} + {this.state.node_os_info.cpu.model} @@ -221,7 +200,7 @@ class ReportIssueView extends Component { cpu load average - {this.state.nodeOsInfo.cpu.loadavg.join(' ')} + {this.state.node_os_info.cpu.loadavg.join(' ')} @@ -239,13 +218,9 @@ class ReportIssueView extends Component { export default connect( state => ({ - clock : state.clock, - config : state.config, - log : state.log, network: state.network, wallet : state.wallet, - backlog: state.backlog, node : state.node }), { updateNetworkState - })(withRouter(ReportIssueView)); + })(withRouter(SystemInfoView)); diff --git a/src/js/components/message/message-compose-form.jsx b/src/js/components/message/message-compose-form.jsx new file mode 100644 index 00000000..e232f518 --- /dev/null +++ b/src/js/components/message/message-compose-form.jsx @@ -0,0 +1,411 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import {withRouter} from 'react-router-dom'; +import {Button, Col, Form, Row} from 'react-bootstrap'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import * as format from '../../helper/format'; +import * as validate from '../../helper/validate'; +import ModalView from './../utils/modal-view'; +import * as text from '../../helper/text'; +import API from '../../api'; +import ErrorList from './../utils/error-list-view'; +import Transaction from '../../common/transaction'; +import HelpIconView from '../utils/help-icon-view'; +import ReactChipInput from 'react-chip-input'; + + +class MessageComposeForm extends Component { + constructor(props) { + super(props); + const propsState = props.location.state || {}; + const address_value = propsState.sent ? propsState.address_to : propsState.address_from; + + let message_body = ''; + if (propsState.message) { + let reply_to_message_body = propsState.message; + message_body = `\n\n______________________________\nOn ${propsState.date} ${address_value} wrote:\n\n${reply_to_message_body}`; + } + + this.state = { + dns_valid : false, + fee_input_locked : true, + error_list : [], + modal_show_confirmation : false, + modal_show_send_result : false, + modal_body_send_result : [], + address_base : '', + address_version : '', + address_key_identifier : '', + amount : '', + fee : '', + destination_address_list: address_value ? [address_value] : [], + subject : propsState.subject ? this.getReplySubjectText(propsState.subject) : '', + message : message_body, + txid : propsState.txid + }; + + this.send = this.send.bind(this); + } + + componentWillUnmount() { + if (this.state.sending) { + API.interruptTransaction().then(_ => _); + } + } + + componentWillReceiveProps(nextProps, nextContext) { + if (this.props.message && this.state.message === 'loading') { + this.populateFormFromProps(); + } + } + + componentDidMount() { + this.amount.value = format.millix(10000, false); + if (this.props.message) { + this.populateFormFromProps(); + } + } + + populateFormFromProps() { + this.setState({ + message : this.props.message, + subject : this.props.subject, + destination_address_list: [this.props.destination_address] + }); + } + + getReplySubjectText(subject) { + if (subject.indexOf('re:') !== 0) { + subject = `re: ${subject}`; + } + return subject; + } + + verifySenderDomainName(domain_name, error_list) { + if (!domain_name) { + return Promise.resolve(true); + } + + const error = { + name : 'verified_sender_not_valid', + message: `verified sender must be a valid domain name` + }; + + domain_name = validate.domain_name('domain_name', domain_name, []); + if (domain_name === null) { + error_list.push(error); + + return Promise.resolve(false); + } + else { + return API.isDNSVerified(domain_name, this.props.wallet.address_key_identifier) + .then(data => { + if (!data.is_address_verified) { + error_list.push({ + name : 'verified_sender_not_valid', + message: <>domain name verification failed. click for instructions + }); + } + + return data.is_address_verified; + }) + .catch(() => { + error_list.push(error); + + return false; + }); + } + } + + send() { + let error_list = []; + if (this.state.sending) { + API.interruptTransaction().then(_ => _); + this.setState({ + canceling: true + }); + return; + } + const transaction_param = { + addresses: validate.required('address', this.state.destination_address_list, error_list), + amount : validate.amount('amount', this.amount.value, error_list), + fee : validate.amount('fee', this.fee.value, error_list), + subject : this.subject.value, + message : this.message.value, + dns : validate.domain_name('verified sender', this.dns.value, error_list) + }; + + if (error_list.length === 0) { + this.verifySenderDomainName(transaction_param.dns, error_list).then(_ => { + if (error_list.length === 0) { + Transaction.verifyAddress(transaction_param).then((data) => { + this.setState(data); + this.changeModalShowConfirmation(); + }).catch((error) => { + error_list.push(error); + }); + } + }); + } + + this.setState({ + error_list: error_list + }); + } + + sendTransaction() { + this.setState({ + sending: true + }); + let transaction_output_payload = this.prepareTransactionOutputPayload(); + Transaction.sendTransaction(transaction_output_payload, true).then((data) => { + this.clearSendForm(); + this.changeModalShowConfirmation(false); + this.changeModalShowSendResult(); + this.setState(data); + }).catch((error) => { + this.changeModalShowConfirmation(false); + this.setState(error); + }); + } + + clearSendForm() { + this.destination_address_list = []; + this.amount.value = ''; + this.subject.value = ''; + this.message.value = ''; + + if (this.props.config.TRANSACTION_FEE_DEFAULT !== undefined) { + this.fee.value = format.millix(this.props.config.TRANSACTION_FEE_DEFAULT, false); + } + } + + prepareTransactionOutputPayload() { + const transactionOutputAttribute = {}; + + if (!!this.state.dns) { + transactionOutputAttribute['dns'] = this.state.dns; + } + if (!!this.state.txid) { + transactionOutputAttribute['parent_transaction_id'] = this.state.txid; + } + + return { + transaction_output_attribute: transactionOutputAttribute, + transaction_data : { + subject: this.state.subject, + message: this.state.message + }, + transaction_output_list : this.state.address_list.map(address => ({ + address_base : address.address_base, + address_version : address.address_version, + address_key_identifier: address.address_key_identifier, + amount : this.state.amount + })), + transaction_output_fee : { + fee_type: 'transaction_fee_default', + amount : this.state.fee + } + }; + } + + cancelSendTransaction() { + this.setState({ + canceling: false, + sending : false + }); + this.changeModalShowConfirmation(false); + } + + changeModalShowConfirmation(value = true) { + this.setState({ + modal_show_confirmation: value + }); + } + + changeModalShowSendResult(value = true) { + this.setState({ + modal_show_send_result: value + }); + } + + addDestinationAddress(value) { + const chips = this.state.destination_address_list.slice(); + value.split(/\n| /).forEach(address => { + if (chips.includes(address.trim())) { + this.setState({ + error_list: [ + { + name : 'recipient_already_exist', + message: `recipients must contain only unique addresses. multiple entries of address ${address.trim()}` + } + ] + }); + return; + } + chips.push(address.trim()); + }); + + this.setState({destination_address_list: chips}); + }; + + removeDestinationAddress(index) { + const chips = this.state.destination_address_list.slice(); + chips.splice(index, 1); + this.setState({destination_address_list: chips}); + }; + + render() { + return ( + <> + + + + + + { + if (ref && !ref.state.focused && ref.formControlRef.current.value !== '') { + this.addDestinationAddress(ref.formControlRef.current.value); + ref.formControlRef.current.value = ''; + } + if (!this.chipInputAddress) { + ref.formControlRef.current.placeholder = 'recipients'; + this.chipInputAddress = ref; + } + }} + classes="chip_input form-control" + chips={this.state.destination_address_list} + onSubmit={value => this.addDestinationAddress(value)} + onRemove={index => this.removeDestinationAddress(index)} + /> + + + + + + + this.setState({subject: c.target.value})} + placeholder="subject" + ref={c => this.subject = c}/> + + + + + + this.setState({message: c.target.value})} + placeholder="message" + autoFocus + ref={c => { + this.message = c; + }}/> + + + + + + this.amount = c} + onChange={validate.handleAmountInputChange.bind(this)}/> + + + + + + + { + this.fee = c; + if (this.fee && !this.feeInitialized && this.props.config.TRANSACTION_FEE_DEFAULT !== undefined) { + this.feeInitialized = true; + this.fee.value = format.millix(this.props.config.TRANSACTION_FEE_DEFAULT, false); + } + }} + onChange={validate.handleAmountInputChange.bind(this)} + disabled={this.state.fee_input_locked}/> + + + + + + + + + this.dns = c} + onChange={e => { + validate.handleInputChangeDNSString(e); + }}/> + + + + + this.sendTransaction()} + on_close={() => this.cancelSendTransaction()} + body={
+
you are about to send a message and {format.millix(this.state.amount)} to
+
{this.state.address_base}{this.state.address_version}{this.state.address_key_identifier}
+ {text.get_confirmation_modal_question()} +
}/> + this.changeModalShowSendResult(false)} + heading={'message has been sent'} + body={this.state.modal_body_send_result}/> + + + + + +
+ + ); + } +} + + +export default connect( + state => ({ + wallet: state.wallet, + config: state.config + }) +)(withRouter(MessageComposeForm)); diff --git a/src/js/components/message/message-compose-view.jsx b/src/js/components/message/message-compose-view.jsx index 77aef3c7..0d6bca3f 100644 --- a/src/js/components/message/message-compose-view.jsx +++ b/src/js/components/message/message-compose-view.jsx @@ -1,239 +1,10 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; -import {Button, Col, Form, Row} from 'react-bootstrap'; -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import * as format from '../../helper/format'; -import * as validate from '../../helper/validate'; -import ModalView from './../utils/modal-view'; -import * as text from '../../helper/text'; -import API from '../../api'; -import ErrorList from './../utils/error-list-view'; -import Transaction from '../../common/transaction'; -import HelpIconView from '../utils/help-icon-view'; -import ReactChipInput from 'react-chip-input'; - +import {Col, Row} from 'react-bootstrap'; +import MessageComposeForm from './message-compose-form'; class MessageComposeView extends Component { - constructor(props) { - super(props); - const propsState = props.location.state || {}; - const address_value = propsState.sent ? propsState.address_to : propsState.address_from; - - let message_body = ''; - if (propsState.message) { - let reply_to_message_body = propsState.message; - message_body = `\n\n______________________________\nOn ${propsState.date} ${address_value} wrote:\n\n${reply_to_message_body}`; - } - - this.state = { - dns_valid : false, - fee_input_locked : true, - error_list : [], - modal_show_confirmation : false, - modal_show_send_result : false, - modal_body_send_result : [], - address_base : '', - address_version : '', - address_key_identifier : '', - amount : '', - fee : '', - destination_address_list: address_value ? [address_value] : [], - subject : propsState.subject ? this.getReplySubjectText(propsState.subject) : '', - message : message_body, - txid : propsState.txid - }; - - this.send = this.send.bind(this); - } - - componentDidMount() { - this.amount.value = format.millix(10000, false); - } - - componentWillUnmount() { - if (this.state.sending) { - API.interruptTransaction().then(_ => _); - } - } - - getReplySubjectText(subject) { - if (subject.indexOf('re:') !== 0) { - subject = `re: ${subject}`; - } - return subject; - } - - verifySenderDomainName(domain_name, error_list) { - if (!domain_name) { - return Promise.resolve(true); - } - - const error = { - name : 'verified_sender_not_valid', - message: `verified sender must be a valid domain name` - }; - - domain_name = validate.domain_name('domain_name', domain_name, []); - if (domain_name === null) { - error_list.push(error); - - return Promise.resolve(false); - } - else { - return API.isDNSVerified(domain_name, this.props.wallet.address_key_identifier) - .then(data => { - if (!data.is_address_verified) { - error_list.push({ - name : 'verified_sender_not_valid', - message: <>domain name verification failed. click for instructions - }); - } - - return data.is_address_verified; - }) - .catch(() => { - error_list.push(error); - - return false; - }); - } - } - - send() { - let error_list = []; - if (this.state.sending) { - API.interruptTransaction().then(_ => _); - this.setState({ - canceling: true - }); - return; - } - const transaction_param = { - addresses: validate.required('address', this.state.destination_address_list, error_list), - amount : validate.amount('amount', this.amount.value, error_list), - fee : validate.amount('fee', this.fee.value, error_list), - subject : this.subject.value, - message : this.message.value, - dns : validate.domain_name('verified sender', this.dns.value, error_list) - }; - - if (error_list.length === 0) { - this.verifySenderDomainName(transaction_param.dns, error_list).then(_ => { - if (error_list.length === 0) { - Transaction.verifyAddress(transaction_param).then((data) => { - this.setState(data); - this.changeModalShowConfirmation(); - }).catch((error) => { - error_list.push(error); - }); - } - }); - } - - this.setState({ - error_list: error_list - }); - } - - sendTransaction() { - this.setState({ - sending: true - }); - let transaction_output_payload = this.prepareTransactionOutputPayload(); - Transaction.sendTransaction(transaction_output_payload, true).then((data) => { - this.clearSendForm(); - this.changeModalShowConfirmation(false); - this.changeModalShowSendResult(); - this.setState(data); - }).catch((error) => { - this.changeModalShowConfirmation(false); - this.setState(error); - }); - } - - clearSendForm() { - this.amount.value = ''; - this.subject.value = ''; - this.message.value = ''; - - if (this.props.config.TRANSACTION_FEE_DEFAULT !== undefined) { - this.fee.value = format.millix(this.props.config.TRANSACTION_FEE_DEFAULT, false); - } - } - - prepareTransactionOutputPayload() { - const transactionOutputAttribute = {}; - - if (!!this.state.dns) { - transactionOutputAttribute['dns'] = this.state.dns; - } - if (!!this.state.txid) { - transactionOutputAttribute['parent_transaction_id'] = this.state.txid; - } - - return { - transaction_output_attribute: transactionOutputAttribute, - transaction_data : { - subject: this.state.subject, - message: this.state.message - }, - transaction_output_list : this.state.address_list.map(address => ({ - address_base : address.address_base, - address_version : address.address_version, - address_key_identifier: address.address_key_identifier, - amount : this.state.amount - })), - transaction_output_fee : { - fee_type: 'transaction_fee_default', - amount : this.state.fee - } - }; - } - - cancelSendTransaction() { - this.setState({ - canceling: false, - sending : false - }); - this.changeModalShowConfirmation(false); - } - - changeModalShowConfirmation(value = true) { - this.setState({ - modal_show_confirmation: value - }); - } - - changeModalShowSendResult(value = true) { - this.setState({ - modal_show_send_result: value - }); - } - - addDestinationAddress(value) { - const chips = this.state.destination_address_list.slice(); - value.split(/\n| /).forEach(address => { - if(chips.includes(address.trim())){ - this.setState({error_list: [ - { - name : 'recipient_already_exist', - message: `recipients must contain only unique addresses. multiple entries of address ${address.trim()}` - } - ]}) - return; - } - chips.push(address.trim()); - }); - - this.setState({destination_address_list: chips}); - }; - - removeDestinationAddress(index) { - const chips = this.state.destination_address_list.slice(); - chips.splice(index, 1); - this.setState({destination_address_list: chips}); - }; render() { return ( @@ -248,145 +19,7 @@ class MessageComposeView extends Component { device. to transport the message to reach the recipient, the message is stored on the millix network for up to 90 days. only you and the recipient can read your messages.

- - - - - - { - if (ref && !ref.state.focused && ref.formControlRef.current.value !== '') { - this.addDestinationAddress(ref.formControlRef.current.value); - ref.formControlRef.current.value = ''; - } - if (!this.chipInputAddress) { - ref.formControlRef.current.placeholder = 'recipients' - this.chipInputAddress = ref; - } - }} - classes="chip_input form-control" - chips={this.state.destination_address_list} - onSubmit={value => this.addDestinationAddress(value)} - onRemove={index => this.removeDestinationAddress(index)} - /> - - -
- - - - this.setState({subject: c.target.value})} - placeholder="subject" - ref={c => this.subject = c}/> - - - - - - this.setState({message: c.target.value})} - placeholder="message" - autoFocus - ref={c => { - this.message = c; - }}/> - - - - - - this.amount = c} - onChange={validate.handleAmountInputChange.bind(this)}/> - - - - - - - { - this.fee = c; - if (this.fee && !this.feeInitialized && this.props.config.TRANSACTION_FEE_DEFAULT !== undefined) { - this.feeInitialized = true; - this.fee.value = format.millix(this.props.config.TRANSACTION_FEE_DEFAULT, false); - } - }} - onChange={validate.handleAmountInputChange.bind(this)} - disabled={this.state.fee_input_locked}/> - - - - - - - - - this.dns = c} - onChange={e => { - validate.handleInputChangeDNSString(e); - }}/> - - - - - this.sendTransaction()} - on_close={() => this.cancelSendTransaction()} - body={
-
you are about to send a message and {format.millix(this.state.amount)} to
-
{this.state.address_base}{this.state.address_version}{this.state.address_key_identifier}
- {text.get_confirmation_modal_question()} -
}/> - this.changeModalShowSendResult(false)} - heading={'message has been sent'} - body={this.state.modal_body_send_result}/> - - - - - -
+
diff --git a/src/js/components/sidebar.jsx b/src/js/components/sidebar.jsx index 32e940ba..9980225c 100644 --- a/src/js/components/sidebar.jsx +++ b/src/js/components/sidebar.jsx @@ -138,7 +138,8 @@ class Sidebar extends Component { else if (section === 'help' && ( (defaultSelected === '/faq') || - (defaultSelected === '/report-issue') + (defaultSelected === '/report-issue') || + (defaultSelected === '/system-info') ) ) { result = true; @@ -429,6 +430,11 @@ class Sidebar extends Component { report problem + + + system info + + From 66253093a5cc1e0204f988a45471fe4acc53ca5e Mon Sep 17 00:00:00 2001 From: crank Date: Fri, 3 Jun 2022 12:37:01 +0200 Subject: [PATCH 09/99] report issue cosmetic fixes --- src/css/form.scss | 13 +++++++++---- src/js/components/help/report-issue-view.jsx | 4 ++-- src/js/components/sidebar.jsx | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/css/form.scss b/src/css/form.scss index 10d67cdb..ff008890 100644 --- a/src/css/form.scss +++ b/src/css/form.scss @@ -121,12 +121,9 @@ label { .chip_input { box-shadow: none !important; - > div { - width: 100%; - } - &.styles_chip-input__3aumB { padding: 0; + border: $input-border-width solid $input-border-color; > div > div { padding: 0 !important; @@ -149,6 +146,14 @@ label { } } + &.styles_shadow-primary__2LDux { + box-shadow: none; + } + + > div { + width: 100%; + } + .no-gutters { --bs-gutter-x: 1rem; } diff --git a/src/js/components/help/report-issue-view.jsx b/src/js/components/help/report-issue-view.jsx index f013bbe8..c3f175a0 100644 --- a/src/js/components/help/report-issue-view.jsx +++ b/src/js/components/help/report-issue-view.jsx @@ -26,7 +26,7 @@ class ReportIssueView extends Component { setReportMessage(node_os_info) { this.setState({ - message: `node: + message: `\n\nnode: node id - ${this.props.network.node_id}; key identifier - ${this.props.wallet.address_key_identifier} build - ${node_os_info.node_millix_version} @@ -62,7 +62,7 @@ hardware:

diff --git a/src/js/components/sidebar.jsx b/src/js/components/sidebar.jsx index 9980225c..befdbd47 100644 --- a/src/js/components/sidebar.jsx +++ b/src/js/components/sidebar.jsx @@ -427,7 +427,7 @@ class Sidebar extends Component { - report problem + report issue From 3cc3ad8f5c599535e576ebb3e963c935d7bc032a Mon Sep 17 00:00:00 2001 From: crank Date: Fri, 3 Jun 2022 16:07:43 +0200 Subject: [PATCH 10/99] change REPORT_ISSUE_ADDRESS --- src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.js b/src/config.js index a5e7471f..89a4e80a 100644 --- a/src/config.js +++ b/src/config.js @@ -3,7 +3,7 @@ export const GENESIS_TRANSACTION_ID = '2VngVznbdiQ5tqfWqn2NMP export const GENESIS_SHARD_ID = 'qGuUgMMVmaCvqrvoWG6zARjkrujGMpzJmpNhBgz1y3RjBG7ZR'; export const CURRENCY_PAIR_SUMMARY_REFRESH_INTERVAL_MS = 10 * 60000; export const DISCORD_URL = 'https://discord.com/invite/nb5uaKq4yX'; -export const REPORT_ISSUE_ADDRESS = 'tcQAg1vsahrrdzA8KM3SmUqbt5KNV2pz1aVsqdPZ1kxw0b013NGMRi19V46EDABTn8vcy5Jg7NjefhYpJ'; +export const REPORT_ISSUE_ADDRESS = 'tA6KT866gnf9UbqwNwvs67SYPXk4Jgm1Y9z43H5YZKdw0b01H6GPn9imdJCGuoxa5syub43JuW4Fi14DA'; const config = { WALLET_LOG_SIZE_MAX, GENESIS_TRANSACTION_ID, From 8f8ea4239f3493af24dd31392d4bfc030e64e0f8 Mon Sep 17 00:00:00 2001 From: crank Date: Fri, 3 Jun 2022 16:36:08 +0200 Subject: [PATCH 11/99] hide extra fields on report issue form --- src/js/components/help/report-issue-view.jsx | 10 ++++---- .../message/message-compose-form.jsx | 23 +++++++++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/js/components/help/report-issue-view.jsx b/src/js/components/help/report-issue-view.jsx index c3f175a0..7a8fed94 100644 --- a/src/js/components/help/report-issue-view.jsx +++ b/src/js/components/help/report-issue-view.jsx @@ -26,7 +26,7 @@ class ReportIssueView extends Component { setReportMessage(node_os_info) { this.setState({ - message: `\n\nnode: + message: `\n\n-----------------------------\n\nnode: node id - ${this.props.network.node_id}; key identifier - ${this.props.wallet.address_key_identifier} build - ${node_os_info.node_millix_version} @@ -56,14 +56,16 @@ hardware:
report issue

- to report about an issue please describe it in the message below. - it already contain your system info for your convenience. - if you can't send us a message for any reason please send it to us on discord + report your issue on discord (free but slower response) or + send a message directly to the development team by describing your issue in the field below (2,000 millix).

diff --git a/src/js/components/message/message-compose-form.jsx b/src/js/components/message/message-compose-form.jsx index e232f518..3e6f55fc 100644 --- a/src/js/components/message/message-compose-form.jsx +++ b/src/js/components/message/message-compose-form.jsx @@ -60,7 +60,12 @@ class MessageComposeForm extends Component { } componentDidMount() { - this.amount.value = format.millix(10000, false); + let amount_default = 10000; + if (this.props.amount) { + amount_default = this.props.amount; + } + this.amount.value = format.millix(amount_default, false); + if (this.props.message) { this.populateFormFromProps(); } @@ -255,13 +260,17 @@ class MessageComposeForm extends Component { this.setState({destination_address_list: chips}); }; + getFieldClassname(field) { + return this.props.hidden_field_list?.includes(field) ? 'd-none' : ''; + } + render() { return ( <> - +
- + - + this.setState({message: c.target.value})} @@ -306,7 +315,7 @@ class MessageComposeForm extends Component { }}/> - + - + @@ -344,7 +353,7 @@ class MessageComposeForm extends Component { - + From 84a687a0d6ee65a405f00069f9c37b4bc8b52218 Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Mon, 6 Jun 2022 17:39:38 +0300 Subject: [PATCH 12/99] MILLIX-191: dns validation * MILLIX-191: dns validation Co-authored-by: AShulpekov Co-authored-by: crank --- src/css/form.scss | 2 +- src/css/loader.scss | 28 ++++--- src/css/message.scss | 6 ++ .../message/message-compose-form.jsx | 80 +++++++++++++++---- src/js/components/wallet-view.jsx | 9 +-- 5 files changed, 90 insertions(+), 35 deletions(-) diff --git a/src/css/form.scss b/src/css/form.scss index ff008890..ccf5ca83 100644 --- a/src/css/form.scss +++ b/src/css/form.scss @@ -30,7 +30,7 @@ body .section_subtitle > .fab { .btn, .btn-xs { - display: inline-block; + display: inline-flex; } .input-group-btn { diff --git a/src/css/loader.scss b/src/css/loader.scss index ecb7c090..6e23bc9f 100644 --- a/src/css/loader.scss +++ b/src/css/loader.scss @@ -6,13 +6,26 @@ border: 0.25rem solid $border-color; border-top: 0.25rem solid $color-primary; border-radius: 50%; - height: 1.8rem; - width: 1.8rem; + height: 1.25rem; + width: 1.25rem; +} + +.btn-outline-input-group-addon.loader { + opacity: 1; + padding: 0.5em 0.8em; + .loader-spin { + margin-right: 0; + } } .btn { .loader-spin { - margin-right: 0.3em; + margin-right: 0.3rem; + } +} + +.btn.loader { + .loader-spin { height: 1.25rem; width: 1.25rem; } @@ -22,18 +35,11 @@ @include loader; } -.loader-spin-xs { - @include loader; - border-width: 1px; - height: 1rem; - width: 1rem; -} - .loader-container { width: 100%; height: 100vh; background: #00000067; - position: absolute; + position: fixed; display: flex; align-items: center; justify-content: center; diff --git a/src/css/message.scss b/src/css/message.scss index b9509639..518f140a 100644 --- a/src/css/message.scss +++ b/src/css/message.scss @@ -28,3 +28,9 @@ margin-left: 0.3rem; } } + +.message_compose { + .verified_sender_mark { + margin-top: 0.3rem; + } +} diff --git a/src/js/components/message/message-compose-form.jsx b/src/js/components/message/message-compose-form.jsx index 3e6f55fc..7b1b3d68 100644 --- a/src/js/components/message/message-compose-form.jsx +++ b/src/js/components/message/message-compose-form.jsx @@ -11,6 +11,7 @@ import API from '../../api'; import ErrorList from './../utils/error-list-view'; import Transaction from '../../common/transaction'; import HelpIconView from '../utils/help-icon-view'; +import {changeLoaderState} from '../loader'; import ReactChipInput from 'react-chip-input'; @@ -28,6 +29,7 @@ class MessageComposeForm extends Component { this.state = { dns_valid : false, + dns_validating : false, fee_input_locked : true, error_list : [], modal_show_confirmation : false, @@ -48,6 +50,7 @@ class MessageComposeForm extends Component { } componentWillUnmount() { + clearTimeout(this.checkDNSHandler); if (this.state.sending) { API.interruptTransaction().then(_ => _); } @@ -86,8 +89,11 @@ class MessageComposeForm extends Component { return subject; } - verifySenderDomainName(domain_name, error_list) { + verifySenderDomainName(domain_name, error_list = []) { if (!domain_name) { + this.setState({ + dns_valid: true + }); return Promise.resolve(true); } @@ -158,7 +164,34 @@ class MessageComposeForm extends Component { }); } + validateDns(e) { + const error_list = []; + validate.handleInputChangeDNSString(e); + this.setState({ + dns_valid : false, + dns_validating: true, + error_list : error_list + }); + clearTimeout(this.checkDNSHandler); + this.checkDNSHandler = setTimeout(() => { + this.verifySenderDomainName(e.target.value, error_list).then((result) => { + this.setState({ + error_list : error_list, + dns_valid : result, + dns_validating: false + }); + }).catch(() => { + this.setState({ + error_list : error_list, + dns_validating: false, + dns_valid : false + }); + }); + }, 500); + } + sendTransaction() { + changeLoaderState(true); this.setState({ sending: true }); @@ -168,17 +201,19 @@ class MessageComposeForm extends Component { this.changeModalShowConfirmation(false); this.changeModalShowSendResult(); this.setState(data); + changeLoaderState(false); }).catch((error) => { this.changeModalShowConfirmation(false); this.setState(error); + changeLoaderState(false); }); } clearSendForm() { this.destination_address_list = []; - this.amount.value = ''; - this.subject.value = ''; - this.message.value = ''; + this.amount.value = ''; + this.subject.value = ''; + this.message.value = ''; if (this.props.config.TRANSACTION_FEE_DEFAULT !== undefined) { this.fee.value = format.millix(this.props.config.TRANSACTION_FEE_DEFAULT, false); @@ -269,7 +304,7 @@ class MessageComposeForm extends Component { <> - + @@ -347,8 +382,7 @@ class MessageComposeForm extends Component { type="button" onClick={() => this.setState({fee_input_locked: !this.state.fee_input_locked})}> + icon={this.state.fee_input_locked ? 'lock' : 'lock-open'}/> @@ -362,10 +396,25 @@ class MessageComposeForm extends Component { placeholder="domain name" pattern="^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$" ref={c => this.dns = c} - onChange={e => { - validate.handleInputChangeDNSString(e); - }}/> + onChange={e => this.validateDns(e)}/> + {this.state.dns_validating ? + + : ''} + {this.state.dns_valid && this.dns?.value !== '' ? +
+ + {this.dns.value} +
+ : ''} +
diff --git a/src/js/components/wallet-view.jsx b/src/js/components/wallet-view.jsx index e6173baf..f3848fd2 100644 --- a/src/js/components/wallet-view.jsx +++ b/src/js/components/wallet-view.jsx @@ -190,8 +190,7 @@ class WalletView extends Component { type="button" onClick={() => this.setState({fee_input_locked: !this.state.fee_input_locked})}> + icon={this.state.fee_input_locked ? 'lock' : 'lock-open'}/>
@@ -223,11 +222,7 @@ class WalletView extends Component { disabled={this.state.canceling}> {this.state.sending ? <> -
+
{this.state.canceling ? 'canceling' : 'cancel transaction'} : <>send} From 84866efa92ce227936aa3b2b2e396526413e52d7 Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:29:58 +0300 Subject: [PATCH 13/99] MILLIX-195: fix link in error message does not work * MILLIX-195: fix link in error message does not work Co-authored-by: AShulpekov --- src/js/common/transaction.js | 4 +- .../action/output-aggregate-view.jsx | 3 +- .../components/utils/user-interface-error.jsx | 86 +++++++++++++++++++ src/js/helper/text.js | 54 ------------ 4 files changed, 90 insertions(+), 57 deletions(-) create mode 100644 src/js/components/utils/user-interface-error.jsx diff --git a/src/js/common/transaction.js b/src/js/common/transaction.js index 5a1b35ba..adfb94ce 100644 --- a/src/js/common/transaction.js +++ b/src/js/common/transaction.js @@ -1,6 +1,6 @@ import API from '../api'; -import * as text from '../helper/text'; import async from 'async'; +import UserInterfaceError from '../components/utils/user-interface-error'; class Transaction { @@ -71,7 +71,7 @@ class Transaction { let error_list = []; if (e !== 'validation_error') { if (e && e.api_message) { - sendTransactionErrorMessage = text.get_ui_error(e.api_message); + sendTransactionErrorMessage = ; } else { sendTransactionErrorMessage = `your transaction could not be sent: (${e?.api_message?.error.error || e?.api_message?.error || e?.message || e?.api_message || e || 'undefined behaviour'})`; diff --git a/src/js/components/action/output-aggregate-view.jsx b/src/js/components/action/output-aggregate-view.jsx index 94233af7..5161bba9 100644 --- a/src/js/components/action/output-aggregate-view.jsx +++ b/src/js/components/action/output-aggregate-view.jsx @@ -9,6 +9,7 @@ import * as text from '../../helper/text'; import API from '../../api'; import HelpIconView from '../utils/help-icon-view'; import SubmitButtonView from '../utils/submit-button-view'; +import UserInterfaceError from '../utils/user-interface-error'; class OutputAggregateView extends Component { @@ -93,7 +94,7 @@ class OutputAggregateView extends Component { }) .catch((e) => { if (e && e.api_message) { - const error_message = text.get_ui_error(e.api_message); + const error_message = ; errorList.push({ name : 'sendTransactionError', diff --git a/src/js/components/utils/user-interface-error.jsx b/src/js/components/utils/user-interface-error.jsx new file mode 100644 index 00000000..75a8aa5b --- /dev/null +++ b/src/js/components/utils/user-interface-error.jsx @@ -0,0 +1,86 @@ +import React, {Component} from 'react'; +import HelpIconView from './help-icon-view'; +import * as format from '../../helper/format'; +import {withRouter} from 'react-router-dom'; + + +class UserInterfaceError extends Component { + constructor(props) { + super(props); + this.state = { + error: '' + }; + } + + componentDidMount() { + this.set_ui_error(); + } + + set_ui_error() { + let api_message = this.props.api_message; + let error = ''; + let api_error_name = 'unknown'; + + if (typeof (api_message) === 'object') { + let result_error = api_message.error; + if (typeof (result_error?.error) !== 'undefined') { + api_error_name = result_error.error; + } + else { + api_error_name = result_error; + } + + switch (api_error_name) { + case 'transaction_input_max_error': + error = <> + your transaction tried to use too many outputs please try to send a + smaller + amount or click this.props.history.push('/actions')}>here to use the balance aggregation tool to + optimize your balance. the max amount you can send is {format.millix(api_message.data.amount_max)}.; + break; + case 'insufficient_balance': + error = <>your balance is lower than the amount you are trying to send. the max amount you can send + is {format.millix(api_message.data.balance_stable)}.; + break; + case 'transaction_send_interrupt': + error = <>transaction has been canceled.; + break; + case 'proxy_not_found': + error = <>proxy not found. please try again.; + break; + case 'transaction_proxy_rejected': + error = <>transaction rejected by a proxy. please try again.; + break; + case 'aggregation_not_required': + error = <>cannot process the request. the aggregation will generate on the same number of unspent deposits.; + break; + case 'aggregation_not_possible': + error = <>cannot aggregate the unspent deposits. the transaction value is too small; + break; + default: + error = <>your transaction could not be sent: + ({api_message?.error?.error || api_message?.error || api_message || 'undefined behaviour'}); + break; + } + } + else if (typeof (api_message) === 'string') { + const match = /unexpected generic api error: \((?.*)\)/.exec(api_message); + error = `your transaction could not be sent: (${match.groups.message})`; + } + + this.setState({ + error: error + }); + } + + + render() { + return ( + <>{this.state.error} + ); + } +} + + +export default withRouter(UserInterfaceError); + diff --git a/src/js/helper/text.js b/src/js/helper/text.js index 5f9b43fa..1faa567e 100644 --- a/src/js/helper/text.js +++ b/src/js/helper/text.js @@ -1,59 +1,5 @@ import React from 'react'; -import HelpIconView from '../components/utils/help-icon-view'; -import * as format from './format'; export function get_confirmation_modal_question() { return
are you sure you want to proceed?
; } - -export function get_ui_error(api_message) { - let error = ''; - let api_error_name = 'unknown'; - - if (typeof (api_message) === 'object') { - let result_error = api_message.error; - if (typeof (result_error?.error) !== 'undefined') { - api_error_name = result_error.error; - } - else { - api_error_name = result_error; - } - - switch (api_error_name) { - case 'transaction_input_max_error': - error = <> - your transaction tried to use too many outputs please try to send a smaller - amount or click this.props.history.push('/actions')}>here to use the balance aggregation tool to - optimize your balance. the max amount you can send is {format.millix(api_message.data.amount_max)}.; - break; - case 'insufficient_balance': - error = <>your balance is lower than the amount you are trying to send. the max amount you can send - is {format.millix(api_message.data.balance_stable)}.; - break; - case 'transaction_send_interrupt': - error = <>transaction has been canceled.; - break; - case 'proxy_not_found': - error = <>proxy not found. please try again.; - break; - case 'transaction_proxy_rejected': - error = <>transaction rejected by a proxy. please try again.; - break; - case 'aggregation_not_required': - error = <>cannot process the request. the aggregation will generate on the same number of unspent deposits.; - break; - case 'aggregation_not_possible': - error = <>cannot aggregate the unspent deposits. the transaction value is too small; - break; - default: - error = <>your transaction could not be sent: ({api_message?.error?.error || api_message?.error || api_message || 'undefined behaviour'}); - break; - } - } - else if (typeof (api_message) === 'string') { - const match = /unexpected generic api error: \((?.*)\)/.exec(api_message); - error = `your transaction could not be sent: (${match.groups.message})`; - } - - return error; -} From 60bc7560a90fecd53960e39aedf69b9bcb97d6b8 Mon Sep 17 00:00:00 2001 From: antonshulpekov <98457590+antonshulpekov@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:31:52 +0300 Subject: [PATCH 14/99] MILLIX-194: message ux improvement from 2022-06-02 make the date field searchable in the inbox / sent data table can't collapse the message parent item in left navigation add the view tx button to the message page. next to reply button on top Co-authored-by: AShulpekov Co-authored-by: crank --- src/css/form.scss | 1 + .../config/config-connection-view.jsx | 6 +- .../components/message/message-inbox-view.jsx | 6 +- .../components/message/message-sent-view.jsx | 6 +- src/js/components/message/message-view.jsx | 14 +- src/js/components/sidebar.jsx | 156 ++++++++++-------- src/js/helper/message.js | 1 + 7 files changed, 120 insertions(+), 70 deletions(-) diff --git a/src/css/form.scss b/src/css/form.scss index ccf5ca83..1c9e119a 100644 --- a/src/css/form.scss +++ b/src/css/form.scss @@ -96,6 +96,7 @@ label { } } +.btn-xs:not(.icon_only), .btn:not(.icon_only) { > .svg-inline--fa { margin-right: 0.3em; diff --git a/src/js/components/config/config-connection-view.jsx b/src/js/components/config/config-connection-view.jsx index d40fb081..810e29a0 100644 --- a/src/js/components/config/config-connection-view.jsx +++ b/src/js/components/config/config-connection-view.jsx @@ -8,6 +8,7 @@ import ErrorList from '../utils/error-list-view'; import * as validate from '../../helper/validate'; import API from '../../api'; import DatatableActionButtonView from '../utils/datatable-action-button-view'; +import _ from 'lodash'; class ConfigConnectionView extends Component { @@ -77,7 +78,10 @@ class ConfigConnectionView extends Component { } updateData(connection_data, element) { - JSON.parse(element.value).forEach(el => { + if(!_.isArray(element.value)){ + element.value = JSON.parse(element.value); + } + element.value.forEach(el => { connection_data[element.config_name].push({ node_id: el, action : ( +
diff --git a/src/js/components/sidebar.jsx b/src/js/components/sidebar.jsx index befdbd47..f797120e 100644 --- a/src/js/components/sidebar.jsx +++ b/src/js/components/sidebar.jsx @@ -21,7 +21,8 @@ class Sidebar extends Component { modalShow : false, node_millix_version : '', node_millix_version_available: '', - application : '' + application : '', + ignore_is_expanded : false }; this.setVersion = this.setVersion.bind(this); @@ -90,68 +91,71 @@ class Sidebar extends Component { isExpanded(section, defaultSelected) { let result = false; - if (section === 'transaction' && - ( - (defaultSelected === '/unspent-transaction-output-list/pending') || - (defaultSelected === '/unspent-transaction-output-list/stable') - ) - ) { - result = true; - } - else if (section === 'status' && - ( - (defaultSelected === '/status-summary') || - (defaultSelected === '/peers') || - (defaultSelected === '/backlog') - ) - ) { - result = true; - } - else if (section === 'advertisement' && - ( - (defaultSelected === '/advertisement-list') || - (defaultSelected === '/advertisement-received-list') - ) - ) { - result = true; - } - else if (section === 'config' && - ( - (defaultSelected === '/config/general') || - (defaultSelected === '/config/network') || - (defaultSelected === '/config/connection') || - (defaultSelected === '/config/consensus') || - (defaultSelected === '/config/address-version') || - (defaultSelected === '/config/config-storage') - ) - ) { - result = true; - } - else if (section === 'ads' && - ( - (defaultSelected === '/ad-create') || - (defaultSelected === '/ad-list') - ) - ) { - result = true; - } - else if (section === 'help' && - ( - (defaultSelected === '/faq') || - (defaultSelected === '/report-issue') || - (defaultSelected === '/system-info') - ) - ) { - result = true; - } - else if (section === 'message' && - ( - (defaultSelected === '/message-compose') || - (defaultSelected === '/message-sent') || - (defaultSelected === '/message-inbox') - ) - ) { - result = true; + if (!this.state.ignore_is_expanded) { + if (section === 'transaction' && + ( + (defaultSelected === '/transaction-list') || + (defaultSelected === '/unspent-transaction-output-list/pending') || + (defaultSelected === '/unspent-transaction-output-list/stable') + ) + ) { + result = true; + } + else if (section === 'status' && + ( + (defaultSelected === '/status-summary') || + (defaultSelected === '/peers') || + (defaultSelected === '/backlog') + ) + ) { + result = true; + } + else if (section === 'advertisement' && + ( + (defaultSelected === '/advertisement-list') || + (defaultSelected === '/advertisement-received-list') + ) + ) { + result = true; + } + else if (section === 'config' && + ( + (defaultSelected === '/config/general') || + (defaultSelected === '/config/network') || + (defaultSelected === '/config/connection') || + (defaultSelected === '/config/consensus') || + (defaultSelected === '/config/address-version') || + (defaultSelected === '/config/config-storage') + ) + ) { + result = true; + } + else if (section === 'ads' && + ( + (defaultSelected === '/ad-create') || + (defaultSelected === '/ad-list') + ) + ) { + result = true; + } + else if (section === 'help' && + ( + (defaultSelected === '/faq') || + (defaultSelected === '/report-issue') || + (defaultSelected === '/system-info') + ) + ) { + result = true; + } + else if (section === 'message' && + ( + (defaultSelected === '/message-compose') || + (defaultSelected === '/message-sent') || + (defaultSelected === '/message-inbox') + ) + ) { + result = true; + } } return result; @@ -165,7 +169,7 @@ class Sidebar extends Component { lockWallet() { changeLoaderState(true); - this.props.lockWallet().then(data => { + this.props.lockWallet().then(() => { changeLoaderState(false); }); } @@ -179,6 +183,14 @@ class Sidebar extends Component { return message_count_badge; } + toggleParentNavigationItem(navigation_id, defaultSelected) { + if (this.isExpanded(navigation_id, defaultSelected)) { + this.setState({ + ignore_is_expanded: true + }); + } + } + render() { let props = this.props; let defaultSelected = this.highlightSelected(props.location.pathname); @@ -238,6 +250,8 @@ class Sidebar extends Component { this.toggleParentNavigationItem('transaction', defaultSelected)} > transactions