2020-06-22 15:51:15 +00:00
|
|
|
pragma Singleton
|
|
|
|
|
|
|
|
import QtQuick 2.13
|
2022-04-07 19:02:54 +00:00
|
|
|
|
2021-10-27 21:27:49 +00:00
|
|
|
import shared 1.0
|
2022-04-07 19:02:54 +00:00
|
|
|
import StatusQ.Core.Theme 0.1
|
2022-07-05 10:12:27 +00:00
|
|
|
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
2020-06-22 15:51:15 +00:00
|
|
|
|
|
|
|
QtObject {
|
2022-04-07 23:58:01 +00:00
|
|
|
property var globalUtilsInst: globalUtils
|
|
|
|
|
2022-07-21 11:29:18 +00:00
|
|
|
function isDigit(value) {
|
|
|
|
return /^\d$/.test(value);
|
|
|
|
}
|
|
|
|
|
2020-06-22 15:51:15 +00:00
|
|
|
function isHex(value) {
|
2020-08-12 03:40:25 +00:00
|
|
|
return /^(-0x|0x)?[0-9a-fA-F]*$/i.test(value)
|
2020-06-22 15:51:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function startsWith0x(value) {
|
|
|
|
return value.startsWith('0x')
|
|
|
|
}
|
|
|
|
|
|
|
|
function isChatKey(value) {
|
2022-04-07 23:58:01 +00:00
|
|
|
return (startsWith0x(value) && isHex(value) && value.length === 132) || globalUtilsInst.isCompressedPubKey(value)
|
2020-06-22 15:51:15 +00:00
|
|
|
}
|
2020-06-22 17:26:47 +00:00
|
|
|
|
2020-06-25 13:26:58 +00:00
|
|
|
function isValidETHNamePrefix(value) {
|
|
|
|
return !(value.trim() === "" || value.endsWith(".") || value.indexOf("..") > -1)
|
|
|
|
}
|
|
|
|
|
2020-06-22 17:26:47 +00:00
|
|
|
function isAddress(value) {
|
|
|
|
return startsWith0x(value) && isHex(value) && value.length === 42
|
|
|
|
}
|
|
|
|
|
|
|
|
function isPrivateKey(value) {
|
|
|
|
return isHex(value) && ((startsWith0x(value) && value.length === 66) ||
|
|
|
|
(!startsWith0x(value) && value.length === 64))
|
|
|
|
}
|
2020-06-22 17:57:06 +00:00
|
|
|
|
2021-03-16 19:19:48 +00:00
|
|
|
function getCurrentThemeAccountColor(color) {
|
|
|
|
const upperCaseColor = color.toUpperCase()
|
|
|
|
if (Style.current.accountColors.indexOf(upperCaseColor) > -1) {
|
|
|
|
return upperCaseColor
|
|
|
|
}
|
|
|
|
|
|
|
|
let colorIndex
|
|
|
|
if (Style.current.name === Constants.lightThemeName) {
|
|
|
|
colorIndex = Style.darkTheme.accountColors.indexOf(upperCaseColor)
|
|
|
|
} else {
|
|
|
|
colorIndex = Style.lightTheme.accountColors.indexOf(upperCaseColor)
|
|
|
|
}
|
|
|
|
if (colorIndex === -1) {
|
|
|
|
// Unknown color
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return Style.current.accountColors[colorIndex]
|
|
|
|
}
|
|
|
|
|
2021-12-10 11:29:33 +00:00
|
|
|
function getReplyMessageStyle(msg, isCurrentUser) {
|
2021-06-11 19:50:52 +00:00
|
|
|
return `<style type="text/css">`+
|
2021-08-24 14:14:19 +00:00
|
|
|
`a {`+
|
2022-05-30 11:20:21 +00:00
|
|
|
`color: ${Style.current.textColor};`+
|
2021-08-24 14:14:19 +00:00
|
|
|
`}`+
|
|
|
|
`a.mention {`+
|
|
|
|
`color: ${isCurrentUser ? Style.current.mentionColor : Style.current.turquoise};`+
|
|
|
|
`background-color: ${Style.current.mentionBgColor};` +
|
|
|
|
`}`+
|
|
|
|
`</style>`+
|
|
|
|
`</head>`+
|
|
|
|
`<body>`+
|
|
|
|
`${msg}`+
|
|
|
|
`</body>`+
|
|
|
|
`</html>`
|
2021-06-11 19:50:52 +00:00
|
|
|
}
|
|
|
|
|
2021-10-21 09:15:33 +00:00
|
|
|
function getLinkStyle(link, hoveredLink, textColor) {
|
2021-10-01 15:58:36 +00:00
|
|
|
return `<style type="text/css">` +
|
|
|
|
`a {` +
|
2021-10-21 09:15:33 +00:00
|
|
|
`color: ${textColor};` +
|
2021-10-01 15:58:36 +00:00
|
|
|
`text-decoration: none;` +
|
|
|
|
`}` +
|
|
|
|
(hoveredLink !== "" ? `a[href="${hoveredLink}"] { text-decoration: underline; }` : "") +
|
|
|
|
`</style>` +
|
|
|
|
`<a href="${link}">${link}</a>`
|
|
|
|
}
|
|
|
|
|
2020-06-22 17:57:06 +00:00
|
|
|
function isMnemonic(value) {
|
2020-07-27 15:11:24 +00:00
|
|
|
if(!value.match(/^([a-z\s]+)$/)){
|
|
|
|
return false;
|
|
|
|
}
|
2021-04-12 15:40:49 +00:00
|
|
|
return Utils.seedPhraseValidWordCount(value);
|
2020-06-22 17:57:06 +00:00
|
|
|
}
|
2020-06-25 13:26:58 +00:00
|
|
|
|
|
|
|
function compactAddress(addr, numberOfChars) {
|
|
|
|
if(addr.length <= 5 + (numberOfChars * 2)){ // 5 represents these chars 0x...
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
return addr.substring(0, 2 + numberOfChars) + "..." + addr.substring(addr.length - numberOfChars);
|
|
|
|
}
|
2020-07-15 21:04:14 +00:00
|
|
|
|
2020-09-16 11:11:31 +00:00
|
|
|
function toLocaleString(val, locale, options) {
|
|
|
|
return NumberPolyFill.toLocaleString(val, locale, options)
|
|
|
|
}
|
|
|
|
|
2020-07-27 17:30:20 +00:00
|
|
|
function isOnlyEmoji(inputText) {
|
|
|
|
var emoji_regex = /^(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c[\ude32-\ude3a]|[\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff]|\s)+$/;
|
|
|
|
return emoji_regex.test(inputText);
|
2020-08-07 19:26:51 +00:00
|
|
|
}
|
2020-07-27 17:30:20 +00:00
|
|
|
|
2020-08-07 19:26:51 +00:00
|
|
|
function removeStatusEns(userName){
|
|
|
|
return userName.endsWith(".stateofus.eth") ? userName.substr(0, userName.length - 14) : userName
|
2020-07-27 17:30:20 +00:00
|
|
|
}
|
2020-08-12 03:40:25 +00:00
|
|
|
|
2021-08-10 18:01:37 +00:00
|
|
|
function addStatusEns(userName){
|
|
|
|
return userName.endsWith(".eth") ? userName : userName + ".stateofus.eth"
|
|
|
|
}
|
|
|
|
|
2020-08-12 03:40:25 +00:00
|
|
|
function isValidAddress(inputValue) {
|
2020-11-04 12:37:53 +00:00
|
|
|
return inputValue !== "0x" && /^0x[a-fA-F0-9]{40}$/.test(inputValue)
|
2020-08-12 03:40:25 +00:00
|
|
|
}
|
2020-08-13 08:24:51 +00:00
|
|
|
|
2020-09-09 11:04:01 +00:00
|
|
|
function isValidEns(inputValue) {
|
2020-11-04 12:37:53 +00:00
|
|
|
if (!inputValue) {
|
|
|
|
return false
|
|
|
|
}
|
2020-09-09 11:04:01 +00:00
|
|
|
const isEmail = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(inputValue)
|
|
|
|
const isDomain = /(?:(?:(?<thld>[\w\-]*)(?:\.))?(?<sld>[\w\-]*))\.(?<tld>[\w\-]*)/.test(inputValue)
|
2020-11-04 12:37:53 +00:00
|
|
|
return isEmail || isDomain || (inputValue.startsWith("@") && inputValue.length > 1)
|
2020-09-09 11:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-13 08:24:51 +00:00
|
|
|
/**
|
|
|
|
* Removes trailing zeros from a string-representation of a number. Throws
|
|
|
|
* if parameter is not a string
|
|
|
|
*/
|
|
|
|
function stripTrailingZeros(strNumber) {
|
|
|
|
if (!(typeof strNumber === "string")) {
|
2020-09-01 03:49:05 +00:00
|
|
|
try {
|
|
|
|
strNumber = strNumber.toString()
|
|
|
|
} catch(e) {
|
|
|
|
throw "[Utils.stripTrailingZeros] input parameter must be a string"
|
|
|
|
}
|
2020-08-13 08:24:51 +00:00
|
|
|
}
|
|
|
|
return strNumber.replace(/(\.[0-9]*[1-9])0+$|\.0*$/,'$1')
|
|
|
|
}
|
2020-08-17 20:46:13 +00:00
|
|
|
|
2020-09-08 21:24:12 +00:00
|
|
|
/**
|
|
|
|
* Removes starting zeros from a string-representation of a number. Throws
|
|
|
|
* if parameter is not a string
|
|
|
|
*/
|
|
|
|
function stripStartingZeros(strNumber) {
|
|
|
|
if (!(typeof strNumber === "string")) {
|
|
|
|
try {
|
|
|
|
strNumber = strNumber.toString()
|
|
|
|
} catch(e) {
|
|
|
|
throw "[Utils.stripStartingZeros] input parameter must be a string"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strNumber.replace(/^(0*)([0-9\.]+)/, "$2")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-17 20:46:13 +00:00
|
|
|
function setColorAlpha(color, alpha) {
|
|
|
|
return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
|
|
|
|
}
|
2020-08-26 15:15:40 +00:00
|
|
|
|
2022-05-18 08:49:48 +00:00
|
|
|
function checkTimestamp(value, errorLocation) {
|
|
|
|
if(Number.isInteger(value) && value > 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-01 10:30:55 +00:00
|
|
|
function formatTime(value, is24hTimeFormat) {
|
|
|
|
const format24h = "hh:mm:ss t"
|
|
|
|
const format12h = "h:mm:ss AP t"
|
|
|
|
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
|
|
|
|
|
2022-05-18 08:49:48 +00:00
|
|
|
return checkTimestamp(value, "formatTime") ? Qt.formatTime(new Date(value), currentTimeFormat) :
|
|
|
|
Qt.formatTime(new Date(), currentTimeFormat)
|
2020-08-26 15:15:40 +00:00
|
|
|
}
|
2020-09-01 03:49:05 +00:00
|
|
|
|
2022-04-01 10:30:55 +00:00
|
|
|
function formatShortTime(value, is24hTimeFormat) {
|
|
|
|
const format24h = "hh:mm"
|
|
|
|
const format12h = "h:mm AP"
|
|
|
|
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
|
|
|
|
|
2022-05-18 08:49:48 +00:00
|
|
|
return checkTimestamp(value, "formatShortTime") ? Qt.formatTime(new Date(value), currentTimeFormat) :
|
|
|
|
Qt.formatTime(new Date(), currentTimeFormat)
|
2021-07-15 14:20:17 +00:00
|
|
|
}
|
|
|
|
|
2021-01-19 20:16:42 +00:00
|
|
|
function formatShortDateStr(longStr) {
|
|
|
|
const dmKeys = {
|
|
|
|
// Days
|
2022-04-04 11:26:30 +00:00
|
|
|
Sunday: qsTr("Sun"),
|
|
|
|
Monday: qsTr("Mon"),
|
|
|
|
Tuesday: qsTr("Tue"),
|
|
|
|
Wednesday: qsTr("Wed"),
|
|
|
|
Thursday: qsTr("Thu"),
|
|
|
|
Friday: qsTr("Fri"),
|
|
|
|
Saturday: qsTr("Sat"),
|
2021-01-19 20:16:42 +00:00
|
|
|
// Months
|
2022-04-04 11:26:30 +00:00
|
|
|
January: qsTr("Jan"),
|
|
|
|
February: qsTr("Feb"),
|
|
|
|
March: qsTr("Mar"),
|
|
|
|
April: qsTr("Apr"),
|
|
|
|
May: qsTr("May"),
|
|
|
|
June: qsTr("Jun"),
|
|
|
|
July: qsTr("Jul"),
|
|
|
|
August: qsTr("Aug"),
|
|
|
|
September: qsTr("Sep"),
|
|
|
|
October: qsTr("Oct"),
|
|
|
|
November: qsTr("Nov"),
|
|
|
|
December: qsTr("Dec")
|
2021-01-19 20:16:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let shortStr = longStr;
|
|
|
|
for (const [key, value] of Object.entries(dmKeys)) {
|
|
|
|
shortStr = shortStr.replace(key, value);
|
|
|
|
shortStr = shortStr.replace(key.toLowerCase(), value);
|
|
|
|
shortStr = shortStr.replace(key.toUpperCase(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return shortStr;
|
|
|
|
}
|
|
|
|
|
2022-04-01 10:30:55 +00:00
|
|
|
function formatLongDate(value, isDDMMYYDateFormat) {
|
|
|
|
const formatDDMMYY = "dddd d MMMM yyyy"
|
|
|
|
const formatMMDDYY = "dddd, MMMM d, yyyy"
|
|
|
|
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
|
2022-05-18 08:49:48 +00:00
|
|
|
return checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), currentFormat) :
|
|
|
|
Qt.formatDate(new Date(), currentFormat)
|
2022-04-01 10:30:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function formatLongDateTime(value, isDDMMYYDateFormat, is24hTimeFormat) {
|
|
|
|
const formatDDMMYY = "dddd d MMMM yyyy"
|
|
|
|
const formatMMDDYY = "dddd, MMMM d, yyyy"
|
|
|
|
const format24h = "hh:mm:ss t"
|
|
|
|
const format12h = "h:mm:ss AP t"
|
|
|
|
const currentDateFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
|
|
|
|
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
|
2022-05-18 08:49:48 +00:00
|
|
|
return checkTimestamp(value, "formatLongDateTime") ? Qt.formatDateTime(new Date(value), currentDateFormat + " " + currentTimeFormat) :
|
|
|
|
Qt.formatDateTime(new Date(), currentDateFormat + " " + currentTimeFormat)
|
2022-04-01 10:30:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// WARN: It is not used!! TO BE REMOVE??
|
2021-01-04 14:47:25 +00:00
|
|
|
function formatDateTime(timestamp, locale) {
|
2020-12-22 10:22:04 +00:00
|
|
|
let now = new Date()
|
|
|
|
let yesterday = new Date()
|
|
|
|
yesterday.setDate(now.getDate()-1)
|
|
|
|
let messageDate = new Date(Math.floor(timestamp))
|
|
|
|
let lastWeek = new Date()
|
|
|
|
lastWeek.setDate(now.getDate()-7)
|
|
|
|
|
|
|
|
let minutes = messageDate.getMinutes();
|
|
|
|
let hours = messageDate.getHours();
|
|
|
|
|
|
|
|
if (now.toDateString() === messageDate.toDateString()) {
|
|
|
|
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes)
|
|
|
|
} else if (yesterday.toDateString() === messageDate.toDateString()) {
|
2022-04-04 11:26:30 +00:00
|
|
|
return qsTr("Yesterday")
|
2020-12-22 10:22:04 +00:00
|
|
|
} else if (lastWeek.getTime() < messageDate.getTime()) {
|
2022-04-04 11:26:30 +00:00
|
|
|
let days = [qsTr("Sunday"),
|
|
|
|
qsTr("Monday"),
|
|
|
|
qsTr("Tuesday"),
|
|
|
|
qsTr("Wednesday"),
|
|
|
|
qsTr("Thursday"),
|
|
|
|
qsTr("Friday"),
|
|
|
|
qsTr("Saturday")];
|
2020-12-22 10:22:04 +00:00
|
|
|
return days[messageDate.getDay()];
|
|
|
|
} else {
|
2021-01-19 20:16:42 +00:00
|
|
|
return formatShortDateStr(new Date().toLocaleDateString(Qt.locale(locale)))
|
2020-12-22 10:22:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-01 10:30:55 +00:00
|
|
|
// WARN: It is not used!! TO BE REMOVE??
|
|
|
|
function formatAgeFromTime(timestamp, epoch) {
|
|
|
|
epoch++ // pretending the parameter is not unused
|
|
|
|
const now = new Date()
|
|
|
|
const messageDate = new Date(Math.floor(timestamp))
|
|
|
|
const diffMs = now - messageDate
|
|
|
|
const diffMin = Math.floor(diffMs / 60000)
|
|
|
|
if (diffMin < 1) {
|
2022-04-04 11:26:30 +00:00
|
|
|
return qsTr("NOW")
|
2022-04-01 10:30:55 +00:00
|
|
|
}
|
|
|
|
const diffHour = Math.floor(diffMin / 60)
|
|
|
|
if (diffHour < 1) {
|
2022-04-04 11:26:30 +00:00
|
|
|
return qsTr("%1M").arg(diffMin)
|
2022-04-01 10:30:55 +00:00
|
|
|
}
|
|
|
|
const diffDay = Math.floor(diffHour / 24)
|
|
|
|
if (diffDay < 1) {
|
2022-04-04 11:26:30 +00:00
|
|
|
return qsTr("%1H").arg(diffHour)
|
2022-04-01 10:30:55 +00:00
|
|
|
}
|
2022-04-04 11:26:30 +00:00
|
|
|
return qsTr("%1D").arg(diffDay)
|
2022-04-01 10:30:55 +00:00
|
|
|
}
|
|
|
|
|
2022-09-01 15:34:27 +00:00
|
|
|
function formatShortDate(value, isDDMMYYDateFormat) {
|
|
|
|
const formatDDMMYY = "d MMMM yyyy"
|
|
|
|
const formatMMDDYY = "MMMM d yyyy"
|
|
|
|
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
|
|
|
|
var timeStamp = checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), currentFormat) :
|
|
|
|
Qt.formatDate(new Date(), currentFormat)
|
|
|
|
return formatShortDateStr(timeStamp)
|
|
|
|
}
|
|
|
|
|
2022-07-01 11:24:32 +00:00
|
|
|
// To-do move to Wallet Store, this should not be under Utils.
|
2022-05-19 08:53:57 +00:00
|
|
|
function findAssetByChainAndSymbol(chainIdToFind, assets, symbolToFind) {
|
2020-09-01 03:49:05 +00:00
|
|
|
for(var i=0; i<assets.rowCount(); i++) {
|
|
|
|
const symbol = assets.rowData(i, "symbol")
|
2022-05-19 08:53:57 +00:00
|
|
|
if (symbol.toLowerCase() === symbolToFind.toLowerCase() && assets.hasChain(i, parseInt(chainIdToFind))) {
|
2020-09-01 03:49:05 +00:00
|
|
|
return {
|
|
|
|
name: assets.rowData(i, "name"),
|
|
|
|
symbol,
|
2022-05-19 08:53:57 +00:00
|
|
|
totalBalance: assets.rowData(i, "totalBalance"),
|
|
|
|
totalCurrencyBalance: assets.rowData(i, "totalCurrencyBalance"),
|
|
|
|
fiatBalance: assets.rowData(i, "totalCurrencyBalance"),
|
|
|
|
chainId: chainIdToFind,
|
2020-09-01 03:49:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-24 21:41:59 +00:00
|
|
|
|
2020-11-06 19:46:21 +00:00
|
|
|
function isValidChannelName(channelName) {
|
|
|
|
return (/^[a-z0-9\-]+$/.test(channelName))
|
|
|
|
}
|
|
|
|
|
2020-10-29 15:19:27 +00:00
|
|
|
function isURL(text) {
|
2021-04-29 19:51:16 +00:00
|
|
|
return (/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}(\.[a-zA-Z0-9()]{1,6})?\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/.test(text))
|
2020-10-29 15:19:27 +00:00
|
|
|
}
|
|
|
|
|
2021-04-19 10:34:03 +00:00
|
|
|
function isURLWithOptionalProtocol(text) {
|
|
|
|
return (/^(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/.test(text))
|
|
|
|
}
|
|
|
|
|
2020-12-11 20:29:46 +00:00
|
|
|
function isHexColor(c) {
|
2021-01-06 14:58:44 +00:00
|
|
|
return (/^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$/i.test(c))
|
2020-12-11 20:29:46 +00:00
|
|
|
}
|
|
|
|
|
2020-09-28 15:23:04 +00:00
|
|
|
function isSpace(c) {
|
|
|
|
return (/( |\t|\n|\r)/.test(c))
|
2020-09-24 21:41:59 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 13:53:46 +00:00
|
|
|
function getTick(wordCount) {
|
|
|
|
return (wordCount === 12 || wordCount === 15 ||
|
|
|
|
wordCount === 18 || wordCount === 21 || wordCount === 24)
|
|
|
|
? "✓ " : "";
|
|
|
|
}
|
|
|
|
|
2021-04-12 15:40:49 +00:00
|
|
|
function isValidNumberOfWords(wordCount) {
|
|
|
|
return !!getTick(wordCount);
|
|
|
|
}
|
|
|
|
|
2020-10-03 13:53:46 +00:00
|
|
|
function countWords(text) {
|
|
|
|
if (text.trim() === "")
|
|
|
|
return 0;
|
2021-04-12 15:40:49 +00:00
|
|
|
return text.trim().replace(/ +/g, " ").split(" ").length;
|
|
|
|
}
|
|
|
|
|
|
|
|
function seedPhraseValidWordCount(text) {
|
|
|
|
return isValidNumberOfWords(countWords(text))
|
2020-10-03 13:53:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns text in the format "✓ 12 words" for seed phrases input boxes
|
|
|
|
*/
|
|
|
|
function seedPhraseWordCountText(text) {
|
|
|
|
let wordCount = countWords(text);
|
2022-04-04 11:26:30 +00:00
|
|
|
return getTick(wordCount) + wordCount.toString() + " " + qsTr("words")
|
2020-10-03 13:53:46 +00:00
|
|
|
}
|
2020-10-07 02:47:21 +00:00
|
|
|
|
|
|
|
function uuid() {
|
|
|
|
return Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
|
|
|
|
}
|
2020-11-23 20:41:57 +00:00
|
|
|
|
2020-12-03 22:26:05 +00:00
|
|
|
|
2020-12-04 14:53:00 +00:00
|
|
|
function validatePasswords(item, firstPasswordField, repeatPasswordField) {
|
2020-12-03 22:26:05 +00:00
|
|
|
switch (item) {
|
|
|
|
case "first":
|
|
|
|
if (firstPasswordField.text === "") {
|
2022-04-04 11:26:30 +00:00
|
|
|
return [false, qsTr("You need to enter a password")];
|
2020-12-03 22:26:05 +00:00
|
|
|
} else if (firstPasswordField.text.length < 6) {
|
2022-04-04 11:26:30 +00:00
|
|
|
return [false, qsTr("Password needs to be 6 characters or more")];
|
2020-12-03 22:26:05 +00:00
|
|
|
}
|
|
|
|
return [true, ""];
|
|
|
|
|
|
|
|
case "repeat":
|
|
|
|
if (repeatPasswordField.text === "") {
|
2022-04-04 11:26:30 +00:00
|
|
|
return [false, qsTr("You need to repeat your password")];
|
2020-12-03 22:26:05 +00:00
|
|
|
} else if (repeatPasswordField.text !== firstPasswordField.text) {
|
2022-04-04 11:26:30 +00:00
|
|
|
return [false, qsTr("Passwords don't match")];
|
2020-12-03 22:26:05 +00:00
|
|
|
}
|
|
|
|
return [true, ""];
|
|
|
|
|
|
|
|
default:
|
|
|
|
return [false, ""];
|
|
|
|
}
|
|
|
|
}
|
feat: whitelist gifs (no url extension needed)
Fixes #1377.
Fixes #1479.
Two sites have been added to the whitelist: giphy.com and tenor.com.
`imageUrls` in its entirety has been removed and instead all links are being handle through the message `linkUrls`. This prevents double-handling of urls that may or may not be images.
The logic to automatically show links previews works like this:
1. If the setting "display chat images" is enabled, all links that *contain* ".png", ".jpg", ".jpeg", ".svg", ".gif" will be automatically shown. If the URL doesn't contain the extension, we are not downloading it. This was meant to be somewhat of a security compromise as we do not want to download each and every link posted in a message just to find out its true content type.
2. If the above setting is *disabled*, then we follow the whitelist settings for tenor and giphy. This allows us to preview gifs that do not have a file extension in their url.
feat: bump status-go to the commit that supports the new whitelist (https://github.com/status-im/status-go/pull/2094), and also lets us get link preview data from urls in the whitelist. NOTE: this commit was branched off status-go `develop`, so once it is merged, and we update this PR to the new commit, we will effectively be getting status-go develop changes. We *could* base that status-go PR off of master if it makes things easier.
fix: height on settings update issue
feat: move date/time of message below links
fix: layout issues when changing setting `neverAskAboutUnfurlingAgain`
feat: Add MessageBorder component to aid in showing rounded corners with different radius
2020-12-11 00:53:44 +00:00
|
|
|
|
2021-09-24 12:03:57 +00:00
|
|
|
function validatePINs(item, firstPINField, repeatPINField) {
|
|
|
|
switch (item) {
|
|
|
|
case "first":
|
2022-09-26 11:57:32 +00:00
|
|
|
if (firstPINField.pinInput === "") {
|
2021-09-24 12:03:57 +00:00
|
|
|
return [false, qsTr("You need to enter a PIN")];
|
2022-09-26 11:57:32 +00:00
|
|
|
} else if (!/^\d+$/.test(firstPINField.pinInput)) {
|
2021-09-24 12:03:57 +00:00
|
|
|
return [false, qsTr("The PIN must contain only digits")];
|
2022-09-26 11:57:32 +00:00
|
|
|
} else if (firstPINField.pinInput.length != Constants.keycard.general.keycardPinLength) {
|
|
|
|
return [false, qsTr("The PIN must be exactly %1 digits").arg(Constants.keycard.general.keycardPinLength)];
|
2021-09-24 12:03:57 +00:00
|
|
|
}
|
|
|
|
return [true, ""];
|
|
|
|
|
|
|
|
case "repeat":
|
2022-09-26 11:57:32 +00:00
|
|
|
if (repeatPINField.pinInput === "") {
|
2021-09-24 12:03:57 +00:00
|
|
|
return [false, qsTr("You need to repeat your PIN")];
|
2022-09-26 11:57:32 +00:00
|
|
|
} else if (repeatPINField.pinInput !== firstPINField.pinInput) {
|
2021-09-24 12:03:57 +00:00
|
|
|
return [false, qsTr("PIN don't match")];
|
|
|
|
}
|
|
|
|
return [true, ""];
|
|
|
|
|
|
|
|
default:
|
|
|
|
return [false, ""];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
feat: whitelist gifs (no url extension needed)
Fixes #1377.
Fixes #1479.
Two sites have been added to the whitelist: giphy.com and tenor.com.
`imageUrls` in its entirety has been removed and instead all links are being handle through the message `linkUrls`. This prevents double-handling of urls that may or may not be images.
The logic to automatically show links previews works like this:
1. If the setting "display chat images" is enabled, all links that *contain* ".png", ".jpg", ".jpeg", ".svg", ".gif" will be automatically shown. If the URL doesn't contain the extension, we are not downloading it. This was meant to be somewhat of a security compromise as we do not want to download each and every link posted in a message just to find out its true content type.
2. If the above setting is *disabled*, then we follow the whitelist settings for tenor and giphy. This allows us to preview gifs that do not have a file extension in their url.
feat: bump status-go to the commit that supports the new whitelist (https://github.com/status-im/status-go/pull/2094), and also lets us get link preview data from urls in the whitelist. NOTE: this commit was branched off status-go `develop`, so once it is merged, and we update this PR to the new commit, we will effectively be getting status-go develop changes. We *could* base that status-go PR off of master if it makes things easier.
fix: height on settings update issue
feat: move date/time of message below links
fix: layout issues when changing setting `neverAskAboutUnfurlingAgain`
feat: Add MessageBorder component to aid in showing rounded corners with different radius
2020-12-11 00:53:44 +00:00
|
|
|
function getHostname(url) {
|
|
|
|
const rgx = /\:\/\/(?:[a-zA-Z0-9\-]*\.{1,}){1,}[a-zA-Z0-9]*/i
|
|
|
|
const matches = rgx.exec(url)
|
2021-02-25 19:32:39 +00:00
|
|
|
if (!matches || !matches.length) {
|
|
|
|
if (url.includes(Constants.deepLinkPrefix)) {
|
|
|
|
return Constants.deepLinkPrefix
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
feat: whitelist gifs (no url extension needed)
Fixes #1377.
Fixes #1479.
Two sites have been added to the whitelist: giphy.com and tenor.com.
`imageUrls` in its entirety has been removed and instead all links are being handle through the message `linkUrls`. This prevents double-handling of urls that may or may not be images.
The logic to automatically show links previews works like this:
1. If the setting "display chat images" is enabled, all links that *contain* ".png", ".jpg", ".jpeg", ".svg", ".gif" will be automatically shown. If the URL doesn't contain the extension, we are not downloading it. This was meant to be somewhat of a security compromise as we do not want to download each and every link posted in a message just to find out its true content type.
2. If the above setting is *disabled*, then we follow the whitelist settings for tenor and giphy. This allows us to preview gifs that do not have a file extension in their url.
feat: bump status-go to the commit that supports the new whitelist (https://github.com/status-im/status-go/pull/2094), and also lets us get link preview data from urls in the whitelist. NOTE: this commit was branched off status-go `develop`, so once it is merged, and we update this PR to the new commit, we will effectively be getting status-go develop changes. We *could* base that status-go PR off of master if it makes things easier.
fix: height on settings update issue
feat: move date/time of message below links
fix: layout issues when changing setting `neverAskAboutUnfurlingAgain`
feat: Add MessageBorder component to aid in showing rounded corners with different radius
2020-12-11 00:53:44 +00:00
|
|
|
return matches[0].substring(3)
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasImageExtension(url) {
|
2021-07-13 07:34:13 +00:00
|
|
|
return Constants.acceptedImageExtensions.some(ext => url.toLowerCase().includes(ext))
|
2021-03-10 04:59:01 +00:00
|
|
|
}
|
|
|
|
|
2022-06-07 14:47:45 +00:00
|
|
|
function removeGifUrls(message) {
|
|
|
|
return message.replace(/(?:https?|ftp):\/\/[\n\S]*(\.gif)+/gm, '');
|
|
|
|
}
|
|
|
|
|
2021-03-10 04:59:01 +00:00
|
|
|
function hasDragNDropImageExtension(url) {
|
2021-07-13 07:34:13 +00:00
|
|
|
return Constants.acceptedDragNDropImageExtensions.some(ext => url.toLowerCase().includes(ext))
|
2021-03-10 04:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function deduplicate(array) {
|
|
|
|
return Array.from(new Set(array))
|
feat: whitelist gifs (no url extension needed)
Fixes #1377.
Fixes #1479.
Two sites have been added to the whitelist: giphy.com and tenor.com.
`imageUrls` in its entirety has been removed and instead all links are being handle through the message `linkUrls`. This prevents double-handling of urls that may or may not be images.
The logic to automatically show links previews works like this:
1. If the setting "display chat images" is enabled, all links that *contain* ".png", ".jpg", ".jpeg", ".svg", ".gif" will be automatically shown. If the URL doesn't contain the extension, we are not downloading it. This was meant to be somewhat of a security compromise as we do not want to download each and every link posted in a message just to find out its true content type.
2. If the above setting is *disabled*, then we follow the whitelist settings for tenor and giphy. This allows us to preview gifs that do not have a file extension in their url.
feat: bump status-go to the commit that supports the new whitelist (https://github.com/status-im/status-go/pull/2094), and also lets us get link preview data from urls in the whitelist. NOTE: this commit was branched off status-go `develop`, so once it is merged, and we update this PR to the new commit, we will effectively be getting status-go develop changes. We *could* base that status-go PR off of master if it makes things easier.
fix: height on settings update issue
feat: move date/time of message below links
fix: layout issues when changing setting `neverAskAboutUnfurlingAgain`
feat: Add MessageBorder component to aid in showing rounded corners with different radius
2020-12-11 00:53:44 +00:00
|
|
|
}
|
2021-04-12 15:40:49 +00:00
|
|
|
|
2021-06-29 12:47:28 +00:00
|
|
|
function hasUpperCaseLetter(str) {
|
|
|
|
return (/[A-Z]/.test(str))
|
|
|
|
}
|
|
|
|
|
|
|
|
function convertSpacesToDashesAndUpperToLowerCase(str)
|
|
|
|
{
|
|
|
|
if (str.includes(" "))
|
|
|
|
str = str.replace(/ /g, "-")
|
|
|
|
|
|
|
|
if(hasUpperCaseLetter(str))
|
|
|
|
str = str.toLowerCase()
|
|
|
|
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Validation section start */
|
|
|
|
|
|
|
|
enum Validate {
|
|
|
|
NoEmpty = 0x01,
|
|
|
|
TextLength = 0x02,
|
|
|
|
TextHexColor = 0x04,
|
|
|
|
TextLowercaseLettersNumberAndDashes = 0x08
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateAndReturnError(str, validation, fieldName = "field", limit = 0)
|
|
|
|
{
|
|
|
|
let errMsg = ""
|
|
|
|
|
|
|
|
if(validation & Utils.Validate.NoEmpty && str === "") {
|
2022-04-04 11:26:30 +00:00
|
|
|
errMsg = qsTr("You need to enter a %1").arg(fieldName)
|
2021-06-29 12:47:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(validation & Utils.Validate.TextLength && str.length > limit) {
|
2022-04-04 11:26:30 +00:00
|
|
|
errMsg = qsTr("The %1 cannot exceed %2 characters").arg(fieldName, limit)
|
2021-06-29 12:47:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(validation & Utils.Validate.TextHexColor && !isHexColor(str)) {
|
2022-04-04 11:26:30 +00:00
|
|
|
errMsg = qsTr("Must be an hexadecimal color (eg: #4360DF)")
|
2021-06-29 12:47:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(validation & Utils.Validate.TextLowercaseLettersNumberAndDashes && !isValidChannelName(str)) {
|
2022-04-04 11:26:30 +00:00
|
|
|
errMsg = qsTr("Use only lowercase letters (a to z), numbers & dashes (-). Do not use chat keys.")
|
2021-06-29 12:47:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return errMsg
|
|
|
|
}
|
|
|
|
|
2021-08-16 13:46:00 +00:00
|
|
|
function getErrorMessage(errors, fieldName) {
|
|
|
|
if (errors) {
|
|
|
|
if (errors.minLength) {
|
|
|
|
return errors.minLength.min === 1 ?
|
|
|
|
qsTr("You need to enter a %1").arg(fieldName) :
|
|
|
|
qsTr("Value has to be at least %1 characters long").arg(errors.minLength.min)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-06-29 12:47:28 +00:00
|
|
|
/* Validation section end */
|
|
|
|
|
2022-06-02 17:47:42 +00:00
|
|
|
function getLabelForEstimatedTxTime(estimatedFlag) {
|
2022-07-07 08:51:04 +00:00
|
|
|
if (estimatedFlag === Constants.transactionEstimatedTime.unknown) {
|
|
|
|
return qsTr("Unknown")
|
|
|
|
}
|
|
|
|
|
2022-06-02 17:47:42 +00:00
|
|
|
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanOneMin) {
|
|
|
|
return qsTr("< 1 min")
|
|
|
|
}
|
|
|
|
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanThreeMins) {
|
|
|
|
return qsTr("< 3 mins")
|
|
|
|
}
|
|
|
|
if (estimatedFlag === Constants.transactionEstimatedTime.lessThanFiveMins) {
|
|
|
|
return qsTr("< 5 mins")
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsTr("> 5 mins")
|
|
|
|
}
|
2021-06-29 12:47:28 +00:00
|
|
|
|
2022-10-06 08:28:24 +00:00
|
|
|
function getContactDetailsAsJson(publicKey, getVerificationRequest=true) {
|
|
|
|
let jsonObj = mainModule.getContactDetailsAsJson(publicKey, getVerificationRequest)
|
2022-01-04 12:06:05 +00:00
|
|
|
try {
|
|
|
|
let obj = JSON.parse(jsonObj)
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
catch (e) {
|
2021-12-31 12:29:51 +00:00
|
|
|
// This log is available only in debug mode, if it's annoying we can remove it
|
2022-01-04 12:06:05 +00:00
|
|
|
console.debug("error parsing contact details for public key: ", publicKey, " error: ", e.message)
|
|
|
|
|
2021-12-31 12:29:51 +00:00
|
|
|
return {
|
|
|
|
displayName: "",
|
|
|
|
displayIcon: "",
|
|
|
|
publicKey: publicKey,
|
|
|
|
name: "",
|
|
|
|
ensVerified: false,
|
|
|
|
alias: "",
|
|
|
|
lastUpdated: 0,
|
|
|
|
lastUpdatedLocally: 0,
|
|
|
|
localNickname: "",
|
|
|
|
thumbnailImage: "",
|
|
|
|
largeImage: "",
|
|
|
|
isContact: false,
|
2022-07-06 16:51:56 +00:00
|
|
|
isAdded: false,
|
2021-12-31 12:29:51 +00:00
|
|
|
isBlocked: false,
|
|
|
|
requestReceived: false,
|
|
|
|
isSyncing: false,
|
2022-06-28 18:11:18 +00:00
|
|
|
removed: false,
|
|
|
|
trustStatus: Constants.trustStatus.unknown,
|
|
|
|
verificationStatus: Constants.verificationStatus.unverified
|
2021-12-31 12:29:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 08:28:24 +00:00
|
|
|
function isEnsVerified(publicKey, getVerificationRequest=true) {
|
2022-09-22 22:18:15 +00:00
|
|
|
if (!publicKey)
|
2022-10-06 08:28:24 +00:00
|
|
|
return false
|
|
|
|
return getContactDetailsAsJson(publicKey, getVerificationRequest).ensVerified
|
2022-09-22 22:18:15 +00:00
|
|
|
}
|
|
|
|
|
2022-03-09 10:27:32 +00:00
|
|
|
function getEmojiHashAsJson(publicKey) {
|
|
|
|
if (publicKey === "") {
|
|
|
|
return ""
|
|
|
|
}
|
2022-03-17 10:28:38 +00:00
|
|
|
let jsonObj = globalUtils.getEmojiHashAsJson(publicKey)
|
2022-03-09 10:27:32 +00:00
|
|
|
return JSON.parse(jsonObj)
|
|
|
|
}
|
2021-12-31 12:29:51 +00:00
|
|
|
|
2022-10-06 08:28:24 +00:00
|
|
|
function getColorHashAsJson(publicKey, force=false, getVerificationRequest=true) {
|
2022-09-22 22:18:15 +00:00
|
|
|
if (publicKey === "")
|
|
|
|
return
|
2022-10-06 08:28:24 +00:00
|
|
|
if (!force && isEnsVerified(publicKey, getVerificationRequest))
|
2022-09-22 22:18:15 +00:00
|
|
|
return
|
2022-03-17 10:28:38 +00:00
|
|
|
let jsonObj = globalUtils.getColorHashAsJson(publicKey)
|
2022-03-09 10:27:32 +00:00
|
|
|
return JSON.parse(jsonObj)
|
|
|
|
}
|
2021-06-29 12:47:28 +00:00
|
|
|
|
2022-04-07 19:02:54 +00:00
|
|
|
function colorIdForPubkey(publicKey) {
|
|
|
|
if (publicKey === "") {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return globalUtils.getColorId(publicKey)
|
|
|
|
}
|
|
|
|
|
2022-09-06 15:06:33 +00:00
|
|
|
function colorForColorId(colorId) {
|
|
|
|
if (colorId < 0 || colorId >= Theme.palette.userCustomizationColors.length) {
|
|
|
|
console.warn("Utils.colorForColorId : colorId is out of bounds")
|
2022-08-19 10:01:54 +00:00
|
|
|
return StatusColors.colors['blue']
|
|
|
|
}
|
2022-09-06 15:06:33 +00:00
|
|
|
return Theme.palette.userCustomizationColors[colorId]
|
|
|
|
}
|
2022-08-19 10:01:54 +00:00
|
|
|
|
2022-09-06 15:06:33 +00:00
|
|
|
function colorForPubkey(publicKey) {
|
|
|
|
const pubKeyColorId = colorIdForPubkey(publicKey)
|
|
|
|
return colorForColorId(pubKeyColorId)
|
2022-04-07 19:02:54 +00:00
|
|
|
}
|
|
|
|
|
2022-03-29 10:53:41 +00:00
|
|
|
function getCompressedPk(publicKey) {
|
|
|
|
if (publicKey === "") {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return globalUtils.getCompressedPk(publicKey)
|
|
|
|
}
|
|
|
|
|
2022-09-15 16:34:41 +00:00
|
|
|
function getElidedPk(publicKey) {
|
|
|
|
if (publicKey === "") {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return StatusQUtils.Utils.elideText(publicKey, 5, 3)
|
|
|
|
}
|
|
|
|
|
2022-03-29 10:53:41 +00:00
|
|
|
function getElidedCompressedPk(publicKey) {
|
|
|
|
if (publicKey === "") {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
let compressedPk = getCompressedPk(publicKey)
|
2022-09-15 16:34:41 +00:00
|
|
|
return getElidedPk(compressedPk, 6, 3)
|
2022-03-29 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2022-03-18 14:47:51 +00:00
|
|
|
function getTimeDifference(d1, d2) {
|
|
|
|
var timeString = ""
|
|
|
|
var day1Year = d1.getFullYear()
|
|
|
|
var day1Month = d1.getMonth()
|
|
|
|
var day1Time = d1.getTime()
|
|
|
|
|
|
|
|
var day2Year = d2.getFullYear()
|
|
|
|
var day2Month = d2.getMonth()
|
|
|
|
var day2Time = d2.getTime()
|
|
|
|
|
|
|
|
var inYears = day2Year-day1Year
|
|
|
|
|
|
|
|
if(inYears > 0) {
|
|
|
|
timeString = inYears > 1 ? qsTr("years ago") : qsTr("year ago")
|
|
|
|
return inYears + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
var inMonths = (day2Month+12*day2Year)-(day1Month+12*day1Year)
|
|
|
|
|
|
|
|
if(inMonths > 0) {
|
|
|
|
timeString = inMonths > 1 ? qsTr("months ago") : qsTr("month ago")
|
|
|
|
return inMonths + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
var inWeeks = parseInt((day2Time-day2Time)/(24*3600*1000*7))
|
|
|
|
|
|
|
|
if(inWeeks > 0) {
|
|
|
|
timeString = inWeeks > 1 ? qsTr("weeks ago") : qsTr("week ago")
|
|
|
|
return inWeeks + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
var inDays = parseInt((day2Time-day1Time)/(24*3600*1000))
|
|
|
|
|
|
|
|
if(inDays > 0) {
|
|
|
|
timeString = inDays > 1 ? qsTr("days ago") : qsTr("day ago")
|
|
|
|
return inDays + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
var inHours = parseInt((day2Time-day1Time)/(3600*1000));
|
|
|
|
|
|
|
|
if(inHours > 0) {
|
|
|
|
timeString = inHours > 1 ? qsTr("hours ago") : qsTr("hour ago")
|
|
|
|
return inHours + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
var inMins = parseInt((day2Time-day1Time)/(60*1000))
|
|
|
|
|
|
|
|
if(inMins > 0) {
|
|
|
|
timeString = inMins > 1 ? qsTr("mins ago") : qsTr("min ago")
|
|
|
|
return inMins + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
var inSecs = parseInt((day2Time-day1Time)/(1000));
|
|
|
|
|
|
|
|
if(inSecs > 0) {
|
|
|
|
timeString = inSecs > 1 ? qsTr("secs ago") : qsTr("sec ago")
|
|
|
|
return inSecs + " " + timeString
|
|
|
|
}
|
|
|
|
|
|
|
|
return qsTr("now")
|
|
|
|
}
|
|
|
|
|
2022-03-10 12:09:39 +00:00
|
|
|
function elideIfTooLong(str, maxLength) {
|
|
|
|
return (str.length > maxLength) ? str.substr(0, maxLength-4) + '...' : str;
|
|
|
|
}
|
|
|
|
|
|
|
|
function escapeHtml(unsafeStr)
|
|
|
|
{
|
|
|
|
return unsafeStr
|
|
|
|
.replace(/&/g, "&")
|
|
|
|
.replace(/</g, "<")
|
|
|
|
.replace(/>/g, ">")
|
|
|
|
.replace(/"/g, """)
|
|
|
|
.replace(/'/g, "'");
|
|
|
|
}
|
|
|
|
|
fix: prevent crash on generate account wrong password
Fixes #2448.
Currently, if a wrong password is entered when generating a wallet account, the app will crash due to attempting to decode a `GeneratedAccount ` from an rpc response containing only an error.
With this PR, we are detecting if an error is returned in the response, and if so, raising a StatusGoException. This exception is caught in the call chain, and translated in to a `StatusGoError` which is serialised and sent to the QML view, where it is parsed and displayed as an invalid password error in the input box.
refactor: remove string return values as error messages in wallet model
In the wallet model, we were passing back empty strings for no error, or an error as a string. This is not only confusing, but does not benefit from leaning on the compiler and strong types. One has to read the entire code to understand if a string result is returned when there is no error instead of implicitly being able to understand there is no return type.
To alleviate this, account creation fundtions that do not need to return a value have been changed to a void return type, and raise `StatusGoException` if there is an error encountered. This can be caught in the call chain and used as necessary (ie to pass to QML).
refactor: move invalid password string detection to Utils
Currently, we are reading returned view model values and checking to see if they include a known string from Status Go that means there was an invalid password used. This string was placed in the codebased in mulitple locations.
This PR moves the string check to a Utils function and updates all the references to use the function in Utils.
2021-05-13 04:41:48 +00:00
|
|
|
function isInvalidPasswordMessage(msg) {
|
|
|
|
return (
|
|
|
|
msg.includes("could not decrypt key with given password") ||
|
|
|
|
msg.includes("invalid password")
|
|
|
|
);
|
|
|
|
}
|
2022-04-05 15:45:01 +00:00
|
|
|
|
2022-08-29 18:02:36 +00:00
|
|
|
function isInvalidPrivateKey(msg) {
|
|
|
|
return msg.includes("invalid private key");
|
|
|
|
}
|
|
|
|
|
2022-05-12 15:24:03 +00:00
|
|
|
function isInvalidPath(msg) {
|
|
|
|
return msg.includes("error parsing derivation path")
|
|
|
|
}
|
|
|
|
|
|
|
|
function accountAlreadyExistsError(msg) {
|
|
|
|
return msg.includes("account already exists")
|
|
|
|
}
|
|
|
|
|
2022-08-11 10:58:09 +00:00
|
|
|
// See also: backend/interpret/cropped_image.nim
|
|
|
|
function getImageAndCropInfoJson(imgPath, cropRect) {
|
|
|
|
return JSON.stringify({imagePath: String(imgPath).replace("file://", ""), cropRect: cropRect})
|
|
|
|
}
|
|
|
|
|
2022-08-25 19:20:21 +00:00
|
|
|
function containsOnlyDigits(msg) {
|
2022-08-29 16:28:54 +00:00
|
|
|
var reg = new RegExp('[+-]?([0-9]*[.])?[0-9]+')
|
2022-08-25 19:20:21 +00:00
|
|
|
return reg.test(msg)
|
|
|
|
}
|
|
|
|
|
2022-04-05 15:45:01 +00:00
|
|
|
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
|
|
|
function isPunct(c) {
|
|
|
|
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|_|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
|
|
|
}
|
|
|
|
|
2020-06-22 15:51:15 +00:00
|
|
|
}
|