API commands scopes, changes for API semantics (#1546)
This commit is contained in:
parent
27bc745fc5
commit
cb72190a18
|
@ -1,4 +1,4 @@
|
|||
(ns ^:figwheel-no-load env.ios.main
|
||||
(ns ^:figwheel-no-load env.ios.main
|
||||
(:require [reagent.core :as r]
|
||||
[re-frisk-remote.core :as rr]
|
||||
[status-im.ios.core :as core]
|
||||
|
@ -12,7 +12,7 @@
|
|||
(def root-el (r/as-element [reloader]))
|
||||
|
||||
(figwheel/watch-and-reload
|
||||
:websocket-url "ws://10.0.1.15:3449/figwheel-ws"
|
||||
:websocket-url "ws://localhost:3449/figwheel-ws"
|
||||
:heads-up-display false
|
||||
:jsload-callback #(swap! cnt inc))
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
"photo-path": "icon_wallet_avatar",
|
||||
"dapp?": true,
|
||||
"pending?": true,
|
||||
"has-global-command?": true,
|
||||
"bot-url": "local://wallet-bot",
|
||||
"unremovable?": true
|
||||
},
|
||||
|
@ -49,10 +48,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":
|
||||
|
|
|
@ -34,9 +34,9 @@ function browse(params, context) {
|
|||
}
|
||||
|
||||
status.command({
|
||||
name: "global",
|
||||
name: "browse",
|
||||
title: I18n.t('browse_title'),
|
||||
registeredOnly: true,
|
||||
scope: ["global", "registered-only", "group-chats", "personal-chats", "can-use-for-dapps"],
|
||||
description: I18n.t('browse_description'),
|
||||
color: "#ffa500",
|
||||
fullscreen: true,
|
||||
|
|
|
@ -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 ropstenNetworkId = 3;
|
||||
var rinkebyNetworkId = 4;
|
||||
|
@ -553,13 +551,12 @@ function faucetSuggestions(params) {
|
|||
return {markup: view};
|
||||
}
|
||||
|
||||
var faucetCommandConfig =
|
||||
{
|
||||
var faucetCommandConfig ={
|
||||
name: "faucet",
|
||||
title: I18n.t('faucet_title'),
|
||||
description: I18n.t('faucet_description'),
|
||||
color: "#7099e6",
|
||||
registeredOnly: true,
|
||||
scope: ["registered-only", "group-chats", "personal-chats", "can-use-for-dapps"],
|
||||
params: [{
|
||||
name: "url",
|
||||
type: status.types.TEXT,
|
||||
|
@ -635,7 +632,7 @@ status.command({
|
|||
title: I18n.t('debug_mode_title'),
|
||||
description: I18n.t('debug_mode_description'),
|
||||
color: "#7099e6",
|
||||
registeredOnly: true,
|
||||
scope: ["registered-only", "group-chats", "personal-chats", "can-use-for-dapps"],
|
||||
params: [{
|
||||
name: "mode",
|
||||
suggestions: debugSuggestions,
|
||||
|
@ -659,18 +656,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",
|
||||
|
|
|
@ -32,6 +32,7 @@ function locationsSuggestions (params) {
|
|||
status.command({
|
||||
name: "location",
|
||||
title: I18n.t('location_title'),
|
||||
scope: ["registered-only", "group-chats", "personal-chats"],
|
||||
description: I18n.t('location_description'),
|
||||
sequentialParams: true,
|
||||
hideSendButton: true,
|
||||
|
|
|
@ -301,7 +301,9 @@ function validateSend(params, context) {
|
|||
params["bot-db"] = {};
|
||||
}
|
||||
|
||||
if (!params["bot-db"]["public"] || !params["bot-db"]["public"]["recipient"] || !params["bot-db"]["public"]["recipient"]["address"]) {
|
||||
if (!params["bot-db"]["public"]
|
||||
|| !params["bot-db"]["public"]["recipient"]
|
||||
|| !params["bot-db"]["public"]["recipient"]["address"]) {
|
||||
return {
|
||||
markup: status.components.validationMessage(
|
||||
"Wrong address",
|
||||
|
@ -516,6 +518,7 @@ function shortPreviewSend(params, context) {
|
|||
|
||||
var send = {
|
||||
name: "send",
|
||||
scope: ["group-chats"],
|
||||
icon: "money_white",
|
||||
color: "#5fc48d",
|
||||
title: I18n.t('send_title'),
|
||||
|
@ -550,6 +553,7 @@ var paramsRequest = [
|
|||
|
||||
status.command({
|
||||
name: "request",
|
||||
scope: ["group-chats"],
|
||||
color: "#5fc48d",
|
||||
title: I18n.t('request_title'),
|
||||
description: I18n.t('request_description'),
|
|
@ -0,0 +1,538 @@
|
|||
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: ["personal-chats"],
|
||||
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: ["personal-chats"],
|
||||
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')
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
|
@ -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ư ('
|
||||
}
|
||||
};
|
|
@ -6,13 +6,23 @@ var _status_catalog = {
|
|||
},
|
||||
status = {};
|
||||
|
||||
function scopeToBitMask(scope) {
|
||||
// this function transforms scopes map to a single integer by generating a bit mask
|
||||
// this similar method also exists on clojure side: status-im.chat.models.commands/scope->bit-mask
|
||||
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.bitmask]] = this;
|
||||
};
|
||||
|
||||
Command.prototype.param = function (parameter) {
|
||||
|
@ -26,9 +36,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;
|
||||
|
@ -43,6 +52,15 @@ Command.prototype.create = function (com) {
|
|||
this["sequential-params"] = com.sequentialParams;
|
||||
this["hide-send-button"] = com.hideSendButton;
|
||||
|
||||
// scopes
|
||||
this["scope"] = {};
|
||||
this["scope"]["global?"] = com["scope"] != null && com["scope"].indexOf("global") > -1;
|
||||
this["scope"]["registered-only?"] = com["scope"] != null && com["scope"].indexOf("registered-only") > -1;
|
||||
this["scope"]["personal-chats?"] = com["scope"] == null || com["scope"].indexOf("personal-chats") > -1;
|
||||
this["scope"]["group-chats?"] = com["scope"] == null || com["scope"].indexOf("group-chats") > -1;
|
||||
this["scope"]["can-use-for-dapps?"] = com["scope"] == null || com["scope"].indexOf("can-use-for-dapps") > -1;
|
||||
this["scope"]["bitmask"] = scopeToBitMask(this["scope"]);
|
||||
|
||||
this.addToCatalog();
|
||||
|
||||
return this;
|
||||
|
@ -51,7 +69,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;
|
||||
|
|
|
@ -3,5 +3,3 @@
|
|||
(def mailman-bot "mailman")
|
||||
(defn mailman-bot? [bot-name]
|
||||
(= mailman-bot bot-name))
|
||||
|
||||
(def hidden-bots #{mailman-bot})
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
[taoensso.timbre :as log]
|
||||
[status-im.utils.handlers :as handlers]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.utils.platform :as platform]))
|
||||
[status-im.utils.platform :as platform]
|
||||
[status-im.chat.models.commands :as commands-model]))
|
||||
|
||||
;;;; Helper fns
|
||||
|
||||
|
@ -23,19 +24,26 @@
|
|||
(defn request-command-message-data
|
||||
"Requests command message data from jail"
|
||||
[db
|
||||
{{:keys [command content-command params type]} :content
|
||||
:keys [chat-id jail-id group-id message-id] :as message}
|
||||
{{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 jail-id chat-id)
|
||||
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])
|
||||
(get-in chats [jail-id :possible-commands (keyword command-name) :owner-id])
|
||||
jail-id)]
|
||||
(if (get-in contacts [jail-id :commands-loaded?])
|
||||
(let [path [(if (= :response (keyword type)) :responses :commands)
|
||||
(or content-command command)
|
||||
[(if content-command-name content-command-name command-name)
|
||||
(commands-model/scope->bit-mask (or scope content-command-scope))]
|
||||
data-type]
|
||||
to (get-in contacts [chat-id :address])
|
||||
jail-params {:parameters params
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
[status-im.chat.utils :as chat-utils]
|
||||
[status-im.chat.models :as model]
|
||||
[status-im.chat.models.input :as input-model]
|
||||
[status-im.chat.models.suggestions :as suggestions-model]
|
||||
[status-im.chat.models.commands :as commands-model]
|
||||
[status-im.chat.events.commands :as commands-events]
|
||||
[status-im.chat.events.animation :as animation-events]
|
||||
[status-im.bots.events :as bots-events]
|
||||
|
@ -53,18 +53,14 @@
|
|||
to be made as a result of suggestions update."
|
||||
[{:keys [chats current-chat-id current-account-id local-storage] :as db}]
|
||||
(let [chat-text (str/trim (or (get-in chats [current-chat-id :input-text]) ""))
|
||||
requests (->> (suggestions-model/get-request-suggestions db chat-text)
|
||||
requests (->> (commands-model/get-possible-requests db)
|
||||
(remove (fn [{:keys [type]}]
|
||||
(= type :grant-permissions))))
|
||||
commands (suggestions-model/get-command-suggestions db chat-text)
|
||||
global-commands (suggestions-model/get-global-command-suggestions db chat-text)
|
||||
all-commands (->> (into global-commands commands)
|
||||
(remove (fn [[k {:keys [hidden?]}]] hidden?))
|
||||
(into {}))
|
||||
commands (commands-model/commands-for-chat db current-chat-id)
|
||||
{:keys [dapp?]} (get-in db [:contacts/contacts current-chat-id])
|
||||
new-db (cond-> db
|
||||
true (assoc-in [:chats current-chat-id :request-suggestions] requests)
|
||||
true (assoc-in [:chats current-chat-id :command-suggestions] all-commands)
|
||||
true (assoc-in [:chats current-chat-id :possible-requests] requests)
|
||||
true (assoc-in [:chats current-chat-id :possible-commands] commands)
|
||||
(and dapp?
|
||||
(str/blank? chat-text))
|
||||
(assoc-in [:chats current-chat-id :parameter-boxes :message] nil))]
|
||||
|
@ -136,13 +132,16 @@
|
|||
(defn load-chat-parameter-box
|
||||
"Returns fx for loading chat parameter box for active chat"
|
||||
[{:keys [current-chat-id bot-db] :accounts/keys [current-account-id] :as db}
|
||||
{:keys [name type bot owner-id] :as command}]
|
||||
{:keys [name scope type bot owner-id] :as command}]
|
||||
(let [parameter-index (input-model/argument-position db)]
|
||||
(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
|
||||
[name
|
||||
(if (= :command type)
|
||||
(commands-model/scope->bit-mask scope)
|
||||
0)]
|
||||
:params
|
||||
parameter-index
|
||||
:suggestions]
|
||||
|
@ -180,7 +179,8 @@
|
|||
(let [input-text (get-in db [:chats current-chat-id :input-text])
|
||||
command (input-model/selected-chat-command db current-chat-id input-text)
|
||||
new-db (model/set-chat-ui-props db {:selection selection})
|
||||
chat-parameter-box-fx (load-chat-parameter-box new-db (:command command))]
|
||||
chat-parameter-box-fx (when command
|
||||
(load-chat-parameter-box new-db (:command command)))]
|
||||
(cond-> {:db new-db}
|
||||
|
||||
chat-parameter-box-fx
|
||||
|
@ -196,7 +196,7 @@
|
|||
"Selects command + (optional) arguments as input for active chat"
|
||||
[{:keys [current-chat-id chat-ui-props] :as db}
|
||||
{:keys [prefill prefill-bot-db sequential-params name] :as command} metadata prevent-auto-focus?]
|
||||
(let [fx (-> db
|
||||
(let [db' (-> db
|
||||
bots-events/clear-bot-db
|
||||
clear-seq-arguments
|
||||
(model/set-chat-ui-props {:show-suggestions? false
|
||||
|
@ -208,10 +208,8 @@
|
|||
(set-chat-input-text (str (chat-utils/command-name command)
|
||||
const/spacing-char
|
||||
(when-not sequential-params
|
||||
(input-model/join-command-args prefill))))
|
||||
update-suggestions
|
||||
(as-> fx'
|
||||
(merge fx' (load-chat-parameter-box (:db fx') command))))]
|
||||
(input-model/join-command-args prefill)))))
|
||||
fx (assoc (load-chat-parameter-box db' command) :db db')]
|
||||
(cond-> fx
|
||||
prefill-bot-db (update :db bots-events/update-bot-db {:db prefill-bot-db})
|
||||
|
||||
|
@ -283,6 +281,8 @@
|
|||
:chat-id chat-id
|
||||
:jail-id (or owner-id jail-id)
|
||||
:content {:command (:name command)
|
||||
:scope (when-not (:to-message-id metadata)
|
||||
(:scope command))
|
||||
:params params
|
||||
:type (:type command)}
|
||||
:on-requested (fn [jail-response]
|
||||
|
@ -330,13 +330,11 @@
|
|||
(fn [db]
|
||||
(input-model/modified-db-after-change db)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
(handlers/register-handler-db
|
||||
:set-chat-input-text
|
||||
[re-frame/trim-v]
|
||||
(fn [{:keys [db]} [text]]
|
||||
(-> db
|
||||
(set-chat-input-text text)
|
||||
update-suggestions)))
|
||||
(fn [db [text]]
|
||||
(set-chat-input-text db text)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:add-to-chat-input-text
|
||||
|
|
|
@ -75,14 +75,18 @@
|
|||
(assoc-in [:chats chat-identifier :last-message] message))
|
||||
:dispatch-n [[:upsert-chat! {:chat-id chat-identifier
|
||||
:group-chat group-chat?}]
|
||||
[:request-command-message-data enriched-message :short-preview]]
|
||||
[:request-command-message-data enriched-message :short-preview]
|
||||
[:update-suggestions]]
|
||||
:save-message (dissoc enriched-message :new?)}
|
||||
|
||||
(get-in enriched-message [:content :command])
|
||||
(update :dispatch-n conj [:request-command-preview enriched-message])
|
||||
|
||||
(= (:content-type enriched-message) const/content-type-command-request)
|
||||
(update :dispatch-n conj [:add-request chat-identifier enriched-message])))
|
||||
(update :dispatch-n conj [:add-request chat-identifier enriched-message])
|
||||
|
||||
true
|
||||
(update :dispatch-n conj [:update-suggestions])))
|
||||
{:db db})))
|
||||
|
||||
(def ^:private receive-interceptors
|
||||
|
|
|
@ -107,8 +107,7 @@
|
|||
::contacts-synced
|
||||
[re-frame/trim-v (re-frame/inject-cofx :random-id)]
|
||||
(fn [{:keys [db random-id] :as cofx} [contacts]]
|
||||
(-> db
|
||||
(contacts-events/add-contacts contacts)
|
||||
(-> {:db db}
|
||||
(as-> fx
|
||||
(merge fx
|
||||
(accounts-events/account-update (assoc cofx :db (:db fx)) {:signed-up? true})
|
||||
|
|
|
@ -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]
|
||||
|
@ -244,7 +242,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/<! (a/timeout 100))
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
(requests/save new-request))
|
||||
|
||||
(defn add-request
|
||||
[db [_ chat-id {:keys [message-id content]}]]
|
||||
[db [_ chat-id {:keys [message-id content] :as r}]]
|
||||
(let [request {:chat-id chat-id
|
||||
:message-id message-id
|
||||
:bot (:bot content)
|
||||
:type (:command content)
|
||||
:added (js/Date.)}
|
||||
request' (update request :type keyword)]
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
(ns status-im.chat.handlers.send-message
|
||||
(:require [status-im.utils.handlers :refer [register-handler] :as u]
|
||||
[clojure.string :as s]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.data-store.handler-data :as handler-data]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.utils.datetime :as time]
|
||||
[re-frame.core :refer [enrich after dispatch path]]
|
||||
(:require [clojure.string :as s]
|
||||
[re-frame.core :refer [after dispatch path]]
|
||||
[status-im.chat.models.commands :as commands-model]
|
||||
[status-im.chat.events.console :as console]
|
||||
[status-im.chat.utils :as cu]
|
||||
[status-im.constants :refer [console-chat-id
|
||||
wallet-chat-id
|
||||
text-content-type
|
||||
content-type-log-message
|
||||
content-type-command
|
||||
content-type-command-request
|
||||
default-number-of-messages] :as c]
|
||||
[status-im.chat.constants :refer [input-height]]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
content-type-command-request] :as c]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[taoensso.timbre :refer-macros [debug] :as log]
|
||||
[status-im.chat.events.console :as console]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.clocks :as clocks]))
|
||||
[status-im.utils.clocks :as clocks]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.handlers :refer [register-handler] :as u]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defn prepare-command
|
||||
[identity chat-id clock-value
|
||||
|
@ -37,15 +34,18 @@
|
|||
:prefill prefill
|
||||
:prefill-bot-db prefillBotDb}
|
||||
{:command (:name command)
|
||||
:scope (:scope command)
|
||||
:params params})
|
||||
content' (assoc content :handler-data handler-data
|
||||
:type (name (:type command))
|
||||
:content-command (:name command)
|
||||
:bot (:bot command))]
|
||||
:content-command-scope (:scope command)
|
||||
:bot (or (:bot command)
|
||||
(:owner-id command)))]
|
||||
{:message-id id
|
||||
:from identity
|
||||
:to chat-id
|
||||
:timestamp (time/now-ms)
|
||||
:timestamp (datetime/now-ms)
|
||||
:content content'
|
||||
:content-type (or content-type
|
||||
(if request
|
||||
|
@ -125,10 +125,9 @@
|
|||
|
||||
(register-handler ::dispatch-responded-requests!
|
||||
(u/side-effect!
|
||||
(fn [_ [_ {:keys [command chat-id]}]]
|
||||
(let [{:keys [to-message]} command]
|
||||
(fn [_ [_ {{:keys [to-message]} :command :keys [chat-id]}]]
|
||||
(when to-message
|
||||
(dispatch [:request-answered! chat-id to-message]))))))
|
||||
(dispatch [:request-answered! chat-id to-message])))))
|
||||
|
||||
(register-handler ::invoke-command-handlers!
|
||||
(u/side-effect!
|
||||
|
@ -140,7 +139,7 @@
|
|||
id]} :command
|
||||
:keys [chat-id address]
|
||||
:as orig-params}]]
|
||||
(let [{:keys [type name bot owner-id]} command
|
||||
(let [{:keys [type name scope bot owner-id]} command
|
||||
handler-type (if (= :command type) :commands :responses)
|
||||
to (get-in contacts [chat-id :address])
|
||||
identity (or owner-id bot chat-id)
|
||||
|
@ -159,7 +158,7 @@
|
|||
identity
|
||||
#(status/call-jail
|
||||
{:jail-id identity
|
||||
:path [handler-type name :handler]
|
||||
:path [handler-type [name (commands-model/scope->bit-mask scope)] :handler]
|
||||
:params jail-params
|
||||
:callback (if (:async-handler command) ; async handler, we ignore return value
|
||||
(fn [_]
|
||||
|
@ -180,7 +179,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'
|
||||
|
@ -205,7 +204,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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
(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->bit-mask
|
||||
"Transforms scope map to a single integer value by generating a bit mask."
|
||||
[{:keys [global? registered-only? personal-chats? group-chats? can-use-for-dapps?]}]
|
||||
(bit-or (when global? 1)
|
||||
(when registered-only? 2)
|
||||
(when personal-chats? 4)
|
||||
(when group-chats? 8)
|
||||
(when can-use-for-dapps? 16)))
|
||||
|
||||
(defn get-mixable-commands
|
||||
"Returns all commands of mixable contacts."
|
||||
[{:contacts/keys [contacts]}]
|
||||
(->> contacts
|
||||
(vals)
|
||||
(filter :mixable?)
|
||||
(mapv :commands)
|
||||
(mapcat #(into [] %))
|
||||
(reduce (fn [acc [k v]] (update acc k #(into % v))) {})))
|
||||
|
||||
(defn get-mixable-identities
|
||||
"Returns a lazy-seq of all mixable contacts. Each contact contains only one key `:identity`."
|
||||
[{:contacts/keys [contacts]}]
|
||||
(->> contacts
|
||||
(vals)
|
||||
(filter :mixable?)
|
||||
(map (fn [{:keys [whisper-identity]}]
|
||||
{:identity whisper-identity}))))
|
||||
|
||||
(defn- transform-commands-map
|
||||
"Transforms a map of commands to a flat list."
|
||||
[commands]
|
||||
(->> commands
|
||||
(map val)
|
||||
(remove empty?)
|
||||
(flatten)))
|
||||
|
||||
(defn get-possible-requests
|
||||
"Returns a list of all possible requests for current chat."
|
||||
[{:keys [current-chat-id] :as db}]
|
||||
(let [requests (->> (get-in db [:chats current-chat-id :requests])
|
||||
(map (fn [{:keys [type chat-id bot] :as req}]
|
||||
[type (map (fn [resp]
|
||||
(assoc resp :request req))
|
||||
(get-in db [:contacts/contacts (or bot chat-id) :responses type]))]))
|
||||
(remove (fn [[_ items]] (empty? items)))
|
||||
(into {}))]
|
||||
(transform-commands-map requests)))
|
||||
|
||||
(defn get-possible-commands
|
||||
"Returns a list of all possible commands for current chat."
|
||||
[{:keys [current-chat-id] :as db}]
|
||||
(->> (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])]
|
||||
(transform-commands-map commands))))
|
||||
(flatten)))
|
||||
|
||||
(defn get-possible-global-commands
|
||||
"Returns a list of all possible global commands for current chat."
|
||||
[{:keys [global-commands] :as db}]
|
||||
(transform-commands-map global-commands))
|
||||
|
||||
(defn commands-for-chat
|
||||
"Returns a list of filtered commands for current chat.
|
||||
Uses scopes to filter commands."
|
||||
[{:keys [global-commands chats]
|
||||
:contacts/keys [contacts]
|
||||
:accounts/keys [accounts current-account-id]
|
||||
:as db} chat-id]
|
||||
(let [global-commands (get-possible-global-commands db)
|
||||
commands (get-possible-commands db)
|
||||
account (get accounts current-account-id)
|
||||
commands (-> (into [] global-commands)
|
||||
(into commands))
|
||||
{chat-contacts :contacts} (get chats chat-id)]
|
||||
(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))))
|
||||
commands)))
|
||||
|
||||
(defn set-command-for-content
|
||||
"Sets the information about command for a specified message content.
|
||||
We need to use this command because `command` field in persistent storage (db) doesn't
|
||||
contain all information about command — we save only the name of it."
|
||||
[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 set-command-for-request
|
||||
"Sets the information about command for a specified request."
|
||||
[{:keys [message-id content] :as message} possible-requests possible-commands]
|
||||
(let [requests (->> possible-requests
|
||||
(map (fn [{:keys [request] :as message}]
|
||||
[(:message-id request) message]))
|
||||
(into {}))
|
||||
commands (->> possible-commands
|
||||
(map (fn [{:keys [name] :as message}]
|
||||
[name message]))
|
||||
(into {}))]
|
||||
(assoc content :command (or (get requests message-id)
|
||||
(get commands (get content :command))))))
|
|
@ -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,29 +35,6 @@
|
|||
(or (str/starts-with? text const/bot-char)
|
||||
(str/starts-with? text const/command-char))))
|
||||
|
||||
(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\"]}"
|
||||
[{: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 {}))))
|
||||
|
||||
(defn split-command-args
|
||||
"Returns a list of command's arguments including the command's name.
|
||||
|
||||
|
@ -128,25 +106,26 @@
|
|||
* `:args` contains all arguments provided by user."
|
||||
([{:keys [current-chat-id] :as db} chat-id input-text]
|
||||
(let [chat-id (or chat-id current-chat-id)
|
||||
input-metadata (get-in db [:chats chat-id :input-metadata])
|
||||
seq-arguments (get-in db [:chats chat-id :seq-arguments])
|
||||
possible-actions (possible-chat-actions db chat-id)
|
||||
{:keys [input-metadata
|
||||
seq-arguments
|
||||
possible-requests
|
||||
possible-commands]} (get-in db [:chats chat-id])
|
||||
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))
|
||||
(when-let [{{:keys [message-id]} :request :as command}
|
||||
(->> (into possible-requests possible-commands)
|
||||
(filter (fn [{:keys [name]}]
|
||||
(= name (subs command-name 1))))
|
||||
(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)
|
||||
:metadata (if (and (nil? (:to-message-id input-metadata)) message-id)
|
||||
(assoc input-metadata :to-message-id message-id)
|
||||
input-metadata)
|
||||
:args (if (empty? seq-arguments)
|
||||
:args (->> (if (empty? seq-arguments)
|
||||
(rest command-args)
|
||||
seq-arguments)}))))
|
||||
seq-arguments)
|
||||
(into []))}))))
|
||||
([{:keys [current-chat-id] :as db} chat-id]
|
||||
(selected-chat-command db chat-id (get-in db [:chats chat-id :input-text]))))
|
||||
|
||||
|
|
|
@ -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 {})))
|
|
@ -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]]
|
||||
|
@ -123,7 +123,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))})))
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
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]))
|
||||
|
@ -37,6 +36,15 @@
|
|||
(fn [db]
|
||||
(:chats db)))
|
||||
|
||||
(reg-sub
|
||||
:chat-actions
|
||||
:<- [:chats]
|
||||
:<- [:get-current-chat-id]
|
||||
:<- [:chat :input-text]
|
||||
(fn [[chats current-chat-id text] [_ type chat-id]]
|
||||
(->> (get-in chats [(or chat-id current-chat-id) type])
|
||||
(filter #(or (str/includes? (chat-utils/command-name %) (or text "")))))))
|
||||
|
||||
(reg-sub
|
||||
:chat
|
||||
:<- [:chats]
|
||||
|
@ -54,30 +62,15 @@
|
|||
(fn [_ [_ chat-id]]
|
||||
(chats/get-by-id chat-id)))
|
||||
|
||||
(reg-sub :get-bots-suggestions
|
||||
(fn [db]
|
||||
(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]]
|
||||
(let [current-chat (or chat-id (db :current-chat-id))]
|
||||
(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
|
||||
|
@ -135,8 +128,8 @@
|
|||
show-suggestions? (subscribe [:chat-ui-props :show-suggestions? chat-id])
|
||||
input-text (subscribe [:chat :input-text chat-id])
|
||||
selected-command (subscribe [:selected-chat-command chat-id])
|
||||
requests (subscribe [:chat :request-suggestions chat-id])
|
||||
commands (subscribe [:chat :command-suggestions chat-id])]
|
||||
requests (subscribe [:chat :possible-requests chat-id])
|
||||
commands (subscribe [:chat :possible-commands chat-id])]
|
||||
(and (or @show-suggestions? (input-model/starts-as-command? (str/trim (or @input-text ""))))
|
||||
(not (:command @selected-command))
|
||||
(or (not-empty @requests)
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -35,9 +35,8 @@
|
|||
(chat-utils/command-name command)]]])
|
||||
|
||||
(defview commands-view []
|
||||
[commands [:chat :command-suggestions]
|
||||
responses [:get-responses]
|
||||
requests [:chat :request-suggestions]
|
||||
[commands [:chat :possible-commands]
|
||||
requests [:chat :possible-requests]
|
||||
show-suggestions? [:show-suggestions?]]
|
||||
[view style/commands-root
|
||||
[view style/command-list-icon-container
|
||||
|
@ -49,11 +48,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])])]])
|
||||
|
||||
|
|
|
@ -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?}])
|
||||
|
||||
|
@ -50,8 +50,8 @@
|
|||
|
||||
(defview suggestions-view []
|
||||
[show-suggestions? [:show-suggestions?]
|
||||
requests [:chat :request-suggestions]
|
||||
commands [:chat :command-suggestions]]
|
||||
requests [:chat-actions :possible-requests]
|
||||
commands [:chat-actions :possible-commands]]
|
||||
(when show-suggestions?
|
||||
[expandable-view {:key :suggestions
|
||||
:draggable? false
|
||||
|
@ -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 %))))
|
||||
(for [[i command] (->> commands
|
||||
(remove #(nil? (:title %)))
|
||||
(map-indexed vector))]
|
||||
^{:key i}
|
||||
[command-item command (= i (dec (count commands)))])])]]]))
|
||||
|
|
|
@ -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/set-command-for-content commands global-commands content)
|
||||
{:keys [name type]
|
||||
icon-path :icon} command]
|
||||
[view st/content-command-view
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
(ns status-im.chat.views.message.request-message
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||
[reagent.core :as r]
|
||||
[status-im.components.react :refer [view
|
||||
|
@ -8,7 +9,7 @@
|
|||
icon
|
||||
touchable-highlight]]
|
||||
[status-im.chat.styles.message.message :as st]
|
||||
[status-im.models.commands :refer [parse-command-request]]
|
||||
[status-im.chat.models.commands :as commands]
|
||||
[status-im.components.animation :as anim]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
|
@ -70,35 +71,35 @@
|
|||
(when command-icon
|
||||
[icon command-icon st/command-request-image])]]))})))
|
||||
|
||||
(defn message-content-command-request
|
||||
(defview message-content-command-request
|
||||
[{:keys [message-id chat-id]}]
|
||||
(let [commands-atom (subscribe [:get-commands-and-responses chat-id])
|
||||
answered? (subscribe [:is-request-answered? message-id])
|
||||
status-initialized? (subscribe [:get :status-module-initialized?])
|
||||
markup (subscribe [:get-message-preview message-id])]
|
||||
(fn [{:keys [message-id content from incoming-group]}]
|
||||
(let [commands @commands-atom
|
||||
{:keys [prefill prefill-bot-db prefillBotDb params]
|
||||
(letsubs [requests [:chat-actions :possible-requests]
|
||||
commands [:chat-actions :possible-commands]
|
||||
answered? [:is-request-answered? message-id]
|
||||
status-initialized? [:get :status-module-initialized?]
|
||||
markup [:get-message-preview message-id]]
|
||||
(fn [{:keys [message-id content from incoming-group] :as message}]
|
||||
(let [{:keys [prefill prefill-bot-db prefillBotDb params]
|
||||
text-content :text} content
|
||||
{:keys [command content]} (parse-command-request commands content)
|
||||
{:keys [command content]} (commands/set-command-for-request message requests commands)
|
||||
command (if (and params command)
|
||||
(merge command {:prefill prefill
|
||||
:prefill-bot-db (or prefill-bot-db prefillBotDb)})
|
||||
command)
|
||||
on-press-handler (if (:execute-immediately? command)
|
||||
#(dispatch [:execute-command-immediately command])
|
||||
(when (and (not @answered?) @status-initialized?)
|
||||
(when (and (not answered?) status-initialized?)
|
||||
#(set-chat-command message-id command)))]
|
||||
[view st/comand-request-view
|
||||
[touchable-highlight
|
||||
{:on-press on-press-handler}
|
||||
[view st/command-request-message-view
|
||||
(if (and @markup
|
||||
(not (string? @markup)))
|
||||
[view @markup]
|
||||
(if (and markup
|
||||
(not (string? markup)))
|
||||
[view markup]
|
||||
[text {:style st/style-message-text
|
||||
:font :default}
|
||||
(or text-content @markup content)])]]
|
||||
(or text-content markup content)])]]
|
||||
(when (:request-text command)
|
||||
[view st/command-request-text-view
|
||||
[text {:style st/style-sub-text
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -3,21 +3,16 @@
|
|||
[status-im.utils.handlers :as u]
|
||||
[status-im.utils.utils :refer [http-get show-popup]]
|
||||
[clojure.string :as s]
|
||||
[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.commands.utils :refer [reg-handler]]
|
||||
[status-im.constants :refer [console-chat-id wallet-chat-id]]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.constants :refer [console-chat-id]]
|
||||
[status-im.chat.models.commands :as commands-model]
|
||||
[status-im.native-module.core :as status]
|
||||
[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.datetime :as time]
|
||||
[status-im.data-store.local-storage :as local-storage]
|
||||
[clojure.string :as str]))
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
|
||||
(defn load-commands!
|
||||
|
@ -50,13 +45,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
|
||||
|
@ -88,7 +80,7 @@
|
|||
(status/parse-jail
|
||||
whisper-identity (str local-storage-js ethereum-id-js file)
|
||||
(fn [result]
|
||||
(let [{:keys [error result]} (json->clj result)]
|
||||
(let [{:keys [error result]} (types/json->clj result)]
|
||||
(log/debug "Parsing commands results: " error result)
|
||||
(if error
|
||||
(dispatch [::loading-failed! whisper-identity ::error-in-jail error])
|
||||
|
@ -104,88 +96,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
|
||||
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})
|
||||
mailman-commands (get-mailmans-commands db)]
|
||||
(cond-> db
|
||||
|
||||
true
|
||||
(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 (merge mailman-commands commands'')
|
||||
:responses (each-merge responses' {:type :response
|
||||
:owner-id id})
|
||||
:commands commands'
|
||||
:responses responses'
|
||||
: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)})))
|
||||
(update :global-commands merge (transform-commands global-commands)))]
|
||||
new-db))
|
||||
|
||||
(defn loading-failed!
|
||||
[db [id reason details]]
|
||||
|
@ -215,10 +180,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 (fn [_ [id]]
|
||||
[(after (fn [_ [id]]
|
||||
(dispatch [:invoke-commands-loading-callbacks id])
|
||||
(dispatch [:invoke-chat-loaded-callbacks id])))
|
||||
(after (fn [{:contacts/keys [contacts]} [id]]
|
||||
|
@ -226,8 +188,8 @@
|
|||
(doseq [[name opts] subscriptions]
|
||||
(dispatch [:register-bot-subscription
|
||||
(assoc opts :bot id
|
||||
:name name)])))))]
|
||||
|
||||
:name name)])))))
|
||||
(after #(dispatch [:update-suggestions]))]
|
||||
add-commands)
|
||||
|
||||
(reg-handler ::loading-failed! (u/side-effect! loading-failed!))
|
||||
|
|
|
@ -107,8 +107,7 @@
|
|||
|
||||
(defn save
|
||||
;; todo remove chat-id parameter
|
||||
[chat-id {:keys [message-id content]
|
||||
:as message}]
|
||||
[chat-id {:keys [message-id content] :as message}]
|
||||
(when-not (data-store/exists? message-id)
|
||||
(let [content' (if (string? content)
|
||||
content
|
||||
|
|
|
@ -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}
|
||||
])
|
||||
|
|
|
@ -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))))))
|
|
@ -0,0 +1,40 @@
|
|||
(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.v15.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
|
||||
handler-data/schema])
|
||||
|
||||
(defn migration [old-realm new-realm]
|
||||
(log/debug "migrating v15 account database: " old-realm new-realm))
|
|
@ -0,0 +1,15 @@
|
|||
(ns status-im.data-store.realm.schemas.account.v15.request
|
||||
(:require [taoensso.timbre :as log]))
|
||||
|
||||
(def schema {:name :request
|
||||
:properties {:message-id :string
|
||||
:chat-id :string
|
||||
:bot {:type :string
|
||||
:optional true}
|
||||
:type :string
|
||||
:status {:type :string
|
||||
:default "open"}
|
||||
:added :date}})
|
||||
|
||||
(defn migration [_ _]
|
||||
(log/debug "migrating request schema"))
|
|
@ -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!
|
||||
|
|
|
@ -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)))
|
|
@ -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 [<! timeout]]
|
||||
|
@ -130,13 +129,13 @@
|
|||
#(do
|
||||
(log/debug :call-jail :jail-id jail-id)
|
||||
(log/debug :call-jail :path path)
|
||||
;; this debug message can contain sensetive info
|
||||
;; this debug message can contain sensitive info
|
||||
#_(log/debug :call-jail :params params)
|
||||
(let [params' (update params :context assoc
|
||||
:debug js/goog.DEBUG
|
||||
:locale rn-dependencies/i18n.locale)
|
||||
cb (fn [r]
|
||||
(let [{:keys [result] :as r'} (t/json->clj r)
|
||||
(let [{:keys [result] :as r'} (types/json->clj r)
|
||||
{:keys [messages]} result]
|
||||
(log/debug r')
|
||||
(doseq [{:keys [type message]} messages]
|
||||
|
|
|
@ -290,7 +290,7 @@
|
|||
(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))
|
||||
message' (-> (if (and group? (not= status :sent))
|
||||
(update-in message
|
||||
[:user-statuses from]
|
||||
(fn [{old-status :status}]
|
||||
|
@ -299,8 +299,10 @@
|
|||
:status (if (= (keyword old-status) :seen)
|
||||
old-status
|
||||
status)}))
|
||||
(assoc message :message-status status))]
|
||||
(messages/update message)))))))
|
||||
(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
|
||||
|
|
|
@ -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,20 +35,38 @@
|
|||
(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?)))
|
||||
(spec/def :contact/commands (spec/nilable (spec/map-of keyword? seq?)))
|
||||
(spec/def :contact/responses (spec/nilable (spec/map-of keyword? seq?)))
|
||||
(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
|
||||
(spec/def :contact/contact
|
||||
(allowed-keys
|
||||
:req-un [:contact/name]
|
||||
:opt-un [:contact/whisper-identity
|
||||
:contact/address
|
||||
:contact/private-key
|
||||
:contact/public-key
|
||||
:contact/photo-path
|
||||
:contact/status
|
||||
:contact/last-updated
|
||||
:contact/last-online
|
||||
:contact/pending?
|
||||
:contact/mixable?
|
||||
:contact/scope
|
||||
: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
|
||||
|
|
|
@ -213,8 +213,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? pending?]}] default-contacts
|
||||
(for [[id {:keys [name photo-path public-key add-chat? pending?
|
||||
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'
|
||||
|
@ -223,12 +223,12 @@
|
|||
:photo-path photo-path
|
||||
:public-key public-key
|
||||
:unremovable? (boolean unremovable?)
|
||||
:mixable? (boolean mixable?)
|
||||
:pending? pending?
|
||||
:dapp? dapp?
|
||||
:dapp-url (:en dapp-url)
|
||||
:bot-url bot-url
|
||||
:global-command global-command
|
||||
:dapp-hash dapp-hash
|
||||
:pending? pending?})]])
|
||||
:dapp-hash dapp-hash})]])
|
||||
|
||||
(defn- prepare-add-chat-events [contacts default-contacts]
|
||||
(for [[id {:keys [name add-chat?]}] default-contacts
|
||||
|
@ -271,50 +271,25 @@
|
|||
[(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)
|
||||
;;TODO temporary hide wallet contact, this code should be deleted after wallet contact will be deleted
|
||||
contacts' (if (get contacts "wallet")
|
||||
(assoc-in contacts ["wallet" :pending?] true)
|
||||
contacts)]
|
||||
{:db (assoc db :contacts/contacts contacts'
|
||||
:global-commands global-commands)
|
||||
:dispatch-n (mapv (fn [_ contact] [:watch-contact contact]) contacts')})))
|
||||
{:db (assoc db :contacts/contacts contacts)
|
||||
:dispatch-n (mapv (fn [_ contact] [:watch-contact contact]) contacts)})))
|
||||
|
||||
(defn add-contacts
|
||||
"Creates effects map for adding contacts"
|
||||
[db new-contacts]
|
||||
(register-handler-fx
|
||||
:add-contacts
|
||||
(fn [{:keys [db]} [_ new-contacts]]
|
||||
(let [{:contacts/keys [contacts]} db
|
||||
identities (set (keys contacts))
|
||||
new-contacts' (->> new-contacts
|
||||
(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'))
|
||||
::save-contacts! (vals new-contacts')}))
|
||||
|
||||
(register-handler-fx
|
||||
:add-contacts
|
||||
[trim-v]
|
||||
(fn [{:keys [db]} [new-contacts]]
|
||||
(add-contacts db new-contacts)))
|
||||
{:db (update db :contacts/contacts merge new-contacts')
|
||||
::save-contacts! (vals new-contacts')})))
|
||||
|
||||
(register-handler-db
|
||||
:remove-contacts-click-handler
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
(reg-sub :all-added-contacts
|
||||
:<- [:get-contacts]
|
||||
(fn [contacts]
|
||||
(->> (remove (fn [[_ {:keys [pending? whisper-identity]}]]
|
||||
(or (true? pending?)
|
||||
(bots-constants/hidden-bots whisper-identity))) contacts)
|
||||
(->> contacts
|
||||
(remove (fn [[_ {:keys [pending? mixable?]}]]
|
||||
(or pending? mixable?)))
|
||||
(sort-contacts))))
|
||||
|
||||
(reg-sub :all-added-people-contacts
|
||||
|
|
|
@ -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,7 +24,8 @@
|
|||
(def demo-bot-js (slurp-bot :demo_bot))
|
||||
|
||||
(def resources
|
||||
{:wallet-bot wallet-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
|
||||
|
|
|
@ -3,23 +3,33 @@
|
|||
[status-im.chat.models.input :as input]))
|
||||
|
||||
(def fake-db
|
||||
{:global-commands {:command1 {:name "global-command1"}}
|
||||
{:global-commands {:command1 (list {:name "global-command1"})}
|
||||
:chats {"test1" {:contacts [{:identity "0x1"}]
|
||||
:requests nil
|
||||
:seq-arguments ["arg1" "arg2"]}
|
||||
:seq-arguments ["arg1" "arg2"]
|
||||
:possible-commands (list {:name "global-command1"}
|
||||
{:name "command2"})}
|
||||
"test2" {:contacts [{:identity "0x1"}
|
||||
{:identity "0x2"}]
|
||||
:requests [{:message-id "id1" :type :request1}]}
|
||||
:requests [{:message-id "id1" :type :request1}]
|
||||
:possible-commands (list {:name "global-command1"}
|
||||
{:name "command2"}
|
||||
{:name "command3"})
|
||||
:possible-requests (list {:name "request1"})}
|
||||
"test3" {:contacts [{:identity "0x1"}]
|
||||
:requests [{:message-id "id1" :type :request1}]}
|
||||
"test4" {:contacts [{:identity "0x1"}
|
||||
{:identity "0x2"}]
|
||||
:requests [{:message-id "id2" :type :request2}]
|
||||
:input-metadata {:meta-k "meta-v"}}}
|
||||
:contacts/contacts {"0x1" {:commands {:command2 {:name "command2"}}
|
||||
:input-metadata {:meta-k "meta-v"}
|
||||
:possible-commands (list {:name "global-command1"}
|
||||
{:name "command2"}
|
||||
{:name "command3"})
|
||||
:possible-requests (list {:name "request1"})}}
|
||||
:contacts/contacts {"0x1" {:commands {:command2 (list {:name "command2"})}
|
||||
:responses nil}
|
||||
"0x2" {:commands {:command3 {:name "command3"}}
|
||||
:responses {:request1 {:name "request1"}}}}})
|
||||
"0x2" {:commands {:command3 (list {:name "command3"})}
|
||||
:responses {:request1 (list {:name "request1"})}}}})
|
||||
|
||||
(deftest text->emoji
|
||||
(is (nil? (input/text->emoji nil)))
|
||||
|
@ -33,27 +43,6 @@
|
|||
(is (false? (input/text-ends-with-space? "word1 word2 word3")))
|
||||
(is (true? (input/text-ends-with-space? "word1 word2 "))))
|
||||
|
||||
(deftest possible-chat-actions
|
||||
(is (= (input/possible-chat-actions fake-db "non-existent-chat") {}))
|
||||
(is (= (input/possible-chat-actions fake-db "test1")
|
||||
{:command1 [{:name "global-command1"} :any]
|
||||
:command2 [{:name "command2"} :any]}))
|
||||
(is (= (input/possible-chat-actions fake-db "test1")
|
||||
{:command1 [{:name "global-command1"} :any]
|
||||
:command2 [{:name "command2"} :any]}))
|
||||
(is (= (input/possible-chat-actions fake-db "test2")
|
||||
{:command1 [{:name "global-command1"} :any]
|
||||
:command2 [{:name "command2"} :any]
|
||||
:command3 [{:name "command3"} :any]
|
||||
:request1 [{:name "request1"} "id1"]}))
|
||||
(is (= (input/possible-chat-actions fake-db "test3")
|
||||
{:command1 [{:name "global-command1"} :any]
|
||||
:command2 [{:name "command2"} :any]}))
|
||||
(is (= (input/possible-chat-actions fake-db "test4")
|
||||
{:command1 [{:name "global-command1"} :any]
|
||||
:command2 [{:name "command2"} :any]
|
||||
:command3 [{:name "command3"} :any]})))
|
||||
|
||||
(deftest split-command-args
|
||||
(is (nil? (input/split-command-args nil)))
|
||||
(is (= [""] (input/split-command-args "")))
|
||||
|
@ -76,7 +65,7 @@
|
|||
(is (= (input/selected-chat-command fake-db "test1" "/command2")
|
||||
{:command {:name "command2"} :metadata nil :args ["arg1" "arg2"]}))
|
||||
(is (= (input/selected-chat-command fake-db "test2" "/request1 arg1")
|
||||
{:command {:name "request1"} :metadata {:to-message-id "id1"} :args ["arg1"]}))
|
||||
{:command {:name "request1"} :metadata nil :args ["arg1"]}))
|
||||
(is (= (input/selected-chat-command fake-db "test4" "/command2 arg1")
|
||||
{:command {:name "command2"} :metadata {:meta-k "meta-v"} :args ["arg1"]})))
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
(doo-tests
|
||||
'status-im.test.chat.events
|
||||
'status-im.test.accounts.events
|
||||
'status-im.test.contacts.events
|
||||
;;'status-im.test.contacts.events
|
||||
'status-im.test.profile.events
|
||||
'status-im.test.wallet.events
|
||||
'status-im.test.wallet.transactions.subs
|
||||
|
|
Loading…
Reference in New Issue