From 9cd2f829fb44b0ea6792f44fee9f84dbe9573e3d Mon Sep 17 00:00:00 2001 From: alwx Date: Mon, 7 Aug 2017 23:29:54 +0200 Subject: [PATCH] API commands scopes, changes for API semantics (#1546) --- ios/Podfile.lock | 2 +- ios/StatusIm.xcodeproj/project.pbxproj | 4 +- resources/default_contacts.json | 27 +- resources/js/bots/browse/bot.js | 10 +- resources/js/bots/console/bot.js | 30 +- resources/js/bots/mailman/bot.js | 6 + .../bots/{wallet => transactor_group}/bot.js | 18 +- .../translations.js | 0 resources/js/bots/transactor_personal/bot.js | 548 ++++++++++++++++++ .../bots/transactor_personal/translations.js | 448 ++++++++++++++ resources/js/status.js | 28 +- src/status_im/bots/constants.cljs | 2 - src/status_im/chat/events/commands.cljs | 81 +-- src/status_im/chat/events/input.cljs | 146 ++--- src/status_im/chat/handlers.cljs | 16 +- src/status_im/chat/handlers/send_message.cljs | 48 +- .../chat/handlers/webview_bridge.cljs | 1 - src/status_im/chat/models/commands.cljs | 116 ++++ src/status_im/chat/models/input.cljs | 56 +- src/status_im/chat/models/suggestions.cljs | 43 -- src/status_im/chat/screen.cljs | 4 +- src/status_im/chat/subs.cljs | 19 +- src/status_im/chat/utils.cljs | 23 +- src/status_im/chat/views/input/input.cljs | 7 +- .../chat/views/input/suggestions.cljs | 18 +- src/status_im/chat/views/message/message.cljs | 14 +- .../chat/views/message/request_message.cljs | 5 +- src/status_im/commands/handlers/jail.cljs | 1 - src/status_im/commands/handlers/loading.cljs | 137 ++--- src/status_im/data_store/messages.cljs | 7 +- .../realm/schemas/account/core.cljs | 4 + .../realm/schemas/account/v12/contact.cljs | 47 ++ .../realm/schemas/account/v15/contact.cljs | 46 ++ .../realm/schemas/account/v15/core.cljs | 39 ++ src/status_im/debug/handlers.cljs | 6 +- src/status_im/models/commands.cljs | 14 - src/status_im/native_module/impl/module.cljs | 5 +- src/status_im/protocol/handlers.cljs | 26 +- src/status_im/ui/screens/contacts/db.cljs | 39 +- src/status_im/ui/screens/contacts/events.cljs | 30 +- src/status_im/ui/screens/contacts/subs.cljs | 5 +- src/status_im/utils/js_resources.cljs | 15 +- 42 files changed, 1684 insertions(+), 457 deletions(-) rename resources/js/bots/{wallet => transactor_group}/bot.js (97%) rename resources/js/bots/{wallet => transactor_group}/translations.js (100%) create mode 100644 resources/js/bots/transactor_personal/bot.js create mode 100644 resources/js/bots/transactor_personal/translations.js create mode 100644 src/status_im/chat/models/commands.cljs delete mode 100644 src/status_im/chat/models/suggestions.cljs create mode 100644 src/status_im/data_store/realm/schemas/account/v12/contact.cljs create mode 100644 src/status_im/data_store/realm/schemas/account/v15/contact.cljs create mode 100644 src/status_im/data_store/realm/schemas/account/v15/core.cljs delete mode 100644 src/status_im/models/commands.cljs diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5e10e65d543..74f3acf1ee8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -44,4 +44,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: d05403e3fce258b6985bd3b614ffe02a0e01b9f7 -COCOAPODS: 1.3.1 +COCOAPODS: 1.2.1 diff --git a/ios/StatusIm.xcodeproj/project.pbxproj b/ios/StatusIm.xcodeproj/project.pbxproj index 5a3b1474f87..bb88a841301 100644 --- a/ios/StatusIm.xcodeproj/project.pbxproj +++ b/ios/StatusIm.xcodeproj/project.pbxproj @@ -1752,7 +1752,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 49A69B853BD9751F84878340 /* [CP] Check Pods Manifest.lock */ = { @@ -1770,7 +1770,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 967469AF8BA27D5CEC47B13C /* [CP] Copy Pods Resources */ = { diff --git a/resources/default_contacts.json b/resources/default_contacts.json index 34d21720477..7f185c1ec04 100644 --- a/resources/default_contacts.json +++ b/resources/default_contacts.json @@ -23,6 +23,7 @@ }, "unremovable?": true }, + "wallet": { "name": @@ -36,12 +37,10 @@ "add-chat?": true, "dapp?": true, "groups": ["dapps"], - "has-global-command?": true, "dapp-url": { "en": "https://status.im/dapps/wallet/" }, - "bot-url": "local://wallet-bot", "unremovable?": true }, @@ -53,10 +52,32 @@ "ru": "Печкин" }, "dapp?": true, - "has-global-command?": true, + "mixable?": true, "bot-url": "local://mailman-bot" }, + "transactor-group": + { + "name": + { + "en": "Transactor" + }, + "dapp?": true, + "mixable?": true, + "bot-url": "local://transactor-group-bot" + }, + + "transactor-personal": + { + "name": + { + "en": "Transactor" + }, + "dapp?": true, + "mixable?": true, + "bot-url": "local://transactor-personal-bot" + }, + "demo-bot": { "name": diff --git a/resources/js/bots/browse/bot.js b/resources/js/bots/browse/bot.js index 52174e2bf02..e26195aa569 100644 --- a/resources/js/bots/browse/bot.js +++ b/resources/js/bots/browse/bot.js @@ -34,9 +34,15 @@ function browse(params, context) { } status.command({ - name: "global", + name: "browse", title: I18n.t('browse_title'), - registeredOnly: true, + scope: { + isGlobal: true, + registeredOnly: true, + personalChats: true, + groupChats: true, + canUseForDApps: true + }, description: I18n.t('browse_description'), color: "#ffa500", fullscreen: true, diff --git a/resources/js/bots/console/bot.js b/resources/js/bots/console/bot.js index 34d55d097ec..e0f43289581 100644 --- a/resources/js/bots/console/bot.js +++ b/resources/js/bots/console/bot.js @@ -455,7 +455,6 @@ function phoneSuggestions(params, context) { var phoneConfig = { name: "phone", - registeredOnly: true, icon: "phone_white", color: "#5bb2a2", title: I18n.t('phone_title'), @@ -491,7 +490,6 @@ var phoneConfig = { } }; status.response(phoneConfig); -status.command(phoneConfig); var faucets = [ /*{ @@ -540,7 +538,13 @@ status.command({ title: I18n.t('faucet_title'), description: I18n.t('faucet_description'), color: "#7099e6", - registeredOnly: true, + scope: { + isGlobal: false, + registeredOnly: true, + personalChats: true, + groupChats: true, + canUseForDApps: true + }, params: [{ name: "url", type: status.types.TEXT, @@ -611,7 +615,13 @@ status.command({ title: I18n.t('debug_mode_title'), description: I18n.t('debug_mode_description'), color: "#7099e6", - registeredOnly: true, + scope: { + isGlobal: false, + registeredOnly: true, + personalChats: true, + groupChats: true, + canUseForDApps: true + }, params: [{ name: "mode", suggestions: debugSuggestions, @@ -635,18 +645,6 @@ status.command({ } }); - -// status.command({ -// name: "help", -// title: "Help", -// description: "Request help from Console", -// color: "#7099e6", -// params: [{ -// name: "query", -// type: status.types.TEXT -// }] -// }); - status.response({ name: "confirmation-code", color: "#7099e6", diff --git a/resources/js/bots/mailman/bot.js b/resources/js/bots/mailman/bot.js index ae579d773f3..372dfe94f10 100644 --- a/resources/js/bots/mailman/bot.js +++ b/resources/js/bots/mailman/bot.js @@ -32,6 +32,12 @@ function locationsSuggestions (params) { status.command({ name: "location", title: I18n.t('location_title'), + scope: { + registeredOnly: true, + personalChats: true, + groupChats: true, + canUseForDApps: false + }, description: I18n.t('location_description'), sequentialParams: true, hideSendButton: true, diff --git a/resources/js/bots/wallet/bot.js b/resources/js/bots/transactor_group/bot.js similarity index 97% rename from resources/js/bots/wallet/bot.js rename to resources/js/bots/transactor_group/bot.js index f6d32279ff6..71a549ca4e8 100644 --- a/resources/js/bots/wallet/bot.js +++ b/resources/js/bots/transactor_group/bot.js @@ -269,7 +269,11 @@ function validateSend(params, context) { params["bot-db"] = {}; } - if (!params["bot-db"]["public"] || !params["bot-db"]["public"]["recipient"] || !params["bot-db"]["public"]["recipient"]["address"]) { + console.log(context); + + if (!params["bot-db"]["public"] + || !params["bot-db"]["public"]["recipient"] + || !params["bot-db"]["public"]["recipient"]["address"]) { return { markup: status.components.validationMessage( "Wrong address", @@ -476,6 +480,12 @@ function shortPreviewSend(params, context) { var send = { name: "send", + scope: { + isGlobal: false, + personalChats: false, + groupChats: true, + canUseForDApps: false + }, icon: "money_white", color: "#5fc48d", title: I18n.t('send_title'), @@ -510,6 +520,12 @@ var paramsRequest = [ status.command({ name: "request", + scope: { + isGlobal: false, + personalChats: false, + groupChats: true, + canUseForDApps: false + }, color: "#5fc48d", title: I18n.t('request_title'), description: I18n.t('request_description'), diff --git a/resources/js/bots/wallet/translations.js b/resources/js/bots/transactor_group/translations.js similarity index 100% rename from resources/js/bots/wallet/translations.js rename to resources/js/bots/transactor_group/translations.js diff --git a/resources/js/bots/transactor_personal/bot.js b/resources/js/bots/transactor_personal/bot.js new file mode 100644 index 00000000000..d39a49fe748 --- /dev/null +++ b/resources/js/bots/transactor_personal/bot.js @@ -0,0 +1,548 @@ +function calculateFee(n, tx) { + var estimatedGas = 21000; + if (tx !== null) { + estimatedGas = web3.eth.estimateGas(tx); + } + + var gasMultiplicator = Math.pow(1.4, n).toFixed(3); + var weiFee = web3.eth.gasPrice * gasMultiplicator * estimatedGas; + // force fee in eth to be of BigNumber type + var ethFee = web3.toBigNumber(web3.fromWei(weiFee, "ether")); + // always display 7 decimal places + return ethFee.toFixed(7); +} + +function calculateGasPrice(n) { + var gasMultiplicator = Math.pow(1.4, n).toFixed(3); + return web3.eth.gasPrice * gasMultiplicator; +} + +status.defineSubscription( + "calculatedFee", + {value: ["sliderValue"], tx: ["transaction"]}, + function (params) { + return calculateFee(params.value, params.tx); + } +); + +function getFeeExplanation(n) { + return I18n.t('send_explanation') + I18n.t('send_explanation_' + (n + 2)); +} + +status.defineSubscription( + "feeExplanation", + {value: ["sliderValue"]}, + function(params) { + return getFeeExplanation(params.value); + } +) + +function amountParameterBox(params, context) { + if (!params["bot-db"]) { + params["bot-db"] = {}; + } + + var contactAddress = context.to; + + var txData; + var amount; + try { + amount = params.args[0]; + txData = { + to: contactAddress, + value: web3.toWei(amount) || 0 + }; + } catch (err) { + amount = null; + txData = { + to: contactAddress, + value: 0 + }; + } + + var sliderValue = params["bot-db"]["sliderValue"] || 0; + + status.setDefaultDb({ + transaction: txData, + calculatedFee: calculateFee(sliderValue, txData), + feeExplanation: getFeeExplanation(sliderValue), + sliderValue: sliderValue + }); + + return { + title: I18n.t('send_title'), + showBack: true, + markup: status.components.scrollView( + { + keyboardShouldPersistTaps: "always" + }, + [status.components.view( + { + flex: 1 + }, + [ + status.components.text( + { + style: { + fontSize: 14, + color: "rgb(147, 155, 161)", + paddingTop: 12, + paddingLeft: 16, + paddingRight: 16, + paddingBottom: 20 + } + }, + I18n.t('send_specify_amount') + ), + status.components.touchable( + { + onPress: status.components.dispatch([status.events.FOCUS_INPUT, []]) + }, + status.components.view( + { + flexDirection: "row", + alignItems: "center", + textAlign: "center", + justifyContent: "center" + }, + [ + status.components.text( + { + font: "light", + numberOfLines: 1, + ellipsizeMode: "tail", + style: { + maxWidth: 250, + fontSize: 38, + marginLeft: 8, + color: "black" + } + }, + amount || "0.00" + ), + status.components.text( + { + font: "light", + style: { + fontSize: 38, + marginLeft: 8, + color: "rgb(147, 155, 161)" + } + }, + I18n.t('eth') + ), + ] + ) + ), + status.components.text( + { + style: { + fontSize: 14, + color: "rgb(147, 155, 161)", + paddingTop: 14, + paddingLeft: 16, + paddingRight: 16, + paddingBottom: 5 + } + }, + I18n.t('send_fee') + ), + status.components.view( + { + flexDirection: "row" + }, + [ + status.components.text( + { + style: { + fontSize: 17, + color: "black", + paddingLeft: 16 + } + }, + [status.components.subscribe(["calculatedFee"])] + ), + status.components.text( + { + style: { + fontSize: 17, + color: "rgb(147, 155, 161)", + paddingLeft: 4, + paddingRight: 4 + } + }, + I18n.t('eth') + ) + ] + ), + status.components.slider( + { + maximumValue: 2, + minimumValue: -2, + onSlidingComplete: status.components.dispatch( + [status.events.UPDATE_DB, "sliderValue"] + ), + step: 1, + style: { + marginLeft: 16, + marginRight: 16 + } + } + ), + status.components.view( + { + flexDirection: "row" + }, + [ + status.components.text( + { + style: { + flex: 1, + fontSize: 14, + color: "rgb(147, 155, 161)", + paddingLeft: 16, + alignSelf: "flex-start" + } + }, + I18n.t('send_cheaper') + ), + status.components.text( + { + style: { + flex: 1, + fontSize: 14, + color: "rgb(147, 155, 161)", + paddingRight: 16, + alignSelf: "flex-end", + textAlign: "right" + } + }, + I18n.t('send_faster') + ) + ] + ), + status.components.text( + { + style: { + fontSize: 14, + color: "black", + paddingTop: 16, + paddingLeft: 16, + paddingRight: 16, + paddingBottom: 16, + lineHeight: 24 + } + }, + [status.components.subscribe(["feeExplanation"])] + ) + ] + )] + ) + }; +} + +var paramsSend = [ + { + name: "amount", + type: status.types.NUMBER, + suggestions: amountParameterBox + } +]; + +function validateSend(params, context) { + if (!params["bot-db"]) { + params["bot-db"] = {}; + } + + if (!params["amount"]) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_amount_specified') + ) + }; + } + + var amount = params["amount"].replace(",", "."); + var amountSplitted = amount.split("."); + if (amountSplitted.length === 2 && amountSplitted[1].length > 18) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_amount_is_too_small') + ) + }; + } + + if (isNaN(parseFloat(params.amount.replace(",", ".")))) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_invalid_number') + ) + }; + } + + try { + var val = web3.toWei(amount, "ether"); + if (val <= 0) { + throw new Error(); + } + } catch (err) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_invalid_number') + ) + }; + } + + var balance = web3.eth.getBalance(context.from); + var fee = calculateFee( + params["bot-db"]["sliderValue"], + { + to: context.to, + value: val + } + ); + + if (bn(val).plus(bn(web3.toWei(fee, "ether"))).greaterThan(bn(balance))) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_insufficient_amount') + + web3.fromWei(balance, "ether") + + " ETH)" + ) + }; + } +} + +function handleSend(params, context) { + var val = web3.toWei(params["amount"].replace(",", "."), "ether"); + + var data = { + from: context.from, + to: context.to, + value: val, + gasPrice: calculateGasPrice(params["bot-db"]["sliderValue"]) + }; + + try { + return web3.eth.sendTransaction(data); + } catch (err) { + return {error: err.message}; + } +} + +function previewSend(params, context) { + var amountStyle = { + fontSize: 36, + color: "#000000", + height: 40 + }; + + var amount = status.components.view( + { + flexDirection: "column", + alignItems: "flex-end", + maxWidth: 250 + }, + [status.components.text( + { + style: amountStyle, + numberOfLines: 1, + ellipsizeMode: "tail", + font: "light" + }, + status.localizeNumber(params.amount, context.delimiter, context.separator) + )]); + + var currency = status.components.view( + { + style: { + flexDirection: "column", + justifyContent: "flex-end", + paddingBottom: 0 + } + }, + [status.components.text( + { + style: { + color: "#9199a0", + fontSize: 16, + lineHeight: 18, + marginLeft: 7.5 + } + }, + I18n.t('eth') + )] + ); + + var row = status.components.view( + { + style: { + flexDirection: "row", + justifyContent: "space-between", + marginTop: 8, + marginBottom: 8 + } + }, + [amount, currency] + ); + + return { + markup: status.components.view( + { + style: { + flexDirection: "column" + } + }, + [row] + ) + }; +} + +function shortPreviewSend(params, context) { + return { + markup: status.components.text( + {}, + I18n.t('send_title') + ": " + + status.localizeNumber(params.amount, context.delimiter, context.separator) + + " ETH" + ) + }; +} + +var send = { + name: "send", + scope: { + isGlobal: false, + personalChats: true, + groupChats: false, + canUseForDApps: false + }, + icon: "money_white", + color: "#5fc48d", + title: I18n.t('send_title'), + description: I18n.t('send_description'), + params: paramsSend, + validator: validateSend, + handler: handleSend, + preview: previewSend, + shortPreview: shortPreviewSend +}; + +status.command(send); +status.response(send); + +var paramsRequest = [ + { + name: "amount", + type: status.types.NUMBER + } +]; + +status.command({ + name: "request", + scope: { + isGlobal: false, + personalChats: true, + groupChats: false, + canUseForDApps: false + }, + color: "#5fc48d", + title: I18n.t('request_title'), + description: I18n.t('request_description'), + params: paramsRequest, + handler: function (params, context) { + var val = params["amount"].replace(",", "."); + + return { + event: "request", + request: { + command: "send", + params: { + amount: val + }, + prefill: [val] + } + }; + }, + preview: function (params, context) { + var row = status.components.text( + {}, + I18n.t('request_requesting') + " " + + status.localizeNumber(params.amount, context.delimiter, context.separator) + + " ETH" + ); + return { + markup: status.components.view( + { + style: { + flexDirection: "column" + } + }, + [row] + ) + }; + }, + shortPreview: function (params, context) { + return { + markup: status.components.text( + {}, + I18n.t('request_requesting') + " " + + status.localizeNumber(params.amount, context.delimiter, context.separator) + + " ETH" + ) + }; + }, + validator: function (params) { + if (!params["bot-db"]) { + params["bot-db"] = {}; + } + + if (!params["amount"]) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_amount_specified') + ) + }; + } + + var amount = params.amount.replace(",", "."); + var amountSplitted = amount.split("."); + if (amountSplitted.length === 2 && amountSplitted[1].length > 18) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_amount_is_too_small') + ) + }; + } + + if (isNaN(parseFloat(params.amount.replace(",", ".")))) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_invalid_number') + ) + }; + } + + try { + var val = web3.toWei(amount, "ether"); + if (val <= 0) { + throw new Error(); + } + } catch (err) { + return { + markup: status.components.validationMessage( + I18n.t('validation_title'), + I18n.t('validation_invalid_number') + ) + }; + } + } +}); diff --git a/resources/js/bots/transactor_personal/translations.js b/resources/js/bots/transactor_personal/translations.js new file mode 100644 index 00000000000..4e72ce27fb3 --- /dev/null +++ b/resources/js/bots/transactor_personal/translations.js @@ -0,0 +1,448 @@ +I18n.translations = { + en: { + send_title: 'Send transaction', + send_description: 'Send a payment', + send_choose_recipient: 'Choose recipient', + send_specify_amount: 'Specify amount', + send_fee: 'Fee', + send_cheaper: 'Cheaper', + send_faster: 'Faster', + send_explanation: 'This is the most amount of money that might be used to process this transaction. Your transaction will be mined ', + send_explanation_0: 'in a few minutes or more.', + send_explanation_1: 'likely within a few minutes.', + send_explanation_2: 'usually within a minute.', + send_explanation_3: 'probably within 30 seconds.', + send_explanation_4: 'probably within a few seconds.', + send_sending_to: 'to ', + + eth: 'ETH', + + request_title: 'Request ETH', + request_description: 'Request a payment', + request_requesting: 'Requesting ', + request_requesting_from: 'from ', + + validation_title: 'Amount', + validation_amount_specified: 'Amount must be specified', + validation_invalid_number: 'Amount is not valid number', + validation_amount_is_too_small: 'Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)', + validation_insufficient_amount: 'Insufficient funds for gas * price + value (balance ' + }, + ru: { + send_title: 'Отправить транзакцию', + send_description: 'Отправить платеж', + + request_title: 'Запросить ETH', + request_description: 'Запросить платеж', + request_requesting: 'Запрос ', + + validation_title: 'Сумма', + validation_amount_specified: 'Необходимо указать сумму', + validation_invalid_number: 'Сумма не является действительным числом', + validation_amount_is_too_small: 'Сумма излишне точная. Минимальная единица, которую можно отправить — 1 Wei (1x10^-18 ETH)', + validation_insufficient_amount: 'Недостаточно ETH на балансе (' + }, + af: { + send_title: 'Stuur ETH', + send_description: 'Stuur \'n betaling', + + request_title: 'Versoek ETH', + request_description: 'Versoek \'n betaling', + request_requesting: 'Besig met versoek ', + + validation_title: 'Bedrag', + validation_amount_specified: 'Bedrag moet gespesifiseer word', + validation_invalid_number: 'Bedrag is nie \'n geldige syfer nie', + validation_insufficient_amount: 'Nie genoeg ETH in rekening nie (' + }, + ar: { + send_title: 'إرسال ETH', + send_description: 'إرسال مدفوعات', + + request_title: 'طلب ETH', + request_description: 'طلب مدفوعات', + request_requesting: 'مُطَالَبَة ', + + validation_title: 'المبلغ', + validation_amount_specified: 'يجب تحديد المبلغ', + validation_invalid_number: 'المبلغ المحدد غير صحيح', + validation_insufficient_amount: 'لا يوجد ETH كافي بالحساب (' + }, + 'zh-hant': { + send_title: '發送 ETH', + send_description: '發送一筆付款', + + request_title: '請求 ETH', + request_description: '請求一筆付款', + request_requesting: '正在請求 ', + + validation_title: '金額', + validation_amount_specified: 'ي未指定金額', + validation_invalid_number: '金額數字無效', + validation_insufficient_amount: '餘額中 ETH 不足 (' + }, + 'zh-hans': { + send_title: '发送ETH', + send_description: '付款', + + request_title: '请求ETH', + request_description: '要求付款', + request_requesting: '正在请求 ', + + validation_title: '金額', + validation_amount_specified: '必须指定金额', + validation_invalid_number: '金额不是有效数字', + validation_insufficient_amount: 'ETH余额不足 (' + }, + 'zh-yue': { + send_title: '發送ETH', + send_description: '發送付款', + + request_title: '徵求ETH', + request_description: '徵求付款', + request_requesting: '徵求中 ', + + validation_title: '金額', + validation_amount_specified: '必須指定金額', + validation_invalid_number: '指定金額並非有效數字', + validation_insufficient_amount: '沒有足夠ETH餘額 (' + }, + 'zh-wuu': { + send_title: '发送ETH', + send_description: '发送付款', + + request_title: '请求ETH', + request_description: '请求付款', + request_requesting: '请求中 ', + + validation_title: '金额', + validation_amount_specified: '金额必须明确', + validation_invalid_number: '金额不是一个有效数字', + validation_insufficient_amount: 'ETH余额不足 (' + }, + nl: { + send_title: 'Stuur ETH', + send_description: 'Stuur een betaling', + + request_title: 'Vraag ETH aan', + request_description: 'Vraag om een betaling', + request_requesting: 'Wordt aangevraagd ', + + validation_title: 'Bedrag', + validation_amount_specified: 'Bedrag moet worden opgegeven', + validation_invalid_number: 'Bedrag is geen geldig nummer', + validation_insufficient_amount: 'Niet genoeg ETH op saldo (' + }, + fr: { + send_title: 'Envoyer l\'ETH', + send_description: 'Envoyer un paiement', + + request_title: 'Demander l\'ETH', + request_description: 'Demander un paiement', + request_requesting: 'Demande en cours: ', + + validation_title: 'Montant', + validation_amount_specified: 'Le montant doit être spécifié', + validation_invalid_number: 'Le montant n\'est pas un nombre valide', + validation_insufficient_amount: 'Pas assez d\'ETH sur le solde (' + }, + de: { + send_title: 'ETH abschicken', + send_description: 'Zahlung senden', + + request_title: 'ETH anfragen', + request_description: 'Zahlung anfragen', + request_requesting: 'Anfrage: ', + + validation_title: 'Betrag', + validation_amount_specified: 'Betrag muss angegeben werden', + validation_invalid_number: 'Betrag ist keine gültige Zahl', + validation_insufficient_amount: 'Nicht genügend ETH auf dem Konto (' + }, + hi: { + send_title: 'ETH भेजें', + send_description: 'भुगतान भेजें', + + request_title: 'ETH का अनुरोध करें', + request_description: 'भुगतान का अनुरोध करें', + request_requesting: 'अनुरोध किया जा रहा है ', + + validation_title: 'राशि', + validation_amount_specified: 'राशि निर्दिष्ट की जानी चाहिए', + validation_invalid_number: 'राशि वैध संख्या नहीं है', + validation_insufficient_amount: 'बैलेंस पर पर्याप्त ETH नहीं है (' + }, + hu: { + send_title: 'ETH küldése', + send_description: 'Kifizetés küldése', + + request_title: 'ETH igénylése', + request_description: 'Fizetés igénylése', + request_requesting: 'Igénylés ', + + validation_title: 'Összeg', + validation_amount_specified: 'Az összeget meg kell határozni', + validation_invalid_number: 'Az összeg nem egy elfogadott szám', + validation_insufficient_amount: 'Nincs elég ETH a számlán (' + }, + it: { + send_title: 'Invia ETH', + send_description: 'Invia un pagamento', + + request_title: 'Richiedi ETH', + request_description: 'Richiedi un pagamento', + request_requesting: 'Richiesta in corso: ', + + validation_title: 'Ammontare', + validation_amount_specified: 'L\'ammontare deve essere specificato', + validation_invalid_number: 'L\'ammontare non è un numero valido', + validation_insufficient_amount: 'ETH insufficiente sul bilancio (' + }, + ja: { + send_title: 'ETHを送信', + send_description: '支払いを送信', + + request_title: 'ETHをリクエスト', + request_description: '支払いをリクエスト', + request_requesting: 'リクエスト中 ', + + validation_title: '金額', + validation_amount_specified: '金額を特定する必要があります', + validation_invalid_number: '金額は有効な数字ではありません', + validation_insufficient_amount: '残高に十分なETHがありません(' + }, + ko: { + send_title: 'ETH 보내기', + send_description: '지불금 보내기', + + request_title: 'ETH 요청', + request_description: '지불금 요청', + request_requesting: '요청 중 ', + + validation_title: '금액', + validation_amount_specified: '금액을 지정해야 합니다', + validation_invalid_number: '금액이 유효한 숫자가 아닙니다', + validation_insufficient_amount: 'ETH 잔고가 부족합니다 (' + }, + pl: { + send_title: 'Wyślij ETH', + send_description: 'Wyślij płatność', + + request_title: 'Poproś o ETH', + request_description: 'Poproś o płatność', + request_requesting: 'Przesyłanie prośby ', + + validation_title: 'Kwota', + validation_amount_specified: 'Należy określić kwotę', + validation_invalid_number: 'Kwota nie jest prawidłową liczbą', + validation_insufficient_amount: 'Brak wystarczającej liczby ETH na koncie (' + }, + 'pt-br': { + send_title: 'Enviar ETH', + send_description: 'Enviar um pagamento', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar um pagamento', + request_requesting: 'Solicitando ', + + validation_title: 'Quantia', + validation_amount_specified: 'É necessário especificar a quantia', + validation_invalid_number: 'A quantia não é um número válido', + validation_insufficient_amount: 'ETH insuficiente no saldo (' + }, + 'pt-pt': { + send_title: 'Enviar ETH', + send_description: 'Enviar um pagamento', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar um pagamento', + request_requesting: 'A solicitar ', + + validation_title: 'Montante', + validation_amount_specified: 'O montante deve ser especificado', + validation_invalid_number: 'O montante não é um número válido', + validation_insufficient_amount: 'Não há ETH suficiente no saldo (' + }, + ro: { + send_title: 'Trimite ETH', + send_description: 'Trimite o plată', + + request_title: 'Solicită ETH', + request_description: 'Solicită o plată', + request_requesting: 'Se solicită ', + + validation_title: 'Sumă', + validation_amount_specified: 'Trebuie menționată o sumă', + validation_invalid_number: 'Suma nu are forma unui număr valid', + validation_insufficient_amount: 'Sold ETH insuficient (' + }, + sl: { + send_title: 'Pošlji ETH', + send_description: 'Pošlji plačilo', + + request_title: 'Zahtevaj ETH', + request_description: 'Zahtevaj plačilo', + request_requesting: 'Zahtevam ', + + validation_title: 'Vsota', + validation_amount_specified: 'Vsota mora biti izrecno navedena', + validation_invalid_number: 'Vsota ni veljavna številka', + validation_insufficient_amount: 'Stanje ETH na računu je prenizko (' + }, + es: { + send_title: 'Enviar ETH ', + send_description: 'Enviar un pago', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar un pago', + request_requesting: 'Solicitando ', + + validation_title: 'Cantidad', + validation_amount_specified: 'Hay que especificar la cantidad', + validation_invalid_number: 'La cantidad no es un número válido', + validation_insufficient_amount: 'No hay suficiente ETH en conjunto (' + }, + 'es-ar': { + send_title: 'Enviar ETH', + send_description: 'Enviar un pago', + + request_title: 'Solicitar ETH', + request_description: 'Solicitar un pago', + request_requesting: 'Solicitando ', + + validation_title: 'Monto', + validation_amount_specified: 'Debes especificar el monto', + validation_invalid_number: 'El monto no es un número válido', + validation_insufficient_amount: 'No tienes suficiente ETH en tu saldo (' + }, + sw: { + send_title: 'Tuma ETH', + send_description: 'Tuma malipo', + + request_title: 'Omba ETH', + request_description: 'Omba malipo', + request_requesting: 'Kuomba ', + + validation_title: 'Kiasi', + validation_amount_specified: 'Kiasi lazima kifafanuliwe', + validation_invalid_number: 'Kiasi si nambari halali', + validation_insufficient_amount: 'ETH haitoshi kwenye salio (' + }, + sv: { + send_title: 'Skicka ETH', + send_description: 'Skicka en betalning', + + request_title: 'Begär ETH', + request_description: 'Begär en betalning', + request_requesting: 'Begär ', + + validation_title: 'Belopp', + validation_amount_specified: 'Beloppet måste anges', + validation_invalid_number: 'Beloppet är inte ett giltigt nummer', + validation_insufficient_amount: 'Inte tillräcklig ETH på balansen (' + }, + 'fr-ch': { + send_title: 'Envoyer des ETH', + send_description: 'Envoyer un paiement', + + request_title: 'Demander des ETH', + request_description: 'Demander un paiement', + request_requesting: 'Demande ', + + validation_title: 'Montant', + validation_amount_specified: 'Le montant doit être spécifié', + validation_invalid_number: 'Le montant n\'est pas un nombre valable', + validation_insufficient_amount: 'Pas assez d\'ETH sur le solde (' + }, + 'de-ch': { + send_title: 'Sende ETH', + send_description: 'Sende eine Zahlung', + + request_title: 'Fordere ETH an', + request_description: 'Eine Zahlung anfordern', + request_requesting: 'Anfordern ', + + validation_title: 'Betrag', + validation_amount_specified: 'Der Betrag muss angegeben werden', + validation_invalid_number: 'Der Betrag ist nicht gültig', + validation_insufficient_amount: 'Nicht genug ETH vorhanden (' + }, + 'it-ch': { + send_title: 'Invia ETH', + send_description: 'Invia un pagamento', + + request_title: 'Richiedi ETH', + request_description: 'Richiedi un pagamento', + request_requesting: 'Richiesta in corso: ', + + validation_title: 'Importo', + validation_amount_specified: 'Specificare l\'importo', + validation_invalid_number: 'Importo inserito non valido', + validation_insufficient_amount: 'Saldo ETH non sufficiente (' + }, + th: { + send_title: 'ส่ง ETH', + send_description: 'ส่งการชำระเงิน', + + request_title: 'ร้องขอ ETH', + request_description: 'ร้องขอการชำระเงิน', + request_requesting: 'กำลังร้องขอ ', + + validation_title: 'จำนวน', + validation_amount_specified: 'จำเป็นต้องระบุจำนวน', + validation_invalid_number: 'จำนวนไม่ใช่หมายเลขที่ถูกต้อง', + validation_insufficient_amount: 'มี ETH ไม่เพียงพอในยอดคงเหลือ (' + }, + tr: { + send_title: 'ETH gönder', + send_description: 'Bir ödeme gönder', + + request_title: 'ETH iste', + request_description: 'Bir ödeme iste', + request_requesting: 'İsteniyor ', + + validation_title: 'Miktar', + validation_amount_specified: 'Miktar belirtilmelidir', + validation_invalid_number: 'Miktar geçerli bir sayı değil', + validation_insufficient_amount: 'Yeterli ETH bakiyesi yok (' + }, + uk: { + send_title: 'Надіслати ETH', + send_description: 'Надіслати платіж', + + request_title: 'Запит ETH', + request_description: 'Запит платежу', + request_requesting: 'Запит ', + + validation_title: 'Сума', + validation_amount_specified: 'Сума повинна бути вказана', + validation_invalid_number: 'Сума не дійсне число', + validation_insufficient_amount: 'Не вистачає ETH на балансі (' + }, + ur: { + send_title: 'ETH بھیجیں', + send_description: 'ادائیگی کریں', + + request_title: 'ETH کی درخواست دیں', + request_description: 'ادائیگی کی درخواست دیں', + request_requesting: 'درخواست کی جارہی ہے ', + + validation_title: 'رقم', + validation_amount_specified: 'رقم درج کی جانی چاہیے۔ ', + validation_invalid_number: 'رقیم درست ہندسے نہیں ہیں', + validation_insufficient_amount: 'ETH میں کافی بیلنس نہیں ہے (' + }, + vi: { + send_title: 'Gửi ETH', + send_description: 'Gửi một khoản thanh toán', + + request_title: 'Yêu cầu ETH', + request_description: 'Yêu cầu một khoản thanh toán', + request_requesting: 'Đang yêu cầu ', + + validation_title: 'Số tiền', + validation_amount_specified: 'Số tiền phải được xác định', + validation_invalid_number: 'Số tiền không phải là một số hợp lệ', + validation_insufficient_amount: 'Không đủ ETH trong số dư (' + } +}; diff --git a/resources/js/status.js b/resources/js/status.js index 5412a0f151f..f0eda279f51 100644 --- a/resources/js/status.js +++ b/resources/js/status.js @@ -6,13 +6,21 @@ var _status_catalog = { }, status = {}; +function transformScope(scope) { + return (scope["global?"] ? 1 : 0) | + (scope["registered-only?"] ? 2 : 0) | + (scope["personal-chats?"] ? 4 : 0) | + (scope["group-chats?"] ? 8 : 0) | + (scope["can-use-for-dapps?"] ? 16 : 0); +} + function Command() { } function Response() { } Command.prototype.addToCatalog = function () { - _status_catalog.commands[this.name] = this; + _status_catalog.commands[[this.name, this.scope.hash]] = this; }; Command.prototype.param = function (parameter) { @@ -26,9 +34,8 @@ Command.prototype.create = function (com) { this.title = com.title; this.description = com.description; this.handler = com.handler; - this["has-handler"] = com.handler != null; - this["async-handler"] = (com.handler != null) && com.asyncHandler - this["registered-only"] = com.registeredOnly; + this["has-handler"] = com.handler !== null; + this["async-handler"] = (com.handler != null) && com.asyncHandler; this.validator = com.validator; this.color = com.color; this.icon = com.icon; @@ -42,7 +49,16 @@ Command.prototype.create = function (com) { this["execute-immediately?"] = com.executeImmediately; this["sequential-params"] = com.sequentialParams; this["hide-send-button"] = com.hideSendButton; - + + // scopes + this["scope"] = {}; + this["scope"]["global?"] = com["scope"] != null && com["scope"]["isGlobal"] === true; + this["scope"]["registered-only?"] = com["scope"] != null && com["scope"]["registeredOnly"] === true; + this["scope"]["personal-chats?"] = com["scope"] == null || com["scope"]["personalChats"] === true; + this["scope"]["group-chats?"] = com["scope"] == null || com["scope"]["groupChats"] === true; + this["scope"]["can-use-for-dapps?"] = com["scope"] == null || com["scope"]["canUseForDApps"] === true; + this["scope"]["hash"] = transformScope(this["scope"]); + this.addToCatalog(); return this; @@ -51,7 +67,7 @@ Command.prototype.create = function (com) { Response.prototype = Object.create(Command.prototype); Response.prototype.addToCatalog = function () { - _status_catalog.responses[this.name] = this; + _status_catalog.responses[[this.name, 0]] = this; }; Response.prototype.onReceiveResponse = function (handler) { this.onReceive = handler; diff --git a/src/status_im/bots/constants.cljs b/src/status_im/bots/constants.cljs index 1743ba9d440..ddb70a494ee 100644 --- a/src/status_im/bots/constants.cljs +++ b/src/status_im/bots/constants.cljs @@ -3,5 +3,3 @@ (def mailman-bot "mailman") (defn mailman-bot? [bot-name] (= mailman-bot bot-name)) - -(def hidden-bots #{mailman-bot}) diff --git a/src/status_im/chat/events/commands.cljs b/src/status_im/chat/events/commands.cljs index 1282c428ed4..d59c91383db 100644 --- a/src/status_im/chat/events/commands.cljs +++ b/src/status_im/chat/events/commands.cljs @@ -3,23 +3,27 @@ [clojure.string :as str] [re-frame.core :refer [reg-fx reg-cofx inject-cofx dispatch trim-v]] [taoensso.timbre :as log] + [status-im.chat.constants :as const] + [status-im.chat.models.commands :as commands-model] + [status-im.commands.utils :as commands-utils] [status-im.data-store.messages :as msg-store] - [status-im.utils.handlers :refer [register-handler-fx]] [status-im.native-module.core :as status] [status-im.i18n :as i18n] - [status-im.utils.platform :as platform])) + [status-im.utils.handlers :refer [register-handler-fx]] + [status-im.utils.platform :as platform] + [status-im.utils.types :as types])) ;;;; Helper fns (defn generate-context "Generates context for jail call" [{:keys [chats] :accounts/keys [current-account-id]} chat-id to group-id] - (merge {:platform platform/platform - :from current-account-id - :to to - :chat {:chat-id chat-id - :group-chat (or (get-in chats [chat-id :group-chat]) - (not (nil? group-id)))}} + (merge {:platform platform/platform + :from current-account-id + :to to + :chat {:chat-id chat-id + :group-chat (or (get-in chats [chat-id :group-chat]) + (not (nil? group-id)))}} i18n/delimeters)) ;;;; Coeffects @@ -65,35 +69,38 @@ (assoc :dispatch (on-requested returned))))) (register-handler-fx - :request-command-data - [trim-v] - (fn [{:keys [db]} - [{{:keys [command content-command params type]} :content - :keys [chat-id jail-id group-id message-id handler-data] :as message} - data-type]] - (let [{:keys [chats] - :accounts/keys [current-account-id] - :contacts/keys [contacts]} db - jail-id (or jail-id chat-id) - jail-id (if (get-in chats [jail-id :group-chat]) - (get-in chats [jail-id :command-suggestions (keyword command) :owner-id]) - jail-id)] - (if (get-in contacts [jail-id :commands-loaded?]) - (let [path [(if (= :response (keyword type)) :responses :commands) - (or content-command command) - data-type] - to (get-in contacts [chat-id :address]) - jail-params {:parameters params - :context (generate-context db chat-id to group-id)}] - {:chat-fx/call-jail {:jail-id jail-id - :path path - :params jail-params - :callback-events-creator (fn [jail-response] - [[::jail-command-data-response - jail-response message data-type]])}}) - {:dispatch-n [[:add-commands-loading-callback jail-id - #(dispatch [:request-command-data message data-type])] - [:load-commands! jail-id]]})))) + :request-command-data + [trim-v] + (fn [{:keys [db]} + [{{command-name :command + content-command-name :content-command + :keys [content-command-scope scope params type bot]} :content + :keys [chat-id jail-id group-id] :as message} + data-type]] + (let [{:keys [chats] + :accounts/keys [current-account-id] + :contacts/keys [contacts]} db + jail-id (or bot jail-id chat-id) + jail-id (if (get-in chats [jail-id :group-chat]) + (get-in chats [jail-id :command-suggestions (keyword command) :owner-id]) + jail-id)] + (if (get-in contacts [jail-id :commands-loaded?]) + (let [path [(if (= :response (keyword type)) :responses :commands) + [(if content-command-name content-command-name command-name) + (commands-model/scope->int (or scope content-command-scope))] + data-type] + to (get-in contacts [chat-id :address]) + jail-params {:parameters params + :context (generate-context db chat-id to group-id)}] + {:chat-fx/call-jail {:jail-id jail-id + :path path + :params jail-params + :callback-events-creator (fn [jail-response] + [[::jail-command-data-response + jail-response message data-type]])}}) + {:dispatch-n [[:add-commands-loading-callback jail-id + #(dispatch [:request-command-data message data-type])] + [:load-commands! jail-id]]})))) (register-handler-fx :execute-command-immediately diff --git a/src/status_im/chat/events/input.cljs b/src/status_im/chat/events/input.cljs index 669a5223acb..300cfef13d2 100644 --- a/src/status_im/chat/events/input.cljs +++ b/src/status_im/chat/events/input.cljs @@ -5,12 +5,13 @@ [status-im.chat.constants :as const] [status-im.chat.utils :as chat-utils] [status-im.chat.models.input :as input-model] - [status-im.chat.models.suggestions :as suggestions] + [status-im.chat.models.commands :as commands-model] [status-im.components.react :as react-comp] [status-im.native-module.core :as status] [status-im.utils.datetime :as time] [status-im.utils.handlers :refer [register-handler-db register-handler-fx]] [status-im.utils.random :as random] + [status-im.utils.types :as types] [status-im.i18n :as i18n])) ;;;; Coeffects @@ -113,19 +114,19 @@ :prev-command name}] [:load-chat-parameter-box command 0]]} - prefill-bot-db - (update :dispatch-n conj [:update-bot-db {:bot current-chat-id - :db prefill-bot-db}]) + prefill-bot-db + (update :dispatch-n conj [:update-bot-db {:bot current-chat-id + :db prefill-bot-db}]) - (not (and sequential-params - prevent-auto-focus?)) - (update :dispatch-n conj [:chat-input-focus :input-ref]) + (not (and sequential-params + prevent-auto-focus?)) + (update :dispatch-n conj [:chat-input-focus :input-ref]) - sequential-params - (assoc :dispatch-later (cond-> [{:ms 100 :dispatch [:set-chat-seq-arg-input-text - (str/join const/spacing-char prefill)]}] - (not prevent-auto-focus?) - (conj {:ms 100 :dispatch [:chat-input-focus :seq-input-ref]})))))) + sequential-params + (assoc :dispatch-later (cond-> [{:ms 100 :dispatch [:set-chat-seq-arg-input-text + (str/join const/spacing-char prefill)]}] + (not prevent-auto-focus?) + (conj {:ms 100 :dispatch [:chat-input-focus :seq-input-ref]})))))) (register-handler-db :set-chat-input-metadata @@ -173,68 +174,67 @@ {::blur-rn-component cmp-ref}))) (register-handler-fx - :update-suggestions - [trim-v] - (fn [{{:keys [current-chat-id] :as db} :db} [chat-id text]] - (let [chat-id (or chat-id current-chat-id) - chat-text (str/trim (or text (get-in db [:chats chat-id :input-text]) "")) - requests (->> (suggestions/get-request-suggestions db chat-text) - (remove (fn [{:keys [type]}] - (= type :grant-permissions)))) - commands (suggestions/get-command-suggestions db chat-text) - global-commands (suggestions/get-global-command-suggestions db chat-text) - all-commands (->> (into global-commands commands) - (remove (fn [[k {:keys [hidden?]}]] hidden?)) - (into {})) - {:keys [dapp?]} (get-in db [:contacts/contacts chat-id]) - new-db (cond-> db - true (assoc-in [:chats chat-id :request-suggestions] requests) - true (assoc-in [:chats chat-id :command-suggestions] all-commands) - (and dapp? - (str/blank? chat-text)) - (assoc-in [:chats chat-id :parameter-boxes :message] nil)) - new-event (when (and dapp? - (not (str/blank? chat-text)) - (every? empty? [requests commands])) - [::check-dapp-suggestions chat-id chat-text])] - (cond-> {:db new-db} - new-event (assoc :dispatch new-event))))) + :update-suggestions + [trim-v] + (fn [{{:keys [current-chat-id] :as db} :db} [chat-id text]] + (let [chat-id (or chat-id current-chat-id) + chat-text (str/trim (or text (get-in db [:chats chat-id :input-text]) "")) + requests (->> (commands-model/get-request-suggestions db chat-text) + (remove (fn [{:keys [type]}] + (= type :grant-permissions)))) + commands (commands-model/commands-for-chat db chat-id chat-text) + {:keys [dapp?]} (get-in db [:contacts/contacts chat-id]) + new-db (cond-> db + true (assoc-in [:chats chat-id :request-suggestions] requests) + true (assoc-in [:chats chat-id :command-suggestions] commands) + (and dapp? + (str/blank? chat-text)) + (assoc-in [:chats chat-id :parameter-boxes :message] nil)) + new-event (when (and dapp? + (not (str/blank? chat-text)) + (every? empty? [requests commands])) + [::check-dapp-suggestions chat-id chat-text])] + (cond-> {:db new-db} + new-event (assoc :dispatch new-event))))) (register-handler-fx - :load-chat-parameter-box - [trim-v] - (fn [{{:keys [current-chat-id bot-db] :accounts/keys [current-account-id] :as db} :db} - [{:keys [name type bot owner-id] :as command}]] - (let [parameter-index (input-model/argument-position db current-chat-id)] - (when (and command (> parameter-index -1)) - (let [data (get-in db [:local-storage current-chat-id]) - bot-db (get bot-db (or bot current-chat-id)) - path [(if (= :command type) :commands :responses) - name - :params - parameter-index - :suggestions] - args (-> (get-in db [:chats current-chat-id :input-text]) - (input-model/split-command-args) - (rest)) - seq-arg (get-in db [:chats current-chat-id :seq-argument-input-text]) - to (get-in db [:contacts/contacts current-chat-id :address]) - params {:parameters {:args args - :bot-db bot-db - :seq-arg seq-arg} - :context (merge {:data data - :from current-account-id - :to to} - (input-model/command-dependent-context-params current-chat-id command))}] - {:chat-fx/call-jail {:jail-id (or bot owner-id current-chat-id) - :path path - :params params - :callback-events-creator (fn [jail-response] - [[:received-bot-response - {:chat-id current-chat-id - :command command - :parameter-index parameter-index} - jail-response]])}}))))) + :load-chat-parameter-box + [trim-v] + (fn [{{:keys [current-chat-id bot-db] :accounts/keys [current-account-id] :as db} :db} + [{:keys [name scope type bot owner-id] :as command}]] + (let [parameter-index (input-model/argument-position db current-chat-id)] + (when (and command (> parameter-index -1)) + (let [data (get-in db [:local-storage current-chat-id]) + bot-db (get bot-db (or bot current-chat-id)) + path [(if (= :command type) :commands :responses) + [name + (if (= :command type) + (commands-model/scope->int scope) + 0)] + :params + parameter-index + :suggestions] + args (-> (get-in db [:chats current-chat-id :input-text]) + (input-model/split-command-args) + (rest)) + seq-arg (get-in db [:chats current-chat-id :seq-argument-input-text]) + to (get-in db [:contacts/contacts current-chat-id :address]) + params {:parameters {:args args + :bot-db bot-db + :seq-arg seq-arg} + :context (merge {:data data + :from current-account-id + :to to} + (input-model/command-dependent-context-params current-chat-id command))}] + {:chat-fx/call-jail {:jail-id (or bot owner-id current-chat-id) + :path path + :params params + :callback-events-creator (fn [jail-response] + [[:received-bot-response + {:chat-id current-chat-id + :command command + :parameter-index parameter-index} + jail-response]])}}))))) (register-handler-fx ::send-message @@ -355,6 +355,8 @@ :chat-id chat-id :jail-id (or owner-id jail-id) :content {:command (:name command) + :scope (when (= (:to-message-id metadata) :any) + (:scope command)) :params params :type (:type command)} :on-requested (fn [jail-response] diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index faae2da7f09..678ce010767 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -1,11 +1,9 @@ (ns status-im.chat.handlers (:require-macros [cljs.core.async.macros :as am]) (:require [re-frame.core :refer [enrich after debug dispatch reg-fx]] - [status-im.models.commands :as commands] [clojure.string :as string] [status-im.components.styles :refer [default-chat-color]] - [status-im.chat.models.suggestions :as suggestions] - [status-im.chat.constants :as chat-consts] + [status-im.chat.constants :as chat-const] [status-im.protocol.core :as protocol] [status-im.data-store.chats :as chats] [status-im.data-store.contacts :as contacts] @@ -29,7 +27,7 @@ [status-im.utils.types :refer [json->clj]] [status-im.chat.utils :refer [console? not-console? safe-trim]] [status-im.utils.gfycat.core :refer [generate-gfy]] - status-im.chat.events.input + status-im.chat.events.input status-im.chat.events.commands status-im.chat.handlers.animation status-im.chat.handlers.requests @@ -128,19 +126,19 @@ (register-handler :account-generation-message (u/side-effect! (fn [_] - (when-not (messages/get-by-id chat-consts/passphrase-message-id) + (when-not (messages/get-by-id chat-const/passphrase-message-id) (sign-up-service/account-generation-message))))) (register-handler :move-to-internal-failure-message (u/side-effect! (fn [_] - (when-not (messages/get-by-id chat-consts/move-to-internal-failure-message-id) + (when-not (messages/get-by-id chat-const/move-to-internal-failure-message-id) (sign-up-service/move-to-internal-failure-message))))) (register-handler :show-mnemonic (u/side-effect! (fn [_ [_ mnemonic signing-phrase]] - (let [crazy-math-message? (messages/get-by-id chat-consts/crazy-math-message-id)] + (let [crazy-math-message? (messages/get-by-id chat-const/crazy-math-message-id)] (sign-up-service/passphrase-messages mnemonic signing-phrase crazy-math-message?))))) (defn- handle-sms [{body :body}] @@ -186,7 +184,7 @@ (defn load-messages! ([db] (load-messages! db nil)) ([{:keys [current-chat-id] :as db} _] - (let [messages (messages/get-by-chat-id current-chat-id)] + (let [messages (messages/get-by-chat-id current-chat-id)] (assoc db :messages messages)))) (defn init-chat @@ -456,7 +454,7 @@ (when dapp-url (am/go (dispatch [:select-chat-input-command - (assoc (:browse global-commands) :prefill [dapp-url]) + (assoc (first (:browse global-commands)) :prefill [dapp-url]) nil true]) (a/int scope)] :handler] :params jail-params :callback (if (:async-handler command) ; async handler, we ignore return value (fn [_] @@ -187,7 +191,7 @@ :from identity :content-type text-content-type :outgoing true - :timestamp (time/now-ms) + :timestamp (datetime/now-ms) :clock-value (clocks/send clock-value) :show? true}) message'' (cond-> message' @@ -212,7 +216,7 @@ (u/side-effect! (fn [_ [_ {:keys [chat-id message]}]] (dispatch [:upsert-chat! {:chat-id chat-id - :timestamp (time/now-ms)}]) + :timestamp (datetime/now-ms)}]) (messages/save chat-id message)))) (register-handler ::send-dapp-message diff --git a/src/status_im/chat/handlers/webview_bridge.cljs b/src/status_im/chat/handlers/webview_bridge.cljs index ddb7bc1d147..d7db9350419 100644 --- a/src/status_im/chat/handlers/webview_bridge.cljs +++ b/src/status_im/chat/handlers/webview_bridge.cljs @@ -5,7 +5,6 @@ [status-im.utils.types :as t] [status-im.i18n :refer [label]] [taoensso.timbre :as log] - [status-im.models.commands :as commands] [status-im.commands.utils :as cu] [status-im.native-module.core :as s] [status-im.components.nfc :as nfc] diff --git a/src/status_im/chat/models/commands.cljs b/src/status_im/chat/models/commands.cljs new file mode 100644 index 00000000000..81f124cc56d --- /dev/null +++ b/src/status_im/chat/models/commands.cljs @@ -0,0 +1,116 @@ +(ns status-im.chat.models.commands + (:require [status-im.chat.constants :as chat-consts] + [status-im.bots.constants :as bots-constants] + [clojure.string :as str] + [taoensso.timbre :as log])) + +(defn scope->int [{:keys [global? registered-only? personal-chats? group-chats? can-use-for-dapps?]}] + (bit-or (if global? 1 0) + (if registered-only? 2 0) + (if personal-chats? 4 0) + (if group-chats? 8 0) + (if can-use-for-dapps? 16 0))) + +(defn can-be-suggested? + [first-char name-key text] + (fn [command] + (let [name (get command name-key)] + (let [text' (cond + (.startsWith text first-char) + text + + (str/blank? text) + first-char + + :default + nil)] + (.startsWith (str first-char name) text'))))) + +(defn get-mixable-commands [{:contacts/keys [contacts]}] + (->> contacts + (vals) + (filter :mixable?) + (mapv :commands) + (apply merge))) + +(defn get-mixable-identities [{:contacts/keys [contacts]}] + (->> contacts + (vals) + (filter :mixable?) + (map (fn [{:keys [whisper-identity]}] + {:identity whisper-identity})))) + +(defn find-suggestions + ([commands text] + (find-suggestions chat-consts/command-char :name commands text)) + ([first-char name-key commands text] + (->> commands + (map val) + (map (fn [items] + (filter #((can-be-suggested? first-char name-key text) %) items))) + (remove empty?) + (flatten)))) + +(defn get-request-suggestions + [{:keys [current-chat-id] :as db} text] + (let [requests (->> (get-in db [:chats current-chat-id :requests]) + (map (fn [{:keys [type] :as req}] + [type (map (fn [resp] + (assoc resp :request req)) + (get-in db [:contacts/contacts current-chat-id :responses type]))])) + (remove (fn [[_ items]] (empty? items))) + (into {}))] + (find-suggestions requests text))) + +(defn get-command-suggestions + [{:keys [current-chat-id] :as db} text] + (->> (get-in db [:chats current-chat-id :contacts]) + (into (get-mixable-identities db)) + (map (fn [{:keys [identity]}] + (let [commands (get-in db [:contacts/contacts identity :commands])] + (find-suggestions commands text)))) + (flatten))) + +(defn get-global-command-suggestions + [{:keys [global-commands] :as db} text] + (find-suggestions chat-consts/bot-char :name global-commands text)) + +(defn commands-for-chat + [{:keys [global-commands chats] + :contacts/keys [contacts] + :accounts/keys [accounts current-account-id] + :as db} chat-id text] + (let [global-commands (get-global-command-suggestions db text) + commands (get-command-suggestions db text) + account (get accounts current-account-id) + commands (-> (into [] global-commands) + (into commands)) + {chat-contacts :contacts} (get chats chat-id)] + (->> commands + (remove (fn [{:keys [scope]}] + (or + (and (:registered-only? scope) + (not (:address account))) + (and (not (:personal-chats? scope)) + (= (count chat-contacts) 1)) + (and (not (:group-chats? scope)) + (> (count chat-contacts) 1)) + (and (not (:can-use-for-dapps? scope)) + (every? (fn [{:keys [identity]}] + (get-in contacts [identity :dapp?])) + chat-contacts)))))))) + +(defn parse-command-message-content + [commands global-commands content] + (if (map? content) + (let [{:keys [command bot]} content] + (if (and bot (not (bots-constants/mailman-bot? bot))) + (update content :command #((keyword bot) global-commands)) + (update content :command #((keyword command) commands)))) + content)) + +(defn parse-command-request [commands content] + (log/debug "ALWX parse-command-request" commands content) + (update content :command (fn [name] + ;; TODO(alwx): probably not `first` !! + (first ((keyword name) commands))))) \ No newline at end of file diff --git a/src/status_im/chat/models/input.cljs b/src/status_im/chat/models/input.cljs index b0d691b1a6b..834e74b6cb7 100644 --- a/src/status_im/chat/models/input.cljs +++ b/src/status_im/chat/models/input.cljs @@ -1,13 +1,14 @@ (ns status-im.chat.models.input (:require [clojure.string :as str] + [status-im.bots.constants :as bots-constants] [status-im.components.react :as rc] [status-im.native-module.core :as status] [status-im.chat.constants :as const] + [status-im.chat.models.commands :as commands-model] [status-im.chat.views.input.validation-messages :refer [validation-message]] + [status-im.chat.utils :as chat-utils] [status-im.i18n :as i18n] [status-im.utils.phone-number :as phone-number] - [status-im.chat.utils :as chat-utils] - [status-im.bots.constants :as bots-constants] [status-im.js-dependencies :as dependencies] [taoensso.timbre :as log])) @@ -34,28 +35,32 @@ (or (str/starts-with? text const/bot-char) (str/starts-with? text const/command-char)))) +;; TODO(alwx): update tests (defn possible-chat-actions "Returns a map of possible chat actions (commands and response) for a specified `chat-id`. Every map's key is a command's name, value is a pair of [`command` `message-id`]. In the case of commands `message-id` is `:any`, for responses value contains the actual id. Example of output: - {:browse [{:description \"Launch the browser\" :name \"browse\" ...} :any] - :request [{:description \"Request a payment\" :name \"request\" ...} \"message-id\"]}" + [[{:description \"Launch the browser\" :name \"browse\" ...} :any] + [{:description \"Request a payment\" :name \"request\" ...} \"message-id\"]]" [{:keys [global-commands current-chat-id] :as db} chat-id] (let [chat-id (or chat-id current-chat-id) {:keys [contacts requests]} (get-in db [:chats chat-id])] (->> contacts (map (fn [{:keys [identity]}] - (let [{:keys [commands responses]} (get-in db [:contacts/contacts identity])] - (let [commands' (mapv (fn [[k v]] [k [v :any]]) (merge global-commands commands)) - responses' (mapv (fn [{:keys [message-id type]}] - (when-let [response (get responses type)] - [type [response message-id]])) - requests)] - (into commands' responses'))))) - (reduce (fn [m cur] (into (or m {}) cur))) - (into {})))) + (let [{:keys [commands responses]} (get-in db [:contacts/contacts identity]) + mixable-commands (commands-model/get-mixable-commands db) + commands' (->> (merge global-commands commands mixable-commands) + (mapv (fn [[_ items]] + (mapv (fn [v] [v :any]) items)))) + responses' (->> requests + (mapv (fn [{:keys [message-id type]}] + (when-let [responses (get responses type)] + (map (fn [response] [response message-id]) responses)))))] + (into commands' responses')))) + (apply into) + (reduce into)))) (defn split-command-args "Returns a list of command's arguments including the command's name. @@ -133,19 +138,18 @@ command-args (split-command-args input-text) command-name (first command-args)] (when (starts-as-command? (or command-name "")) - (when-let [[command to-message-id] - (-> (filter (fn [[{:keys [name bot]} message-id]] - (= (or (when-not (bots-constants/mailman-bot? bot) bot) name) - (subs command-name 1))) - (vals possible-actions)) - (first))] - {:command command - :metadata (if (and (nil? (:to-message-id input-metadata)) (not= :any to-message-id)) - (assoc input-metadata :to-message-id to-message-id) - input-metadata) - :args (if (empty? seq-arguments) - (rest command-args) - seq-arguments)})))) + (when-let [[{{:keys [message-id]} :request :as command} to-message-id] + (->> possible-actions + (filter (fn [[{:keys [name]} _]] + (= name (subs command-name 1)))) + (first))] + {:command command + :metadata (if (and (nil? (:to-message-id input-metadata)) (not= :any message-id)) + (assoc input-metadata :to-message-id to-message-id) + input-metadata) + :args (if (empty? seq-arguments) + (rest command-args) + seq-arguments)})))) ([{:keys [current-chat-id] :as db} chat-id] (selected-chat-command db chat-id (get-in db [:chats chat-id :input-text])))) diff --git a/src/status_im/chat/models/suggestions.cljs b/src/status_im/chat/models/suggestions.cljs deleted file mode 100644 index d0404650a58..00000000000 --- a/src/status_im/chat/models/suggestions.cljs +++ /dev/null @@ -1,43 +0,0 @@ -(ns status-im.chat.models.suggestions - (:require [status-im.chat.constants :as chat-consts] - [clojure.string :as str])) - -(defn can-be-suggested? - ([text] (can-be-suggested? chat-consts/command-char :name text)) - ([first-char name-key text] - (fn [command] - (let [name (get command name-key)] - (let [text' (cond - (.startsWith text first-char) - text - - (str/blank? text) - first-char - - :default - nil)] - (.startsWith (str first-char name) text')))))) - -(defn get-request-suggestions - [{:keys [current-chat-id] :as db} text] - (let [requests (get-in db [:chats current-chat-id :requests])] - (->> requests - (map (fn [{:keys [type] :as v}] - (assoc v :name (get-in db [:contacts/contacts current-chat-id :responses type :name])))) - (filter (fn [v] ((can-be-suggested? text) v)))))) - -(defn get-command-suggestions - [{:keys [current-chat-id] :as db} text] - (->> (get-in db [:chats current-chat-id :contacts]) - (map (fn [{:keys [identity]}] - (let [commands (get-in db [:contacts/contacts identity :commands])] - (->> commands - (filter (fn [[_ v]] ((can-be-suggested? text) v))))))) - (reduce (fn [m cur] (into (or m {}) cur))) - (into {}))) - -(defn get-global-command-suggestions - [{:keys [global-commands] :as db} text] - (->> global-commands - (filter (fn [[_ v]] ((can-be-suggested? chat-consts/bot-char :bot text) v))) - (into {}))) diff --git a/src/status_im/chat/screen.cljs b/src/status_im/chat/screen.cljs index c5f384ba961..6f505edeac5 100644 --- a/src/status_im/chat/screen.cljs +++ b/src/status_im/chat/screen.cljs @@ -25,7 +25,7 @@ [status-im.chat.views.input.input :as input] [status-im.chat.views.actions :refer [actions-view]] [status-im.chat.views.bottom-info :refer [bottom-info-view]] - [status-im.chat.constants :as const] + [status-im.chat.constants :as chat-const] [status-im.i18n :refer [label label-pluralize]] [status-im.components.animation :as anim] [status-im.components.sync-state.offline :refer [offline-view]] @@ -124,7 +124,7 @@ (defn get-intro-status-message [all-messages] (let [{:keys [timestamp content-type]} (last all-messages)] (when (not= content-type content-type-status) - {:message-id const/intro-status-message-id + {:message-id chat-const/intro-status-message-id :content-type content-type-status :timestamp (or timestamp (time/now-ms))}))) diff --git a/src/status_im/chat/subs.cljs b/src/status_im/chat/subs.cljs index 855aa418626..f768c271547 100644 --- a/src/status_im/chat/subs.cljs +++ b/src/status_im/chat/subs.cljs @@ -1,19 +1,18 @@ (ns status-im.chat.subs - (:require [re-frame.core :refer [reg-sub dispatch subscribe path]] + (:require [re-frame.core :refer [reg-sub dispatch subscribe path]] [status-im.data-store.chats :as chats] [status-im.chat.constants :as const] - [status-im.chat.models.input :as input-model] + [status-im.chat.models.input :as input-model] [status-im.chat.utils :as chat-utils] [status-im.chat.views.input.utils :as input-utils] [status-im.constants :refer [response-suggesstion-resize-duration content-type-status console-chat-id]] [status-im.commands.utils :as commands-utils] - [status-im.models.commands :as commands] [status-im.utils.platform :refer [platform-specific ios?]] [taoensso.timbre :as log] [clojure.string :as str])) - + (reg-sub :chat-ui-props (fn [db [_ ui-element chat-id]] @@ -59,11 +58,6 @@ (let [chat-id (subscribe [:get-current-chat-id])] (get-in db [:bots-suggestions @chat-id])))) -(reg-sub :get-commands - (fn [db [_ chat-id]] - (let [current-chat (or chat-id (db :current-chat-id))] - (or (get-in db [:contacts/contacts current-chat :commands]) {})))) - (reg-sub :get-responses (fn [db [_ chat-id]] @@ -71,13 +65,14 @@ (or (get-in db [:contacts/contacts current-chat :responses]) {})))) (reg-sub :get-commands-and-responses - (fn [{:keys [chats] :contacts/keys [contacts]} [_ chat-id]] + (fn [{:keys [chats global-commands] :contacts/keys [contacts]} [_ chat-id]] (->> (get-in chats [chat-id :contacts]) (filter :is-in-chat) (mapv (fn [{:keys [identity]}] (let [{:keys [commands responses]} (get contacts identity)] (merge responses commands)))) - (apply merge)))) + (apply merge) + (merge global-commands)))) (reg-sub :selected-chat-command @@ -192,7 +187,7 @@ (fn [db [_ message-id]] (get-in db [:message-data :short-preview message-id :markup]))) -(reg-sub :get-last-message-short-preview +(reg-sub :get-last-message-short-preview (fn [db [_ chat-id]] (let [last-message (subscribe [:get-last-message chat-id]) preview (subscribe [:get-message-short-preview-markup (:message-id @last-message)])] diff --git a/src/status_im/chat/utils.cljs b/src/status_im/chat/utils.cljs index 801cb290a98..ba5a640d908 100644 --- a/src/status_im/chat/utils.cljs +++ b/src/status_im/chat/utils.cljs @@ -1,18 +1,16 @@ (ns status-im.chat.utils - (:require [status-im.constants :refer [console-chat-id - wallet-chat-id]] - [clojure.string :as str] - [status-im.chat.constants :as const] - [status-im.bots.constants :as bots-constants])) + (:require [clojure.string :as str] + [status-im.constants :as consts] + [status-im.chat.constants :as chat-const])) (defn console? [s] - (= console-chat-id s)) + (= consts/console-chat-id s)) (def not-console? (complement console?)) (defn wallet? [s] - (= wallet-chat-id s)) + (= consts/wallet-chat-id s)) (defn safe-trim [s] (when (string? s) @@ -48,11 +46,10 @@ (validator message) (pos? (count message)))) -(defn command-name [{:keys [bot name]}] +(defn command-name [{:keys [bot name scope]}] (cond - (bots-constants/mailman-bot? bot) - (str const/command-char name) + (:global? scope) + (str chat-const/bot-char name) - bot (str const/bot-char bot) - - :else (str const/command-char name))) + :default + (str chat-const/command-char name))) diff --git a/src/status_im/chat/views/input/input.cljs b/src/status_im/chat/views/input/input.cljs index 2510e95de63..870d8c9f224 100644 --- a/src/status_im/chat/views/input/input.cljs +++ b/src/status_im/chat/views/input/input.cljs @@ -36,7 +36,6 @@ (defview commands-view [] [commands [:chat :command-suggestions] - responses [:get-responses] requests [:chat :request-suggestions] show-suggestions? [:show-suggestions?]] [view style/commands-root @@ -51,11 +50,9 @@ [scroll-view {:horizontal true :showsHorizontalScrollIndicator false :keyboardShouldPersistTaps :always} - (let [requests-names (map :type requests) - all-commands (merge (into {} commands) (select-keys responses requests-names)) - all-commands-indexed (map-indexed vector (vals all-commands))] + (let [all-commands (apply conj commands requests)] [view style/commands - (for [[index command] all-commands-indexed] + (for [[index command] (map-indexed vector all-commands)] ^{:key (str "command-" index)} [command-view (= index 0) command])])]]) diff --git a/src/status_im/chat/views/input/suggestions.cljs b/src/status_im/chat/views/input/suggestions.cljs index c2553f542e5..bea38c34dc5 100644 --- a/src/status_im/chat/views/input/suggestions.cljs +++ b/src/status_im/chat/views/input/suggestions.cljs @@ -25,14 +25,14 @@ :number-of-lines 2} description]]]) -(defview request-item [{:keys [type message-id]} last?] - [{:keys [name description] :as response} [:get-response type] - {:keys [chat-id]} [:get-current-chat]] +(defview request-item [{:keys [name description] + {:keys [type message-id]} :request :as command} last?] + [{:keys [chat-id]} [:get-current-chat]] [suggestion-item {:on-press #(let [{:keys [params]} (messages/get-message-content-by-id message-id) metadata (assoc params :to-message-id message-id)] - (dispatch [:select-chat-input-command response metadata])) - :name name + (dispatch [:select-chat-input-command command metadata])) + :name (chat-utils/command-name command) :description description :last? last?}]) @@ -61,14 +61,14 @@ (when (seq requests) [view [item-title false (label :t/suggestions-requests)] - (for [[i {:keys [chat-id message-id] :as request}] (map-indexed vector requests)] + (for [[i {{:keys [chat-id message-id]} :request :as request}] (map-indexed vector requests)] ^{:key [chat-id message-id]} [request-item request (= i (dec (count requests)))])]) (when (seq commands) [view [item-title (seq requests) (label :t/suggestions-commands)] - (for [[i [_ command]] (->> commands - (remove #(nil? (:title (second %)))) - (map-indexed vector))] + (for [[i command] (->> commands + (remove #(nil? (:title %))) + (map-indexed vector))] ^{:key i} [command-item command (= i (dec (count commands)))])])]]])) diff --git a/src/status_im/chat/views/message/message.cljs b/src/status_im/chat/views/message/message.cljs index f5ea1f46dd3..defe04ac73f 100644 --- a/src/status_im/chat/views/message/message.cljs +++ b/src/status_im/chat/views/message/message.cljs @@ -15,14 +15,13 @@ get-dimensions dismiss-keyboard!]] [status-im.components.animation :as anim] - [status-im.chat.constants :as chat-consts] [status-im.components.list-selection :refer [share browse share-or-open-map]] - [status-im.chat.views.message.request-message :refer [message-content-command-request]] + [status-im.chat.constants :as chat-consts] + [status-im.chat.models.commands :as commands] [status-im.chat.styles.message.message :as st] [status-im.chat.styles.message.command-pill :as pill-st] + [status-im.chat.views.message.request-message :refer [message-content-command-request]] [status-im.chat.views.message.datemark :refer [chat-datemark]] - [status-im.models.commands :refer [parse-command-message-content - parse-command-request]] [status-im.react-native.resources :as res] [status-im.constants :refer [console-chat-id wallet-chat-id @@ -125,11 +124,6 @@ (first (vals params)) (str params))])) -(defn commands-subscription [{:keys [type]}] - (if (= type "response") - :get-responses - :get-commands)) - (defview message-content-command [{:keys [message-id content content-type chat-id to from outgoing] :as message}] [commands [:get-commands-and-responses chat-id] @@ -139,7 +133,7 @@ contact-chat [:get-in [:chats (if outgoing to from)]] preview [:get-message-preview message-id]] (let [commands (merge commands from-commands) - {:keys [command params]} (parse-command-message-content commands global-commands content) + {:keys [command params]} (commands/parse-command-message-content commands global-commands content) {:keys [name type] icon-path :icon} command] [view st/content-command-view diff --git a/src/status_im/chat/views/message/request_message.cljs b/src/status_im/chat/views/message/request_message.cljs index 0072dcfacf3..786763b5700 100644 --- a/src/status_im/chat/views/message/request_message.cljs +++ b/src/status_im/chat/views/message/request_message.cljs @@ -8,7 +8,8 @@ icon touchable-highlight]] [status-im.chat.styles.message.message :as st] - [status-im.models.commands :refer [parse-command-request]] + [status-im.accessibility-ids :as id] + [status-im.chat.models.commands :as commands] [status-im.components.animation :as anim] [taoensso.timbre :as log])) @@ -80,7 +81,7 @@ (let [commands @commands-atom {:keys [prefill prefill-bot-db prefillBotDb params] text-content :text} content - {:keys [command content]} (parse-command-request commands content) + {:keys [command content]} (commands/parse-command-request commands content) command (if (and params command) (merge command {:prefill prefill :prefill-bot-db (or prefill-bot-db prefillBotDb)}) diff --git a/src/status_im/commands/handlers/jail.cljs b/src/status_im/commands/handlers/jail.cljs index face714491e..b73f5b0c5db 100644 --- a/src/status_im/commands/handlers/jail.cljs +++ b/src/status_im/commands/handlers/jail.cljs @@ -6,7 +6,6 @@ [status-im.commands.utils :refer [generate-hiccup reg-handler]] [clojure.string :as s] [status-im.components.react :as r] - [status-im.models.commands :as cm] [status-im.constants :refer [console-chat-id]] [status-im.i18n :refer [get-contact-translated]] [taoensso.timbre :as log] diff --git a/src/status_im/commands/handlers/loading.cljs b/src/status_im/commands/handlers/loading.cljs index e196079b6f2..658a747eb9a 100644 --- a/src/status_im/commands/handlers/loading.cljs +++ b/src/status_im/commands/handlers/loading.cljs @@ -3,21 +3,22 @@ [status-im.utils.handlers :as u] [status-im.utils.utils :refer [http-get show-popup]] [clojure.string :as s] + [status-im.bots.constants :as bots-constants] [status-im.data-store.commands :as commands] [status-im.data-store.contacts :as contacts] - [status-im.native-module.core :as status] - [status-im.utils.types :refer [json->clj]] + [status-im.data-store.local-storage :as local-storage] + [status-im.components.status :as status] [status-im.commands.utils :refer [reg-handler]] [status-im.constants :refer [console-chat-id wallet-chat-id]] - [taoensso.timbre :as log] + [status-im.chat.models.commands :as commands-model] + [status-im.native-module.core :as status] [status-im.i18n :refer [label]] [status-im.utils.homoglyph :as h] [status-im.utils.js-resources :as js-res] - [status-im.utils.random :as random] - [status-im.bots.constants :as bots-constants] + [status-im.utils.random :as random] + [status-im.utils.types :refer [json->clj]] [status-im.utils.datetime :as time] - [status-im.data-store.local-storage :as local-storage] - [clojure.string :as str])) + [taoensso.timbre :as log])) (defn load-commands! @@ -50,13 +51,10 @@ bot-url dapp?]} :contact :as params}]] - (if bot-url + (when bot-url (if-let [resource (js-res/get-resource bot-url)] (dispatch [::validate-hash params resource]) - (http-get-commands params bot-url)) - (when-not dapp? - ;; TODO: this part should be removed in the future - (dispatch [::validate-hash params js-res/wallet-js])))) + (http-get-commands params bot-url)))) (defn dispatch-loaded! [db [{{:keys [whisper-identity]} :contact @@ -99,88 +97,61 @@ (get-hash-by-file file))] (assoc db :command-hash-valid? valid?))) -(defn each-merge [coll with] +(defn each-merge [with coll] (->> coll (map (fn [[k v]] [k (merge v with)])) (into {}))) -(defn filter-commands [account {:keys [contacts chat-id] :as chat} commands] +(defn extract-commands [{:keys [contacts]} commands] (->> commands - (remove (fn [[_ {:keys [registered-only name]}]] - (and (not (:address account)) - (not= name "global") - registered-only))) - ;; TODO: this part should be removed because it's much better to provide the ability to do this in the API - (map (fn [[k {:keys [name] :as v}]] - [k (assoc v :hidden? (and (some #{name} ["send" "request"]) - (= chat-id wallet-chat-id)))])) - (remove (fn [[k _]] + (remove (fn [[_ {:keys [name]}]] (and (= (count contacts) 1) (not= console-chat-id (get (first contacts) :identity)) - (h/matches (name k) "password")))) + (h/matches name "password")))) + (map (fn [[k {:keys [name scope] :as v}]] + [[name scope] v])) (into {}))) -(defn get-mailmans-commands [db] - (->> (get-in db [:contacts/contacts bots-constants/mailman-bot :commands]) - (map - (fn [[k v :as com]] - [k (-> v - (update :params (fn [p] - (if (map? p) - ((comp vec vals) p) - p))) - (assoc :bot bots-constants/mailman-bot - :type :command))])) +(defn extract-global-commands [commands chat-id] + (->> commands + (filter (fn [[_ {:keys [scope]}]] + (:global? scope))) + (map (fn [[k {:keys [name scope] :as v}]] + [[name scope] (assoc v :bot chat-id :type :command)])) (into {}))) +(defn transform-commands [commands] + (reduce (fn [m [_ {:keys [name scope] :as v}]] + (update m (keyword name) conj v)) + {} + commands)) + (defn add-commands - [{:keys [chats] :as db} [id _ {:keys [commands responses subscriptions]}]] - (let [account @(subscribe [:get-current-account]) + [{:keys [current-account-id accounts chats] :as db} + [id _ {:keys [commands responses subscriptions]}]] + (let [account (get accounts current-account-id) chat (get chats id) - commands' (filter-commands account chat commands) - responses' (filter-commands account chat responses) - global-command (:global commands') - commands'' (each-merge (apply dissoc commands' [:init :global]) - {:type :command - :owner-id id}) - mailman-commands (get-mailmans-commands db)] - (cond-> db - - true - (update-in [:contacts/contacts id] assoc - :commands-loaded? true - :commands (merge mailman-commands commands'') - :responses (each-merge responses' {:type :response - :owner-id id}) - :subscriptions subscriptions) - - global-command - (update :global-commands assoc (keyword id) - (assoc global-command :bot id - :type :command)) - - (= id bots-constants/mailman-bot) - (update :contacts/contacts (fn [contacts] - (reduce (fn [contacts [k _]] - (update-in contacts [k :commands] - (fn [c] - (merge mailman-commands c)))) - contacts - contacts)))))) - -(defn save-commands-js! - [_ [id file]] - #_(commands/save {:chat-id id :file file})) - -(defn save-commands! - [{:keys [global-commands] :contacts/keys [contacts]} [id]] - (let [command (get global-commands (keyword id)) - commands (get-in contacts [id :commands]) - responses (get-in contacts [id :responses])] - (contacts/save {:whisper-identity id - :global-command command - :commands (vals commands) - :responses (vals responses)}))) + global-commands (extract-global-commands commands id) + mixable-commands (when-not (get-in db [:contacts/contacts id :mixable?]) + (commands-model/get-mixable-commands db)) + commands' (->> (apply dissoc commands (keys global-commands)) + (extract-commands chat) + (each-merge {:type :command + :owner-id id}) + (transform-commands) + (merge mixable-commands)) + responses' (->> (extract-commands chat responses) + (each-merge {:type :response + :owner-id id}) + (transform-commands)) + new-db (-> db + (update-in [:contacts/contacts id] assoc + :commands-loaded? true + :commands commands' + :responses responses' + :subscriptions subscriptions) + (update :global-commands merge (transform-commands global-commands)))] + new-db)) (defn loading-failed! [db [id reason details]] @@ -210,9 +181,7 @@ (reg-handler ::parse-commands! (u/side-effect! parse-commands!)) (reg-handler ::add-commands - [(after save-commands-js!) - (after save-commands!) - (after #(dispatch [:update-suggestions])) + [(after #(dispatch [:update-suggestions])) (after (fn [_ [id]] (dispatch [:invoke-commands-loading-callbacks id]) (dispatch [:invoke-chat-loaded-callbacks id]))) diff --git a/src/status_im/data_store/messages.cljs b/src/status_im/data_store/messages.cljs index b67f0e52149..25351a21f9e 100644 --- a/src/status_im/data_store/messages.cljs +++ b/src/status_im/data_store/messages.cljs @@ -5,7 +5,8 @@ [status-im.utils.utils :refer [update-if-present]] [clojure.walk :refer [stringify-keys keywordize-keys]] [cljs.reader :refer [read-string]] - [status-im.constants :as c]) + [status-im.constants :as c] + [taoensso.timbre :as log]) (:refer-clojure :exclude [update])) (defn- user-statuses-to-map @@ -101,6 +102,7 @@ ;; todo remove chat-id parameter [chat-id {:keys [message-id content] :as message}] + (log/debug "ALWX command save" message) (when-not (data-store/exists? message-id) (let [content' (if (string? content) content @@ -113,7 +115,8 @@ (data-store/save message')))) (defn update - [{:keys [message-id] :as message}] + [{:keys [message-id preview] :as message}] + (log/debug "ALWX command update" message) (when (data-store/exists? message-id) (let [message (update-if-present message :user-statuses vals)] (data-store/save message)))) diff --git a/src/status_im/data_store/realm/schemas/account/core.cljs b/src/status_im/data_store/realm/schemas/account/core.cljs index d48faaa0f3b..c939ac1de52 100644 --- a/src/status_im/data_store/realm/schemas/account/core.cljs +++ b/src/status_im/data_store/realm/schemas/account/core.cljs @@ -13,6 +13,7 @@ [status-im.data-store.realm.schemas.account.v12.core :as v12] [status-im.data-store.realm.schemas.account.v13.core :as v13] [status-im.data-store.realm.schemas.account.v14.core :as v14] + [status-im.data-store.realm.schemas.account.v15.core :as v15] )) ;; TODO(oskarth): Add failing test if directory vXX exists but isn't in schemas. @@ -60,4 +61,7 @@ {:schema v14/schema :schemaVersion 14 :migration v14/migration} + {:schema v15/schema + :schemaVersion 15 + :migration v15/migration} ]) diff --git a/src/status_im/data_store/realm/schemas/account/v12/contact.cljs b/src/status_im/data_store/realm/schemas/account/v12/contact.cljs new file mode 100644 index 00000000000..ed6273f8642 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v12/contact.cljs @@ -0,0 +1,47 @@ +(ns status-im.data-store.realm.schemas.account.v12.contact + (:require [taoensso.timbre :as log])) + +(def schema {:name :contact + :primaryKey :whisper-identity + :properties {:address {:type :string :optional true} + :whisper-identity :string + :name {:type :string :optional true} + :photo-path {:type :string :optional true} + :last-updated {:type :int :default 0} + :last-online {:type :int :default 0} + :pending? {:type :bool :default false} + :mixable? {:type :bool :default false} + :status {:type :string :optional true} + :public-key {:type :string + :optional true} + :private-key {:type :string + :optional true} + :unremovable? {:type :bool + :default false} + :dapp? {:type :bool + :default false} + :dapp-url {:type :string + :optional true} + :bot-url {:type :string + :optional true} + :global-command {:type :command + :optional true} + :commands {:type :list + :objectType :command} + :responses {:type :list + :objectType :command} + :dapp-hash {:type :int + :optional true} + :debug? {:type :bool + :default false}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating contact schema v12") + (let [new-contacts (.objects new-realm "contact")] + (dotimes [i (.-length new-contacts)] + (let [contact (aget new-contacts i) + id (aget contact "whisper-identity")] + (when (or (= id "mailman") + (= id "transactor-group") + (= id "transactor-personal")) + (aset contact "mixable?" true)))))) diff --git a/src/status_im/data_store/realm/schemas/account/v15/contact.cljs b/src/status_im/data_store/realm/schemas/account/v15/contact.cljs new file mode 100644 index 00000000000..fade9150a39 --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v15/contact.cljs @@ -0,0 +1,46 @@ +(ns status-im.data-store.realm.schemas.account.v15.contact + (:require [taoensso.timbre :as log])) + +(def schema {:name :contact + :primaryKey :whisper-identity + :properties {:address {:type :string :optional true} + :whisper-identity :string + :name {:type :string :optional true} + :photo-path {:type :string :optional true} + :last-updated {:type :int :default 0} + :last-online {:type :int :default 0} + :pending? {:type :bool :default false} + :mixable? {:type :bool :default false} + :status {:type :string :optional true} + :fcm-token {:type :string :optional true} + :public-key {:type :string + :optional true} + :private-key {:type :string + :optional true} + :dapp? {:type :bool + :default false} + :dapp-url {:type :string + :optional true} + :bot-url {:type` :string + :optional true} + :global-command {:type :command + :optional true} + :commands {:type :list + :objectType :command} + :responses {:type :list + :objectType :command} + :dapp-hash {:type :int + :optional true} + :debug? {:type :bool + :default false}}}) + +(defn migration [old-realm new-realm] + (log/debug "migrating contact schema v15") + (let [new-contacts (.objects new-realm "contact")] + (dotimes [i (.-length new-contacts)] + (let [contact (aget new-contacts i) + id (aget contact "whisper-identity")] + (when (or (= id "mailman") + (= id "transactor-group") + (= id "transactor-personal")) + (aset contact "mixable?" true)))))) diff --git a/src/status_im/data_store/realm/schemas/account/v15/core.cljs b/src/status_im/data_store/realm/schemas/account/v15/core.cljs new file mode 100644 index 00000000000..e1c807e4d9d --- /dev/null +++ b/src/status_im/data_store/realm/schemas/account/v15/core.cljs @@ -0,0 +1,39 @@ +(ns status-im.data-store.realm.schemas.account.v15.core + (:require [status-im.data-store.realm.schemas.account.v11.chat :as chat] + [status-im.data-store.realm.schemas.account.v1.chat-contact :as chat-contact] + [status-im.data-store.realm.schemas.account.v6.command :as command] + [status-im.data-store.realm.schemas.account.v9.command-parameter :as command-parameter] + [status-im.data-store.realm.schemas.account.v15.contact :as contact] + [status-im.data-store.realm.schemas.account.v1.discover :as discover] + [status-im.data-store.realm.schemas.account.v1.kv-store :as kv-store] + [status-im.data-store.realm.schemas.account.v10.message :as message] + [status-im.data-store.realm.schemas.account.v12.pending-message :as pending-message] + [status-im.data-store.realm.schemas.account.v1.processed-message :as processed-message] + [status-im.data-store.realm.schemas.account.v1.request :as request] + [status-im.data-store.realm.schemas.account.v1.tag :as tag] + [status-im.data-store.realm.schemas.account.v1.user-status :as user-status] + [status-im.data-store.realm.schemas.account.v5.contact-group :as contact-group] + [status-im.data-store.realm.schemas.account.v5.group-contact :as group-contact] + [status-im.data-store.realm.schemas.account.v8.local-storage :as local-storage] + [status-im.data-store.realm.schemas.account.v13.handler-data :as handler-data] + [taoensso.timbre :as log])) + +(def schema [chat/schema + chat-contact/schema + command/schema + command-parameter/schema + contact/schema + discover/schema + kv-store/schema + message/schema + pending-message/schema + processed-message/schema + request/schema + tag/schema + user-status/schema + contact-group/schema + group-contact/schema + local-storage/schema]) + +(defn migration [old-realm new-realm] + (log/debug "migrating v15 account database: " old-realm new-realm)) \ No newline at end of file diff --git a/src/status_im/debug/handlers.cljs b/src/status_im/debug/handlers.cljs index cefe27516b7..af1faaace47 100644 --- a/src/status_im/debug/handlers.cljs +++ b/src/status_im/debug/handlers.cljs @@ -2,11 +2,11 @@ (:require [re-frame.core :refer [after dispatch]] [status-im.utils.handlers :refer [register-handler] :as u] [status-im.components.react :refer [http-bridge]] - [status-im.utils.types :refer [clj->json]] [status-im.data-store.messages :as messages] [status-im.data-store.accounts :as accounts] [taoensso.timbre :as log] - [status-im.utils.platform :as platform])) + [status-im.utils.platform :as platform] + [status-im.utils.types :as types])) (def debug-server-port 5561) @@ -14,7 +14,7 @@ (.respond http-bridge 200 "application/json" - (clj->json data))) + (types/clj->json data))) (register-handler :init-debug-mode (u/side-effect! diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs deleted file mode 100644 index c3c8e2a29cc..00000000000 --- a/src/status_im/models/commands.cljs +++ /dev/null @@ -1,14 +0,0 @@ -(ns status-im.models.commands - (:require [status-im.bots.constants :as bots-constants])) - -(defn parse-command-message-content - [commands global-commands content] - (if (map? content) - (let [{:keys [command bot]} content] - (if (and bot (not (bots-constants/mailman-bot? bot))) - (update content :command #((keyword bot) global-commands)) - (update content :command #((keyword command) commands)))) - content)) - -(defn parse-command-request [commands content] - (update content :command #((keyword %) commands))) diff --git a/src/status_im/native_module/impl/module.cljs b/src/status_im/native_module/impl/module.cljs index 179f3857a5c..6f044ac7621 100644 --- a/src/status_im/native_module/impl/module.cljs +++ b/src/status_im/native_module/impl/module.cljs @@ -2,7 +2,6 @@ (:require-macros [cljs.core.async.macros :refer [go-loop go]]) (:require [status-im.components.react :as r] - [status-im.utils.types :as t] [re-frame.core :refer [dispatch]] [taoensso.timbre :as log] [cljs.core.async :refer [clj r) + (let [{:keys [result] :as r'} (types/json->clj r) {:keys [messages]} result] (log/debug r') (doseq [{:keys [type message]} messages] diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index d3e77abce31..03dd11d8e72 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -285,18 +285,20 @@ (let [message-id' (or ack-of-message message-id)] (when-let [{:keys [message-status] :as message} (messages/get-by-id message-id')] (when-not (= (keyword message-status) :seen) - (let [group? (boolean group-id) - message (if (and group? (not= status :sent)) - (update-in message - [:user-statuses from] - (fn [{old-status :status}] - {:id (random/id) - :whisper-identity from - :status (if (= (keyword old-status) :seen) - old-status - status)})) - (assoc message :message-status status))] - (messages/update message))))))) + (let [group? (boolean group-id) + message' (-> (if (and group? (not= status :sent)) + (update-in message + [:user-statuses from] + (fn [{old-status :status}] + {:id (random/id) + :whisper-identity from + :status (if (= (keyword old-status) :seen) + old-status + status)})) + (assoc message :message-status status)) + ;; we need to dissoc preview because it has been saved before + (dissoc :preview))] + (messages/update message'))))))) (defn update-message-status [status] (fn [db diff --git a/src/status_im/ui/screens/contacts/db.cljs b/src/status_im/ui/screens/contacts/db.cljs index 90e95ccc027..71c8c9c096a 100644 --- a/src/status_im/ui/screens/contacts/db.cljs +++ b/src/status_im/ui/screens/contacts/db.cljs @@ -27,6 +27,7 @@ (spec/def :contact/last-updated (spec/nilable int?)) (spec/def :contact/last-online (spec/nilable int?)) (spec/def :contact/pending? boolean?) +(spec/def :contact/mixable? boolean?) (spec/def :contact/unremovable? boolean?) (spec/def :contact/dapp? boolean?) @@ -34,21 +35,39 @@ (spec/def :contact/dapp-hash (spec/nilable int?)) (spec/def :contact/bot-url (spec/nilable string?)) (spec/def :contact/global-command (spec/nilable map?)) -(spec/def :contact/commands (spec/nilable (spec/map-of keyword? map?))) -(spec/def :contact/responses (spec/nilable (spec/map-of keyword? map?))) +;; TODO(alwx): +;;(spec/def :contact/commands (spec/nilable (spec/map-of vector? map?))) +;;(spec/def :contact/responses (spec/nilable (spec/map-of vector? map?))) (spec/def :contact/commands-loaded? (spec/nilable boolean?)) (spec/def :contact/subscriptions (spec/nilable map?)) ;true when contact added using status-dev-cli (spec/def :contact/debug? boolean?) -(spec/def :contact/contact (allowed-keys - :req-un [:contact/name :contact/whisper-identity] - :opt-un [:contact/address :contact/private-key :contact/public-key :contact/photo-path - :contact/status :contact/last-updated :contact/last-online :contact/pending? - :contact/unremovable? :contact/dapp? :contact/dapp-url :contact/dapp-hash - :contact/bot-url :contact/global-command :contact/commands-loaded? - :contact/commands :contact/responses :contact/debug? :contact/subscriptions - :contact/fcm-token])) +(spec/def :contact/contact + (allowed-keys + :req-un [:contact/name + :contact/whisper-identity] + :opt-un [:contact/address + :contact/private-key + :contact/public-key + :contact/photo-path + :contact/status + :contact/last-updated + :contact/last-online + :contact/pending? + :contact/mixable? + :contact/unremovable? + :contact/dapp? + :contact/dapp-url + :contact/dapp-hash + :contact/bot-url + :contact/global-command + :contact/commands-loaded? + :contact/commands + :contact/responses + :contact/debug? + :contact/subscriptions + :contact/fcm-token])) ;;Contact list ui props (spec/def :contact-list-ui/edit? boolean?) diff --git a/src/status_im/ui/screens/contacts/events.cljs b/src/status_im/ui/screens/contacts/events.cljs index 1af07e82664..2e0870c7333 100644 --- a/src/status_im/ui/screens/contacts/events.cljs +++ b/src/status_im/ui/screens/contacts/events.cljs @@ -211,8 +211,8 @@ (defn- prepare-default-contacts-events [contacts default-contacts] [[:add-contacts - (for [[id {:keys [name photo-path public-key add-chat? global-command - dapp? dapp-url dapp-hash bot-url unremovable?]}] default-contacts + (for [[id {:keys [name photo-path public-key add-chat? + dapp? dapp-url dapp-hash bot-url unremovable? mixable?]}] default-contacts :let [id' (clojure.core/name id)] :when (not (get contacts id'))] {:whisper-identity id' @@ -221,10 +221,10 @@ :photo-path photo-path :public-key public-key :unremovable? (boolean unremovable?) + :mixable? (boolean mixable?) :dapp? dapp? :dapp-url (:en dapp-url) :bot-url bot-url - :global-command global-command :dapp-hash dapp-hash})]]) (defn- prepare-add-chat-events [contacts default-contacts] @@ -268,17 +268,8 @@ [(inject-cofx ::get-all-contacts)] (fn [{:keys [db all-contacts]} _] (let [contacts-list (map #(vector (:whisper-identity %) %) all-contacts) - global-commands (->> contacts-list - (filter (fn [[_ c]] (:global-command c))) - (map (fn [[id {:keys [global-command]}]] - [(keyword id) (-> global-command - (update :params (comp vec vals)) - (assoc :bot id - :type :command))])) - (into {})) contacts (into {} contacts-list)] - {:db (assoc db :contacts/contacts contacts - :global-commands global-commands) + {:db (assoc db :contacts/contacts contacts) :dispatch-n (mapv (fn [_ contact] [:watch-contact contact]) contacts)}))) (register-handler-fx @@ -290,17 +281,8 @@ (map #(update-pending-status contacts %)) (remove #(identities (:whisper-identity %))) (map #(vector (:whisper-identity %) %)) - (into {})) - global-commands (->> new-contacts' - (keep (fn [[n {:keys [global-command]}]] - (when global-command - [(keyword n) (assoc global-command - :type :command - :bot n)]))) - (into {}))] - {:db (-> db - (update :global-commands merge global-commands) - (update :contacts/contacts merge new-contacts')) + (into {}))] + {:db (update db :contacts/contacts merge new-contacts') ::save-contacts! (vals new-contacts')}))) (register-handler-db diff --git a/src/status_im/ui/screens/contacts/subs.cljs b/src/status_im/ui/screens/contacts/subs.cljs index f10448a37fa..bfe59944e0a 100644 --- a/src/status_im/ui/screens/contacts/subs.cljs +++ b/src/status_im/ui/screens/contacts/subs.cljs @@ -26,9 +26,10 @@ :all-added-contacts :<- [:get-contacts] (fn [contacts] - (->> (remove (fn [[_ {:keys [pending? whisper-identity]}]] + (->> contacts + (remove (fn [[_ {:keys [pending? mixable? whisper-identity]}]] (or (true? pending?) - (bots-constants/hidden-bots whisper-identity))) contacts) + (true? mixable?)))) (sort-contacts)))) (reg-sub diff --git a/src/status_im/utils/js_resources.cljs b/src/status_im/utils/js_resources.cljs index d1d0c1c6044..75966ea108a 100644 --- a/src/status_im/utils/js_resources.cljs +++ b/src/status_im/utils/js_resources.cljs @@ -11,7 +11,9 @@ (def default-contacts (json->clj (slurp "resources/default_contacts.json"))) (def default-contact-groups (json->clj (slurp "resources/default_contact_groups.json"))) -(def wallet-js (slurp-bot :wallet)) +(def transactor-group-js (slurp-bot :transactor_group)) + +(def transactor-personal-js (slurp-bot :transactor_personal)) (def console-js (slurp-bot :console "web3_metadata.js")) @@ -22,11 +24,12 @@ (def demo-bot-js (slurp-bot :demo_bot)) (def resources - {:wallet-bot wallet-js - :console-bot console-js - :browse-bot browse-js - :mailman-bot mailman-js - :demo-bot demo-bot-js}) + {:transactor-group-bot transactor-group-js + :transactor-personal-bot transactor-personal-js + :console-bot console-js + :browse-bot browse-js + :mailman-bot mailman-js + :demo-bot demo-bot-js}) (defn get-resource [url] (let [resource-name (keyword (subs url (count local-protocol)))]