Merge branch 'develop' into sabrecat/teams-rebase

This commit is contained in:
SabreCat
2022-07-25 15:04:43 -05:00
45 changed files with 1403 additions and 490 deletions

View File

@@ -1,4 +1,5 @@
{ {
"ACCOUNT_MIN_CHAT_AGE": "0",
"ADMIN_EMAIL": "you@example.com", "ADMIN_EMAIL": "you@example.com",
"AMAZON_PAYMENTS_CLIENT_ID": "CLIENT_ID", "AMAZON_PAYMENTS_CLIENT_ID": "CLIENT_ID",
"AMAZON_PAYMENTS_MODE": "sandbox", "AMAZON_PAYMENTS_MODE": "sandbox",

609
package-lock.json generated
View File

@@ -1597,6 +1597,27 @@
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.0.tgz",
"integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==" "integrity": "sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg=="
}, },
"@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"requires": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
},
"dependencies": {
"@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"requires": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
}
}
}
},
"@jridgewell/sourcemap-codec": { "@jridgewell/sourcemap-codec": {
"version": "1.4.11", "version": "1.4.11",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
@@ -1876,18 +1897,18 @@
} }
}, },
"@types/eslint": { "@types/eslint": {
"version": "8.4.1", "version": "8.4.5",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
"integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==", "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==",
"requires": { "requires": {
"@types/estree": "*", "@types/estree": "*",
"@types/json-schema": "*" "@types/json-schema": "*"
} }
}, },
"@types/eslint-scope": { "@types/eslint-scope": {
"version": "3.7.3", "version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
"requires": { "requires": {
"@types/eslint": "*", "@types/eslint": "*",
"@types/estree": "*" "@types/estree": "*"
@@ -2148,22 +2169,22 @@
} }
}, },
"@webpack-cli/configtest": { "@webpack-cli/configtest": {
"version": "1.1.1", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
"integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==" "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg=="
}, },
"@webpack-cli/info": { "@webpack-cli/info": {
"version": "1.4.1", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
"integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
"requires": { "requires": {
"envinfo": "^7.7.3" "envinfo": "^7.7.3"
} }
}, },
"@webpack-cli/serve": { "@webpack-cli/serve": {
"version": "1.6.1", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
"integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==" "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q=="
}, },
"@xtuc/ieee754": { "@xtuc/ieee754": {
"version": "1.2.0", "version": "1.2.0",
@@ -2373,9 +2394,9 @@
} }
}, },
"apidoc": { "apidoc": {
"version": "0.51.1", "version": "0.52.0",
"resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.51.1.tgz", "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.52.0.tgz",
"integrity": "sha512-0dcL7NSUWDibgQ051ne/wXT0Dp+ArD66jwofuurxzRAPMuf2cRwa7GxRm9xlGXbOLaU7dVSZDr8LVeVBdI/oTQ==", "integrity": "sha512-k0gaMI7LWxaqKt2D+twC6XpI8X5SHkJalfo8TmF72d2TSNABquqB8LyIrVYzLcadcHuLMRFDaDibOK271gD67w==",
"requires": { "requires": {
"bootstrap": "3.4.1", "bootstrap": "3.4.1",
"commander": "^8.3.0", "commander": "^8.3.0",
@@ -2399,19 +2420,6 @@
"winston": "^3.3.3" "winston": "^3.3.3"
}, },
"dependencies": { "dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"anymatch": { "anymatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
@@ -2431,26 +2439,6 @@
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz",
"integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA=="
}, },
"boxen": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
"integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
"requires": {
"ansi-align": "^3.0.0",
"camelcase": "^6.2.0",
"chalk": "^4.1.0",
"cli-boxes": "^2.2.1",
"string-width": "^4.2.2",
"type-fest": "^0.20.2",
"widest-line": "^3.1.0",
"wrap-ansi": "^7.0.0"
}
},
"camelcase": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
},
"chokidar": { "chokidar": {
"version": "3.5.3", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -2466,24 +2454,6 @@
"readdirp": "~3.6.0" "readdirp": "~3.6.0"
} }
}, },
"cli-boxes": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
"integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw=="
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"debug": { "debug": {
"version": "3.2.7", "version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@@ -2492,11 +2462,6 @@
"ms": "^2.1.1" "ms": "^2.1.1"
} }
}, },
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"entities": { "entities": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
@@ -2509,14 +2474,14 @@
"optional": true "optional": true
}, },
"glob": { "glob": {
"version": "7.2.0", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
"inherits": "2", "inherits": "2",
"minimatch": "^3.0.4", "minimatch": "^3.1.1",
"once": "^1.3.0", "once": "^1.3.0",
"path-is-absolute": "^1.0.0" "path-is-absolute": "^1.0.0"
} }
@@ -2529,38 +2494,6 @@
"is-glob": "^4.0.1" "is-glob": "^4.0.1"
} }
}, },
"global-dirs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz",
"integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==",
"requires": {
"ini": "2.0.0"
}
},
"ini": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
"integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-installed-globally": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
"integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
"requires": {
"global-dirs": "^3.0.0",
"is-path-inside": "^3.0.2"
}
},
"is-npm": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz",
"integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA=="
},
"linkify-it": { "linkify-it": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz",
@@ -2581,10 +2514,18 @@
"uc.micro": "^1.0.5" "uc.micro": "^1.0.5"
} }
}, },
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"nodemon": { "nodemon": {
"version": "2.0.15", "version": "2.0.19",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.15.tgz", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz",
"integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", "integrity": "sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==",
"requires": { "requires": {
"chokidar": "^3.5.2", "chokidar": "^3.5.2",
"debug": "^3.2.7", "debug": "^3.2.7",
@@ -2592,10 +2533,10 @@
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"pstree.remy": "^1.1.8", "pstree.remy": "^1.1.8",
"semver": "^5.7.1", "semver": "^5.7.1",
"simple-update-notifier": "^1.0.7",
"supports-color": "^5.5.0", "supports-color": "^5.5.0",
"touch": "^3.1.0", "touch": "^3.1.0",
"undefsafe": "^2.0.5", "undefsafe": "^2.0.5"
"update-notifier": "^5.1.0"
}, },
"dependencies": { "dependencies": {
"semver": { "semver": {
@@ -2605,14 +2546,6 @@
} }
} }
}, },
"pupa": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz",
"integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==",
"requires": {
"escape-goat": "^2.0.0"
}
},
"readdirp": { "readdirp": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -2629,64 +2562,10 @@
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
} }
}, },
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"type-fest": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
},
"undefsafe": { "undefsafe": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
},
"update-notifier": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz",
"integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==",
"requires": {
"boxen": "^5.0.0",
"chalk": "^4.1.0",
"configstore": "^5.0.1",
"has-yarn": "^2.1.0",
"import-lazy": "^2.1.0",
"is-ci": "^2.0.0",
"is-installed-globally": "^0.4.0",
"is-npm": "^5.0.0",
"is-yarn-global": "^0.3.0",
"latest-version": "^5.1.0",
"pupa": "^2.1.1",
"semver": "^7.3.4",
"semver-diff": "^3.1.1",
"xdg-basedir": "^4.0.0"
}
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
} }
} }
}, },
@@ -3985,21 +3864,20 @@
"dev": true "dev": true
}, },
"browserslist": { "browserslist": {
"version": "4.19.1", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
"integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
"requires": { "requires": {
"caniuse-lite": "^1.0.30001286", "caniuse-lite": "^1.0.30001366",
"electron-to-chromium": "^1.4.17", "electron-to-chromium": "^1.4.188",
"escalade": "^3.1.1", "node-releases": "^2.0.6",
"node-releases": "^2.0.1", "update-browserslist-db": "^1.0.4"
"picocolors": "^1.0.0"
}, },
"dependencies": { "dependencies": {
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.4.28", "version": "1.4.192",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.192.tgz",
"integrity": "sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg==" "integrity": "sha512-8nCXyIQY9An88NXAp+PuPy5h3/w5ZY7Iu2lag65Q0XREprcat5F8gKhoHsBUnQcFuCRnmevpR8yEBYRU3d2HDw=="
} }
} }
}, },
@@ -4157,9 +4035,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001292", "version": "1.0.30001367",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz",
"integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==" "integrity": "sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw=="
}, },
"caseless": { "caseless": {
"version": "0.12.0", "version": "0.12.0",
@@ -4543,9 +4421,9 @@
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
}, },
"colorette": { "colorette": {
"version": "2.0.16", "version": "2.0.19",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
"integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==" "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="
}, },
"colors": { "colors": {
"version": "1.4.0", "version": "1.4.0",
@@ -5710,9 +5588,9 @@
} }
}, },
"enhanced-resolve": { "enhanced-resolve": {
"version": "5.9.3", "version": "5.10.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
"integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==", "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
"requires": { "requires": {
"graceful-fs": "^4.2.4", "graceful-fs": "^4.2.4",
"tapable": "^2.2.0" "tapable": "^2.2.0"
@@ -5822,122 +5700,122 @@
} }
}, },
"esbuild": { "esbuild": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.49.tgz",
"integrity": "sha512-HhFHPiRXGYOCRlrhpiVDYKcFJRdO0sBElZ668M4lh2ER0YgnkLxECuFe7uWCf23FrcLc59Pqr7dHkTqmRPDHmw==", "integrity": "sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==",
"requires": { "requires": {
"esbuild-android-64": "0.14.36", "esbuild-android-64": "0.14.49",
"esbuild-android-arm64": "0.14.36", "esbuild-android-arm64": "0.14.49",
"esbuild-darwin-64": "0.14.36", "esbuild-darwin-64": "0.14.49",
"esbuild-darwin-arm64": "0.14.36", "esbuild-darwin-arm64": "0.14.49",
"esbuild-freebsd-64": "0.14.36", "esbuild-freebsd-64": "0.14.49",
"esbuild-freebsd-arm64": "0.14.36", "esbuild-freebsd-arm64": "0.14.49",
"esbuild-linux-32": "0.14.36", "esbuild-linux-32": "0.14.49",
"esbuild-linux-64": "0.14.36", "esbuild-linux-64": "0.14.49",
"esbuild-linux-arm": "0.14.36", "esbuild-linux-arm": "0.14.49",
"esbuild-linux-arm64": "0.14.36", "esbuild-linux-arm64": "0.14.49",
"esbuild-linux-mips64le": "0.14.36", "esbuild-linux-mips64le": "0.14.49",
"esbuild-linux-ppc64le": "0.14.36", "esbuild-linux-ppc64le": "0.14.49",
"esbuild-linux-riscv64": "0.14.36", "esbuild-linux-riscv64": "0.14.49",
"esbuild-linux-s390x": "0.14.36", "esbuild-linux-s390x": "0.14.49",
"esbuild-netbsd-64": "0.14.36", "esbuild-netbsd-64": "0.14.49",
"esbuild-openbsd-64": "0.14.36", "esbuild-openbsd-64": "0.14.49",
"esbuild-sunos-64": "0.14.36", "esbuild-sunos-64": "0.14.49",
"esbuild-windows-32": "0.14.36", "esbuild-windows-32": "0.14.49",
"esbuild-windows-64": "0.14.36", "esbuild-windows-64": "0.14.49",
"esbuild-windows-arm64": "0.14.36" "esbuild-windows-arm64": "0.14.49"
} }
}, },
"esbuild-android-64": { "esbuild-android-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.49.tgz",
"integrity": "sha512-jwpBhF1jmo0tVCYC/ORzVN+hyVcNZUWuozGcLHfod0RJCedTDTvR4nwlTXdx1gtncDqjk33itjO+27OZHbiavw==", "integrity": "sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==",
"optional": true "optional": true
}, },
"esbuild-android-arm64": { "esbuild-android-arm64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.49.tgz",
"integrity": "sha512-/hYkyFe7x7Yapmfv4X/tBmyKnggUmdQmlvZ8ZlBnV4+PjisrEhAvC3yWpURuD9XoB8Wa1d5dGkTsF53pIvpjsg==", "integrity": "sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==",
"optional": true "optional": true
}, },
"esbuild-darwin-64": { "esbuild-darwin-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.49.tgz",
"integrity": "sha512-kkl6qmV0dTpyIMKagluzYqlc1vO0ecgpviK/7jwPbRDEv5fejRTaBBEE2KxEQbTHcLhiiDbhG7d5UybZWo/1zQ==", "integrity": "sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==",
"optional": true "optional": true
}, },
"esbuild-darwin-arm64": { "esbuild-darwin-arm64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.49.tgz",
"integrity": "sha512-q8fY4r2Sx6P0Pr3VUm//eFYKVk07C5MHcEinU1BjyFnuYz4IxR/03uBbDwluR6ILIHnZTE7AkTUWIdidRi1Jjw==", "integrity": "sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==",
"optional": true "optional": true
}, },
"esbuild-freebsd-64": { "esbuild-freebsd-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.49.tgz",
"integrity": "sha512-Hn8AYuxXXRptybPqoMkga4HRFE7/XmhtlQjXFHoAIhKUPPMeJH35GYEUWGbjteai9FLFvBAjEAlwEtSGxnqWww==", "integrity": "sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==",
"optional": true "optional": true
}, },
"esbuild-freebsd-arm64": { "esbuild-freebsd-arm64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.49.tgz",
"integrity": "sha512-S3C0attylLLRiCcHiJd036eDEMOY32+h8P+jJ3kTcfhJANNjP0TNBNL30TZmEdOSx/820HJFgRrqpNAvTbjnDA==", "integrity": "sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==",
"optional": true "optional": true
}, },
"esbuild-linux-32": { "esbuild-linux-32": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.49.tgz",
"integrity": "sha512-Eh9OkyTrEZn9WGO4xkI3OPPpUX7p/3QYvdG0lL4rfr73Ap2HAr6D9lP59VMF64Ex01LhHSXwIsFG/8AQjh6eNw==", "integrity": "sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==",
"optional": true "optional": true
}, },
"esbuild-linux-64": { "esbuild-linux-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.49.tgz",
"integrity": "sha512-vFVFS5ve7PuwlfgoWNyRccGDi2QTNkQo/2k5U5ttVD0jRFaMlc8UQee708fOZA6zTCDy5RWsT5MJw3sl2X6KDg==", "integrity": "sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==",
"optional": true "optional": true
}, },
"esbuild-linux-arm": { "esbuild-linux-arm": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.49.tgz",
"integrity": "sha512-NhgU4n+NCsYgt7Hy61PCquEz5aevI6VjQvxwBxtxrooXsxt5b2xtOUXYZe04JxqQo+XZk3d1gcr7pbV9MAQ/Lg==", "integrity": "sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==",
"optional": true "optional": true
}, },
"esbuild-linux-arm64": { "esbuild-linux-arm64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.49.tgz",
"integrity": "sha512-24Vq1M7FdpSmaTYuu1w0Hdhiqkbto1I5Pjyi+4Cdw5fJKGlwQuw+hWynTcRI/cOZxBcBpP21gND7W27gHAiftw==", "integrity": "sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==",
"optional": true "optional": true
}, },
"esbuild-linux-mips64le": { "esbuild-linux-mips64le": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.49.tgz",
"integrity": "sha512-hZUeTXvppJN+5rEz2EjsOFM9F1bZt7/d2FUM1lmQo//rXh1RTFYzhC0txn7WV0/jCC7SvrGRaRz0NMsRPf8SIA==", "integrity": "sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==",
"optional": true "optional": true
}, },
"esbuild-linux-ppc64le": { "esbuild-linux-ppc64le": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.49.tgz",
"integrity": "sha512-1Bg3QgzZjO+QtPhP9VeIBhAduHEc2kzU43MzBnMwpLSZ890azr4/A9Dganun8nsqD/1TBcqhId0z4mFDO8FAvg==", "integrity": "sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==",
"optional": true "optional": true
}, },
"esbuild-linux-riscv64": { "esbuild-linux-riscv64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.49.tgz",
"integrity": "sha512-dOE5pt3cOdqEhaufDRzNCHf5BSwxgygVak9UR7PH7KPVHwSTDAZHDoEjblxLqjJYpc5XaU9+gKJ9F8mp9r5I4A==", "integrity": "sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==",
"optional": true "optional": true
}, },
"esbuild-linux-s390x": { "esbuild-linux-s390x": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.49.tgz",
"integrity": "sha512-g4FMdh//BBGTfVHjF6MO7Cz8gqRoDPzXWxRvWkJoGroKA18G9m0wddvPbEqcQf5Tbt2vSc1CIgag7cXwTmoTXg==", "integrity": "sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==",
"optional": true "optional": true
}, },
"esbuild-loader": { "esbuild-loader": {
"version": "2.18.0", "version": "2.19.0",
"resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.18.0.tgz", "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.19.0.tgz",
"integrity": "sha512-AKqxM3bI+gvGPV8o6NAhR+cBxVO8+dh+O0OXBHIXXwuSGumckbPWHzZ17subjBGI2YEGyJ1STH7Haj8aCrwL/w==", "integrity": "sha512-urGNVE6Tl2rqx92ElKi/LiExXjGvcH6HfDBFzJ9Ppwqh4n6Jmx8x7RKAyMzSM78b6CAaJLhDncG5sPrL0ROh5Q==",
"requires": { "requires": {
"esbuild": "^0.14.6", "esbuild": "^0.14.39",
"joycon": "^3.0.1", "joycon": "^3.0.1",
"json5": "^2.2.0", "json5": "^2.2.0",
"loader-utils": "^2.0.0", "loader-utils": "^2.0.0",
@@ -5946,39 +5824,39 @@
} }
}, },
"esbuild-netbsd-64": { "esbuild-netbsd-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.49.tgz",
"integrity": "sha512-UB2bVImxkWk4vjnP62ehFNZ73lQY1xcnL5ZNYF3x0AG+j8HgdkNF05v67YJdCIuUJpBuTyCK8LORCYo9onSW+A==", "integrity": "sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==",
"optional": true "optional": true
}, },
"esbuild-openbsd-64": { "esbuild-openbsd-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.49.tgz",
"integrity": "sha512-NvGB2Chf8GxuleXRGk8e9zD3aSdRO5kLt9coTQbCg7WMGXeX471sBgh4kSg8pjx0yTXRt0MlrUDnjVYnetyivg==", "integrity": "sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==",
"optional": true "optional": true
}, },
"esbuild-sunos-64": { "esbuild-sunos-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.49.tgz",
"integrity": "sha512-VkUZS5ftTSjhRjuRLp+v78auMO3PZBXu6xl4ajomGenEm2/rGuWlhFSjB7YbBNErOchj51Jb2OK8lKAo8qdmsQ==", "integrity": "sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==",
"optional": true "optional": true
}, },
"esbuild-windows-32": { "esbuild-windows-32": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.49.tgz",
"integrity": "sha512-bIar+A6hdytJjZrDxfMBUSEHHLfx3ynoEZXx/39nxy86pX/w249WZm8Bm0dtOAByAf4Z6qV0LsnTIJHiIqbw0w==", "integrity": "sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==",
"optional": true "optional": true
}, },
"esbuild-windows-64": { "esbuild-windows-64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.49.tgz",
"integrity": "sha512-+p4MuRZekVChAeueT1Y9LGkxrT5x7YYJxYE8ZOTcEfeUUN43vktSn6hUNsvxzzATrSgq5QqRdllkVBxWZg7KqQ==", "integrity": "sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==",
"optional": true "optional": true
}, },
"esbuild-windows-arm64": { "esbuild-windows-arm64": {
"version": "0.14.36", "version": "0.14.49",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.36.tgz", "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.49.tgz",
"integrity": "sha512-fBB4WlDqV1m18EF/aheGYQkQZHfPHiHJSBYzXIo8yKehek+0BtBwo/4PNwKGJ5T0YK0oc8pBKjgwPbzSrPLb+Q==", "integrity": "sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==",
"optional": true "optional": true
}, },
"escalade": { "escalade": {
@@ -8678,9 +8556,9 @@
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
}, },
"image-size": { "image-size": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.1.tgz", "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz",
"integrity": "sha512-VAwkvNSNGClRw9mDHhc5Efax8PLlsOGcUTh0T/LIriC8vPA3U5PdqXWqkz406MoYHMKW8Uf9gWr05T/rYB44kQ==", "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==",
"requires": { "requires": {
"queue": "6.0.2" "queue": "6.0.2"
} }
@@ -9627,10 +9505,10 @@
"resolved": "https://registry.npmjs.org/json-content-demux/-/json-content-demux-0.1.4.tgz", "resolved": "https://registry.npmjs.org/json-content-demux/-/json-content-demux-0.1.4.tgz",
"integrity": "sha512-3GqPH2O0+8qBMTa1YTuL+7L24YJYNDjdXfa798y9S6GetScZAY2iAOGCdFkEPZJZdafPKv8ZUnp18VCCPTs0Nw==" "integrity": "sha512-3GqPH2O0+8qBMTa1YTuL+7L24YJYNDjdXfa798y9S6GetScZAY2iAOGCdFkEPZJZdafPKv8ZUnp18VCCPTs0Nw=="
}, },
"json-parse-better-errors": { "json-parse-even-better-errors": {
"version": "1.0.2", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
}, },
"json-schema": { "json-schema": {
"version": "0.2.3", "version": "0.2.3",
@@ -11350,9 +11228,9 @@
} }
}, },
"node-releases": { "node-releases": {
"version": "2.0.1", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
"integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg=="
}, },
"nodemon": { "nodemon": {
"version": "2.0.4", "version": "2.0.4",
@@ -13433,6 +13311,21 @@
"is-arrayish": "^0.3.1" "is-arrayish": "^0.3.1"
} }
}, },
"simple-update-notifier": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
"integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
"requires": {
"semver": "~7.0.0"
},
"dependencies": {
"semver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A=="
}
}
},
"sinon": { "sinon": {
"version": "13.0.2", "version": "13.0.2",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz", "resolved": "https://registry.npmjs.org/sinon/-/sinon-13.0.2.tgz",
@@ -14066,7 +13959,8 @@
"strip-final-newline": { "strip-final-newline": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"optional": true
}, },
"strip-indent": { "strip-indent": {
"version": "1.0.1", "version": "1.0.1",
@@ -14395,20 +14289,20 @@
"integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw=="
}, },
"terser": { "terser": {
"version": "5.12.1", "version": "5.14.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
"integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
"requires": { "requires": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0", "acorn": "^8.5.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20" "source-map-support": "~0.5.20"
}, },
"dependencies": { "dependencies": {
"acorn": { "acorn": {
"version": "8.7.0", "version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
}, },
"commander": { "commander": {
"version": "2.20.3", "version": "2.20.3",
@@ -14416,9 +14310,9 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
}, },
"source-map": { "source-map": {
"version": "0.7.3", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}, },
"source-map-support": { "source-map-support": {
"version": "0.5.21", "version": "0.5.21",
@@ -14427,34 +14321,20 @@
"requires": { "requires": {
"buffer-from": "^1.0.0", "buffer-from": "^1.0.0",
"source-map": "^0.6.0" "source-map": "^0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
} }
} }
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "5.3.1", "version": "5.3.3",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
"integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
"requires": { "requires": {
"@jridgewell/trace-mapping": "^0.3.7",
"jest-worker": "^27.4.5", "jest-worker": "^27.4.5",
"schema-utils": "^3.1.1", "schema-utils": "^3.1.1",
"serialize-javascript": "^6.0.0", "serialize-javascript": "^6.0.0",
"source-map": "^0.6.1",
"terser": "^5.7.2" "terser": "^5.7.2"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
} }
}, },
"text-hex": { "text-hex": {
@@ -15546,9 +15426,9 @@
} }
}, },
"watchpack": { "watchpack": {
"version": "2.3.1", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"requires": { "requires": {
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2" "graceful-fs": "^4.1.2"
@@ -15560,9 +15440,9 @@
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
}, },
"webpack": { "webpack": {
"version": "5.72.0", "version": "5.73.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.72.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
"integrity": "sha512-qmSmbspI0Qo5ld49htys8GY9XhS9CGqFoHTsOVAnjBdg0Zn79y135R+k4IR4rKK6+eKaabMhJwiVB7xw0SJu5w==", "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
"requires": { "requires": {
"@types/eslint-scope": "^3.7.3", "@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51", "@types/estree": "^0.0.51",
@@ -15573,13 +15453,13 @@
"acorn-import-assertions": "^1.7.6", "acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5", "browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2", "chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.9.2", "enhanced-resolve": "^5.9.3",
"es-module-lexer": "^0.9.0", "es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1", "eslint-scope": "5.1.1",
"events": "^3.2.0", "events": "^3.2.0",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.9", "graceful-fs": "^4.2.9",
"json-parse-better-errors": "^1.0.2", "json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0", "loader-runner": "^4.2.0",
"mime-types": "^2.1.27", "mime-types": "^2.1.27",
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
@@ -15591,9 +15471,9 @@
}, },
"dependencies": { "dependencies": {
"acorn": { "acorn": {
"version": "8.7.0", "version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
}, },
"eslint-scope": { "eslint-scope": {
"version": "5.1.1", "version": "5.1.1",
@@ -15632,17 +15512,17 @@
} }
}, },
"webpack-cli": { "webpack-cli": {
"version": "4.9.2", "version": "4.10.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
"integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
"requires": { "requires": {
"@discoveryjs/json-ext": "^0.5.0", "@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.1.1", "@webpack-cli/configtest": "^1.2.0",
"@webpack-cli/info": "^1.4.1", "@webpack-cli/info": "^1.5.0",
"@webpack-cli/serve": "^1.6.1", "@webpack-cli/serve": "^1.7.0",
"colorette": "^2.0.14", "colorette": "^2.0.14",
"commander": "^7.0.0", "commander": "^7.0.0",
"execa": "^5.0.0", "cross-spawn": "^7.0.3",
"fastest-levenshtein": "^1.0.12", "fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2", "import-local": "^3.0.2",
"interpret": "^2.2.0", "interpret": "^2.2.0",
@@ -15655,58 +15535,11 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
}, },
"execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
"requires": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.0",
"human-signals": "^2.1.0",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^4.0.1",
"onetime": "^5.1.2",
"signal-exit": "^3.0.3",
"strip-final-newline": "^2.0.0"
}
},
"get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
},
"human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
},
"interpret": { "interpret": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
}, },
"npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
"requires": {
"path-key": "^3.0.0"
}
},
"onetime": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
"requires": {
"mimic-fn": "^2.1.0"
}
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"rechoir": { "rechoir": {
"version": "0.7.1", "version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",

View File

@@ -13,7 +13,7 @@
"accepts": "^1.3.8", "accepts": "^1.3.8",
"amazon-payments": "^0.2.9", "amazon-payments": "^0.2.9",
"amplitude": "^6.0.0", "amplitude": "^6.0.0",
"apidoc": "^0.51.1", "apidoc": "^0.52.0",
"apple-auth": "^1.0.7", "apple-auth": "^1.0.7",
"bcrypt": "^5.0.1", "bcrypt": "^5.0.1",
"body-parser": "^1.20.0", "body-parser": "^1.20.0",
@@ -39,7 +39,7 @@
"gulp.spritesmith": "^6.13.0", "gulp.spritesmith": "^6.13.0",
"habitica-markdown": "^3.0.0", "habitica-markdown": "^3.0.0",
"helmet": "^4.6.0", "helmet": "^4.6.0",
"image-size": "^1.0.1", "image-size": "^1.0.2",
"in-app-purchase": "^1.11.3", "in-app-purchase": "^1.11.3",
"js2xmlparser": "^4.0.2", "js2xmlparser": "^4.0.2",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",

View File

@@ -15,6 +15,10 @@ describe('DELETE /groups/:groupId/chat/:chatId', () => {
type: 'guild', type: 'guild',
privacy: 'public', privacy: 'public',
}, },
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
}); });
groupWithChat = group; groupWithChat = group;

View File

@@ -117,7 +117,9 @@ describe('POST /chat/:chatId/flag', () => {
}); });
it('Flags a chat when the author\'s account was deleted', async () => { it('Flags a chat when the author\'s account was deleted', async () => {
const deletedUser = await generateUser(); const deletedUser = await generateUser({
'auth.timestamps.created': new Date('2022-01-01'),
});
const { message } = await deletedUser.post(`/groups/${group._id}/chat`, { message: TEST_MESSAGE }); const { message } = await deletedUser.post(`/groups/${group._id}/chat`, { message: TEST_MESSAGE });
await deletedUser.del('/user', { await deletedUser.del('/user', {
password: 'password', password: 'password',

View File

@@ -18,11 +18,16 @@ describe('POST /chat/:chatId/like', () => {
privacy: 'public', privacy: 'public',
}, },
members: 1, members: 1,
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
}); });
user = groupLeader; user = groupLeader;
groupWithChat = group; groupWithChat = group;
anotherUser = members[0]; // eslint-disable-line prefer-destructuring anotherUser = members[0]; // eslint-disable-line prefer-destructuring
await anotherUser.update({ 'auth.timestamps.created': new Date('2022-01-01') });
}); });
it('Returns an error when chat message is not found', async () => { it('Returns an error when chat message is not found', async () => {

View File

@@ -38,10 +38,15 @@ describe('POST /chat', () => {
members: 2, members: 2,
}); });
user = groupLeader; user = groupLeader;
await user.update({ 'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL }); // prevent tests accidentally throwing messageGroupChatSpam await user.update({
'contributor.level': SPAM_MIN_EXEMPT_CONTRIB_LEVEL,
'auth.timestamps.created': new Date('2022-01-01'),
}); // prevent tests accidentally throwing messageGroupChatSpam
groupWithChat = group; groupWithChat = group;
member = members[0]; // eslint-disable-line prefer-destructuring member = members[0]; // eslint-disable-line prefer-destructuring
additionalMember = members[1]; // eslint-disable-line prefer-destructuring additionalMember = members[1]; // eslint-disable-line prefer-destructuring
await member.update({ 'auth.timestamps.created': new Date('2022-01-01') });
await additionalMember.update({ 'auth.timestamps.created': new Date('2022-01-01') });
}); });
it('Returns an error when no message is provided', async () => { it('Returns an error when no message is provided', async () => {
@@ -104,7 +109,10 @@ describe('POST /chat', () => {
}); });
const privateGuildMemberWithChatsRevoked = members[0]; const privateGuildMemberWithChatsRevoked = members[0];
await privateGuildMemberWithChatsRevoked.update({ 'flags.chatRevoked': true }); await privateGuildMemberWithChatsRevoked.update({
'flags.chatRevoked': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await privateGuildMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage }); const message = await privateGuildMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage });
@@ -122,7 +130,10 @@ describe('POST /chat', () => {
}); });
const privatePartyMemberWithChatsRevoked = members[0]; const privatePartyMemberWithChatsRevoked = members[0];
await privatePartyMemberWithChatsRevoked.update({ 'flags.chatRevoked': true }); await privatePartyMemberWithChatsRevoked.update({
'flags.chatRevoked': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await privatePartyMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage }); const message = await privatePartyMemberWithChatsRevoked.post(`/groups/${group._id}/chat`, { message: testMessage });
@@ -183,7 +194,10 @@ describe('POST /chat', () => {
}); });
const userWithChatShadowMuted = members[0]; const userWithChatShadowMuted = members[0];
await userWithChatShadowMuted.update({ 'flags.chatShadowMuted': true }); await userWithChatShadowMuted.update({
'flags.chatShadowMuted': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage }); const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage });
@@ -202,7 +216,10 @@ describe('POST /chat', () => {
}); });
const userWithChatShadowMuted = members[0]; const userWithChatShadowMuted = members[0];
await userWithChatShadowMuted.update({ 'flags.chatShadowMuted': true }); await userWithChatShadowMuted.update({
'flags.chatShadowMuted': true,
'auth.timestamps.created': new Date('2022-01-01'),
});
const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage }); const message = await userWithChatShadowMuted.post(`/groups/${group._id}/chat`, { message: testMessage });
@@ -312,6 +329,7 @@ describe('POST /chat', () => {
}, },
members: 1, members: 1,
}); });
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage }); const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage });
@@ -330,6 +348,7 @@ describe('POST /chat', () => {
// Update the bannedWordsAllowed property for the group // Update the bannedWordsAllowed property for the group
group.update({ bannedWordsAllowed: true }); group.update({ bannedWordsAllowed: true });
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage }); const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage });
@@ -345,6 +364,7 @@ describe('POST /chat', () => {
}, },
members: 1, members: 1,
}); });
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage }); const message = await members[0].post(`/groups/${group._id}/chat`, { message: testBannedWordMessage });
@@ -411,6 +431,7 @@ describe('POST /chat', () => {
}, },
members: 1, members: 1,
}); });
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
const message = await members[0].post(`/groups/${group._id}/chat`, { message: testSlurMessage }); const message = await members[0].post(`/groups/${group._id}/chat`, { message: testSlurMessage });
@@ -430,6 +451,16 @@ describe('POST /chat', () => {
}); });
}); });
it('errors when user account is too young', async () => {
const brandNewUser = await generateUser();
await expect(brandNewUser.post('/groups/habitrpg/chat', { message: 'hi im new' }))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('chatTemporarilyUnavailable'),
});
});
it('creates a chat', async () => { it('creates a chat', async () => {
const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage }); const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`); const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`);
@@ -492,6 +523,7 @@ describe('POST /chat', () => {
'items.currentMount': mount, 'items.currentMount': mount,
'items.currentPet': pet, 'items.currentPet': pet,
'preferences.style': style, 'preferences.style': style,
'auth.timestamps.created': new Date('2022-01-01'),
}); });
await userWithStyle.sync(); await userWithStyle.sync();
@@ -517,6 +549,7 @@ describe('POST /chat', () => {
}; };
const backer = await generateUser({ const backer = await generateUser({
backer: backerInfo, backer: backerInfo,
'auth.timestamps.created': new Date('2022-01-01'),
}); });
const message = await backer.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage }); const message = await backer.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage });
@@ -587,6 +620,9 @@ describe('POST /chat', () => {
privacy: 'private', privacy: 'private',
}, },
members: 1, members: 1,
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
},
}); });
const message = await groupLeader.post(`/groups/${group._id}/chat`, { message: testMessage }); const message = await groupLeader.post(`/groups/${group._id}/chat`, { message: testMessage });

View File

@@ -15,6 +15,10 @@ describe('POST /groups/:id/chat/seen', () => {
privacy: 'public', privacy: 'public',
}, },
members: 1, members: 1,
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
}); });
guild = group; guild = group;
@@ -51,6 +55,9 @@ describe('POST /groups/:id/chat/seen', () => {
privacy: 'private', privacy: 'private',
}, },
members: 1, members: 1,
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
},
}); });
party = group; party = group;

View File

@@ -18,6 +18,10 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
type: 'guild', type: 'guild',
privacy: 'public', privacy: 'public',
}, },
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
}); });
groupWithChat = group; groupWithChat = group;
@@ -65,6 +69,7 @@ describe('POST /groups/:id/chat/:id/clearflags', () => {
members: 1, members: 1,
}); });
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
let privateMessage = await members[0].post(`/groups/${group._id}/chat`, { message: 'Some message' }); let privateMessage = await members[0].post(`/groups/${group._id}/chat`, { message: 'Some message' });
privateMessage = privateMessage.message; privateMessage = privateMessage.message;

View File

@@ -37,6 +37,7 @@ describe('POST /groups/:groupId/leave', () => {
leader = groupLeader; leader = groupLeader;
member = members[0]; // eslint-disable-line prefer-destructuring member = members[0]; // eslint-disable-line prefer-destructuring
memberCount = group.memberCount; memberCount = group.memberCount;
await members[0].update({ 'auth.timestamps.created': new Date('2022-01-01') });
}); });
it('prevents non members from leaving', async () => { it('prevents non members from leaving', async () => {
@@ -152,6 +153,10 @@ describe('POST /groups/:groupId/leave', () => {
type: 'guild', type: 'guild',
}, },
invites: 1, invites: 1,
leaderDetails: {
'auth.timestamps.created': new Date('2022-01-01'),
balance: 10,
},
}); });
privateGuild = group; privateGuild = group;

View File

@@ -153,6 +153,7 @@ describe('POST /groups/:groupId/removeMember/:memberId', () => {
}, },
invites: 1, invites: 1,
members: 2, members: 2,
leaderDetails: { 'auth.timestamps.created': new Date('2022-01-01') },
}); });
party = group; party = group;

View File

@@ -25,6 +25,7 @@ describe('Prevent multiple notifications', () => {
for (let i = 0; i < 4; i += 1) { for (let i = 0; i < 4; i += 1) {
for (let memberIndex = 0; memberIndex < partyMembers.length; memberIndex += 1) { for (let memberIndex = 0; memberIndex < partyMembers.length; memberIndex += 1) {
await partyMembers[memberIndex].update({ 'auth.timestamps.created': new Date('2022-01-01') }); // eslint-disable-line no-await-in-loop
multipleChatMessages.push( multipleChatMessages.push(
partyMembers[memberIndex].post(`/groups/${party._id}/chat`, { message: `Message ${i}_${memberIndex}` }), partyMembers[memberIndex].post(`/groups/${party._id}/chat`, { message: `Message ${i}_${memberIndex}` }),
); );

View File

@@ -39,25 +39,38 @@ describe('POST /user/buy-quest/:key', () => {
})); }));
}); });
it('returns an error if quest prerequisites are not met', async () => { it('returns an error if not all quest prerequisites are met', async () => {
const key = 'dilatoryDistress2'; const prerequisites = ['dilatoryDistress1', 'dilatoryDistress2'];
const key = 'dilatoryDistress3';
const achievementName1 = `achievements.quests.${prerequisites[0]}`;
await user.update({
[achievementName1]: true,
'stats.gp': 9999,
});
await expect(user.post(`/user/buy-quest/${key}`)) await expect(user.post(`/user/buy-quest/${key}`))
.to.eventually.be.rejected.and.eql({ .to.eventually.be.rejected.and.eql({
code: 401, code: 401,
error: 'NotAuthorized', error: 'NotAuthorized',
message: t('mustComplete', { quest: 'dilatoryDistress1' }), message: t('mustComplete', { quest: prerequisites[1] }),
}); });
}); });
it('allows purchase of a quest if prerequisites are met', async () => { it('allows purchase of a quest if prerequisites are met', async () => {
const prerequisite = 'dilatoryDistress1'; const prerequisites = ['dilatoryDistress1', 'dilatoryDistress2'];
const key = 'dilatoryDistress2'; const key = 'dilatoryDistress3';
const item = content.quests[key]; const item = content.quests[key];
const achievementName = `achievements.quests.${prerequisite}`; const achievementName1 = `achievements.quests.${prerequisites[0]}`;
const achievementName2 = `achievements.quests.${prerequisites[1]}`;
await user.update({ [achievementName]: true, 'stats.gp': 9999 }); await user.update({
[achievementName1]: true,
[achievementName2]: true,
'stats.gp': 9999,
});
const res = await user.post(`/user/buy-quest/${key}`); const res = await user.post(`/user/buy-quest/${key}`);
await user.sync(); await user.sync();

View File

@@ -33,7 +33,7 @@ describe('shared.ops.buyQuestGems', () => {
pinnedGearUtils.removeItemByPath.restore(); pinnedGearUtils.removeItemByPath.restore();
}); });
context('successful purchase', () => { context('single purchase', () => {
const userGemAmount = 10; const userGemAmount = 10;
before(() => { before(() => {
@@ -44,7 +44,7 @@ describe('shared.ops.buyQuestGems', () => {
user.pinnedItems.push({ type: 'quests', key: 'gryphon' }); user.pinnedItems.push({ type: 'quests', key: 'gryphon' });
}); });
it('purchases quests', async () => { it('successfully purchases quest', async () => {
const key = 'gryphon'; const key = 'gryphon';
await buyQuest(user, { params: { key } }); await buyQuest(user, { params: { key } });
@@ -58,6 +58,28 @@ describe('shared.ops.buyQuestGems', () => {
await buyQuest(user, { params: { key } }); await buyQuest(user, { params: { key } });
expect(user.items.quests[key]).to.equal(1);
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
});
it('errors if the user has not completed prerequisite quests', async () => {
const key = 'atom3';
user.achievements.quests.atom1 = 1;
try {
await buyQuest(user, { params: { key } });
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('mustComplete', { quest: 'atom2' }));
expect(user.items.quests[key]).to.eql(undefined);
}
});
it('successfully purchases quest if user has completed all prerequisite quests', async () => {
const key = 'atom3';
user.achievements.quests.atom1 = 1;
user.achievements.quests.atom2 = 1;
await buyQuest(user, { params: { key } });
expect(user.items.quests[key]).to.equal(1); expect(user.items.quests[key]).to.equal(1);
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true); expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
}); });

View File

@@ -162,7 +162,9 @@ describe('shared.ops.buyQuest', () => {
} }
}); });
it('does not buy a quest without completing previous quests', async () => { it('returns error if user has not completed all prerequisite quests', async () => {
user.stats.gp = 9999;
user.achievements.quests.dilatoryDistress1 = 1;
try { try {
await buyQuest(user, { await buyQuest(user, {
params: { params: {
@@ -175,4 +177,22 @@ describe('shared.ops.buyQuest', () => {
expect(user.items.quests).to.eql({}); expect(user.items.quests).to.eql({});
} }
}); });
it('successfully purchases quest if user has completed all prerequisite quests', async () => {
user.stats.gp = 500;
user.achievements.quests.dilatoryDistress1 = 1;
user.achievements.quests.dilatoryDistress2 = 1;
await buyQuest(user, {
params: {
key: 'dilatoryDistress3',
},
}, analytics);
expect(user.items.quests).to.eql({
dilatoryDistress3: 1,
});
expect(user.stats.gp).to.equal(100);
expect(analytics.track).to.be.calledOnce;
});
}); });

View File

@@ -11,6 +11,7 @@ if (process.env.LOAD_SERVER === '0') { // when the server is in a different proc
setupNconf('./config.json.example'); setupNconf('./config.json.example');
nconf.set('NODE_DB_URI', nconf.get('TEST_DB_URI')); nconf.set('NODE_DB_URI', nconf.get('TEST_DB_URI'));
nconf.set('NODE_ENV', 'test'); nconf.set('NODE_ENV', 'test');
nconf.set('ACCOUNT_MIN_CHAT_AGE', '2');
nconf.set('IS_TEST', true); nconf.set('IS_TEST', true);
// We require src/server and not src/index because // We require src/server and not src/index because
// 1. nconf is already setup // 1. nconf is already setup

View File

@@ -15839,9 +15839,9 @@
} }
}, },
"core-js": { "core-js": {
"version": "3.23.3", "version": "3.23.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.3.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.5.tgz",
"integrity": "sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q==" "integrity": "sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg=="
}, },
"core-js-compat": { "core-js-compat": {
"version": "3.11.0", "version": "3.11.0",

View File

@@ -32,7 +32,7 @@
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"bootstrap-vue": "^2.22.0", "bootstrap-vue": "^2.22.0",
"chai": "^4.3.6", "chai": "^4.3.6",
"core-js": "^3.23.3", "core-js": "^3.23.5",
"dompurify": "^2.3.8", "dompurify": "^2.3.8",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0", "eslint-config-habitrpg": "^6.2.0",

View File

@@ -82,7 +82,7 @@ input, textarea, input.form-control, textarea.form-control {
} }
} }
/** Colored Input-Groups, ignoring checklist */ // Colored Input-Groups, ignoring checklist
.input-group:not(.checklist-group) { .input-group:not(.checklist-group) {
border-radius: 2px; border-radius: 2px;
border: solid 1px $gray-400; border: solid 1px $gray-400;
@@ -100,7 +100,7 @@ input, textarea, input.form-control, textarea.form-control {
} }
} }
/** Generic Input Group Styles */ // Generic Input Group Styles
.input-group { .input-group {
height: 2rem; height: 2rem;
@@ -179,10 +179,11 @@ input, textarea, input.form-control, textarea.form-control {
padding-left: 0px; padding-left: 0px;
} }
// Checkboxes and radios // used in checkboxes and radios
$bg-focused-active-control: #4f2993; $bg-focused-active-control: #4f2993;
$bg-disabled-control: #34303a; $bg-disabled-control: #34303a;
// custom control
.custom-control { .custom-control {
margin-bottom: .5rem; margin-bottom: .5rem;
@@ -205,6 +206,7 @@ $bg-disabled-control: #34303a;
} }
} }
// checkboxes
.custom-checkbox { .custom-checkbox {
.custom-control-label::before { .custom-control-label::before {
border-radius: 2px; border-radius: 2px;
@@ -280,11 +282,26 @@ $bg-disabled-control: #34303a;
padding-left: 36px; padding-left: 36px;
} }
// radio buttons
$bg-color: $purple-400;
// svg for the purple dot
@mixin custom-radio-checked-icon ($bg-color) { @mixin custom-radio-checked-icon ($bg-color) {
background-image: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$bg-color}'/%3E%3C/svg%3E"), "#", "%23"); background-image: str-replace(url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$bg-color}'/%3E%3C/svg%3E"), "#", "%23");
} }
.custom-radio .custom-control-input { .custom-radio .custom-control-input {
opacity: 0;
margin: 15px 25px 34px 25px;
// outside circle
&:checked~.custom-control-label::before {
background-color: $gray-700;
background-size: 12px 12px;
border-color: $purple-400;
}
// checked indicator
&:checked~.custom-control-label::after { &:checked~.custom-control-label::after {
@include custom-radio-checked-icon($purple-400); @include custom-radio-checked-icon($purple-400);
width: 18px; width: 18px;
@@ -292,51 +309,84 @@ $bg-disabled-control: #34303a;
background-size: 12px 12px; background-size: 12px 12px;
} }
&:checked~.custom-control-label::before {
background-color: $gray-700;
background-size: 12px 12px;
border-color: $purple-400;
}
&:active~.custom-control-label::before { &:active~.custom-control-label::before {
background-color: inherit; background-color: inherit;
} }
&:focus:not(:checked):not(:disabled)~.custom-control-label::before, &:active:not(:checked):not(:disabled)~.custom-control-label::before { // focus / not checked / not disabled
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1); &:focus:not(:checked):not(:disabled)~.custom-control-label::before,
&:active:not(:checked):not(:disabled)~.custom-control-label::before {
border: 2px solid $gray-300;
box-shadow: 0 0 0 2px rgba(146, 92, 243, 0.5);
} }
&:focus:checked:not(:disabled)~.custom-control-label::before, &:active:checked:not(:disabled)~.custom-control-label::before { // focus / checked / not disabled
box-shadow: 0 0 0 6px rgba($bg-focused-active-control, 0.1); &:focus:checked:not(:disabled)~.custom-control-label::before,
border-color: $purple-400; &:active:checked:not(:disabled)~.custom-control-label::before {
background-color: rgba($bg-focused-active-control, 0.1); border: 2px solid $purple-400;
box-shadow: 0 0 0 2px rgba(146, 92, 243, 0.5);
} }
&:disabled:checked~.custom-control-label::before { // hover / not checked / not disabled
border-color: $gray-400; &:hover:not(:checked):not(:disabled)~.custom-control-label::before,
background-color: transparent; &:active:not(:checked):not(:disabled)~.custom-control-label::before {
width: 18px;
height: 18px;
background: 50%/50% 50% no-repeat;
@include custom-radio-checked-icon($purple-400);
background-size: 12px 12px;
border: solid 2px $purple-400;
} }
&:disabled:checked~.custom-control-label::after { // hover / checked / not disabled
&:hover:checked:not(:disabled)~.custom-control-label::before,
&:active::checked:not(:disabled)~.custom-control-label::before {
width: 18px;
height: 18px;
background: 50%/50% 50% no-repeat;
@include custom-radio-checked-icon($gray-400); @include custom-radio-checked-icon($gray-400);
background-size: 12px 12px;
border: solid 2px $purple-300;
}
// disabled / checked / before
&:disabled:checked~.custom-control-label::before {
background: 50%/50% 50% no-repeat;
@include custom-radio-checked-icon($gray-300);
border: 2px solid $gray-200;
background-color: transparent;
opacity: 0.75;
}
// disabled / checked / after
&:disabled:checked~.custom-control-label::after {
background: 50%/50% 50% no-repeat;
@include custom-radio-checked-icon($gray-300);
width: 18px; width: 18px;
height: 18px; height: 18px;
background-size: 12px 12px; background-size: 12px 12px;
} }
// disabled / not checked / before
&:disabled:not(:checked)~.custom-control-label::before { &:disabled:not(:checked)~.custom-control-label::before {
border-color: $gray-300; background-color: $gray-600;
background-color: transparent; border: 2px solid $gray-200;
} }
&:focus:disabled~.custom-control-label::before, &:active:disabled~.custom-control-label::before { // focus and disabled / not checked / before
box-shadow: 0 0 0 6px rgba($bg-disabled-control, 0.1); &:focus:disabled~.custom-control-label::before,
border-color: $gray-300; &:active:disabled~.custom-control-label::before {
background-color: rgba($bg-disabled-control, 0.1); background-color: rgba($bg-disabled-control, 0.1);
box-shadow: 0 0 0 6px rgba($bg-disabled-control, 0.1);
border: 2px solid $gray-200;
} }
&:focus:disabled:checked~.custom-control-label::before, &:active:disabled:checked~.custom-control-label::before { // focus and disabled / checked / before
border-color: $gray-400; &:focus:disabled:checked~.custom-control-label::before,
&:active:disabled:checked~.custom-control-label::before {
background: 50%/50% 50% no-repeat;
@include custom-radio-checked-icon($gray-300);
border: 2px solid $gray-200;
} }
} }

View File

@@ -0,0 +1,27 @@
<svg width="176" height="67" viewBox="0 0 176 67" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path fill="#77F4C7" d="M35.667 11.667 40 9.5l-4.333-2.167L33.5 3l-2.167 4.333L27 9.5l4.333 2.167L33.5 16z"/>
<path fill="#BDA8FF" d="M24.667 38.667 30 36l-5.333-2.667L22 28l-2.667 5.333L14 36l5.333 2.667L22 44z"/>
<path fill="#8EEDF6" d="M35.667 63.667 39 62l-3.333-1.667L34 57l-1.667 3.333L29 62l3.333 1.667L34 67z"/>
<path fill="#FFBE5D" d="M6.667 49.667 10 48l-3.333-1.667L5 43l-1.667 3.333L0 48l3.333 1.667L5 53z"/>
<path fill="#FFB6B8" d="M5.667 20.667 8 19.5l-2.333-1.167L4.5 16l-1.167 2.333L1 19.5l2.333 1.167L4.5 23z"/>
<g>
<path fill="#77F4C7" d="M140.333 11.667 136 9.5l4.333-2.167L142.5 3l2.167 4.333L149 9.5l-4.333 2.167L142.5 16z"/>
<path fill="#BDA8FF" d="M151.333 38.667 146 36l5.333-2.667L154 28l2.667 5.333L162 36l-5.333 2.667L154 44z"/>
<path fill="#8EEDF6" d="M140.333 63.667 137 62l3.333-1.667L142 57l1.667 3.333L147 62l-3.333 1.667L142 67z"/>
<path fill="#FFBE5D" d="M169.333 49.667 166 48l3.333-1.667L171 43l1.667 3.333L176 48l-3.333 1.667L171 53z"/>
<path fill="#FFB6B8" d="M170.333 20.667 168 19.5l2.333-1.167L171.5 16l1.167 2.333L175 19.5l-2.333 1.167L171.5 23z"/>
</g>
<g>
<path d="M81.117 13.904c-2.139-4.838-6.274-9.113-11.25-9.324-4.976-.211-7.828 3.779-6.367 7.309 1.461 3.53 4.94 4.177 16.227 7.202 3.204.858 3.528-.35 1.39-5.187z" stroke="#22AEB7" stroke-width="4"/>
<path d="M93.833 13.904c2.138-4.838 6.273-9.113 11.25-9.324 4.975-.211 7.828 3.779 6.367 7.309-1.462 3.53-4.94 4.177-16.227 7.202-3.205.858-3.528-.35-1.39-5.187z" stroke="#38C9C6" stroke-width="4"/>
<path d="M87.128 11c-9.738 0-3.907 11.145 0 11.145 3.908 0 9.74-11.145 0-11.145z" fill="#46DDDA"/>
<path fill="#6133B4" d="M62 33h52v34H62zM56 21h64v12H56z"/>
<path fill-opacity=".5" fill="#FFF" style="mix-blend-mode:soft-light" d="M32 30h26v34H32z" transform="translate(56 3)"/>
<path fill="#8EEDF6" d="M88 33h6v34h-6z"/>
<path fill="#3BCAD7" d="M82 33h6v34h-6zM76 21h12v12H76z"/>
<path fill="#8EEDF6" d="M88 21h12v12H88z"/>
<path fill-opacity=".2" fill="#000" style="mix-blend-mode:multiply" d="M6 30h26v6H6zM20 18h12v6H20zM0 24h20v6H0zM44 24h20v6H44zM32 18h12v6H32zM6 58h26v6H6zM32 30h26v6H32zM32 58h26v6H32z" transform="translate(56 3)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,5 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"> <?xml version="1.0" encoding="UTF-8"?>
<g fill="none" fill-rule="evenodd" stroke="#A5A1AC" stroke-width="2"> <svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M1 11L11 1M11 11L1 1"/> <title>Icon/Close</title>
<g id="Modals" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Shop-Modals" transform="translate(-183.000000, -655.000000)" fill="#878190" fill-rule="nonzero">
<g id="Icon/Close" transform="translate(183.000000, 655.000000)">
<polygon id="Mask" points="12.1973467 2 14 3.80265326 9.80187117 8 14 12.1973467 12.1973467 14 8 9.80187117 3.80265326 14 2 12.1973467 6.19812883 8 2 3.80265326 3.80265326 2 8 6.19812883"></polygon>
</g>
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 747 B

View File

@@ -122,6 +122,11 @@
font-weight: bold; font-weight: bold;
} }
.custom-control-input {
z-index: -1;
opacity: 0;
}
.box:hover { .box:hover {
cursor: pointer; cursor: pointer;
opacity: 0.7; opacity: 0.7;

View File

@@ -3,7 +3,7 @@
<creator-intro /> <creator-intro />
<profileModal /> <profileModal />
<report-flag-modal /> <report-flag-modal />
<send-gems-modal /> <send-gift-modal />
<select-user-modal /> <select-user-modal />
<b-navbar <b-navbar
id="habitica-menu" id="habitica-menu"
@@ -747,7 +747,7 @@ import creatorIntro from '../creatorIntro';
import notificationMenu from './notificationsDropdown'; import notificationMenu from './notificationsDropdown';
import profileModal from '../userMenu/profileModal'; import profileModal from '../userMenu/profileModal';
import reportFlagModal from '../chat/reportFlagModal'; import reportFlagModal from '../chat/reportFlagModal';
import sendGemsModal from '@/components/payments/sendGemsModal'; import sendGiftModal from '@/components/payments/sendGiftModal';
import selectUserModal from '@/components/payments/selectUserModal'; import selectUserModal from '@/components/payments/selectUserModal';
import sync from '@/mixins/sync'; import sync from '@/mixins/sync';
import userDropdown from './userDropdown'; import userDropdown from './userDropdown';
@@ -759,7 +759,7 @@ export default {
notificationMenu, notificationMenu,
profileModal, profileModal,
reportFlagModal, reportFlagModal,
sendGemsModal, sendGiftModal,
selectUserModal, selectUserModal,
userDropdown, userDropdown,
}, },

View File

@@ -198,7 +198,7 @@ export default {
appState.newGroup = false; appState.newGroup = false;
appState.group = pick(this.amazonPayments.group, ['_id', 'memberCount', 'name']); appState.group = pick(this.amazonPayments.group, ['_id', 'memberCount', 'name']);
} }
} else if (paymentType.indexOf('gift-') === 0) { } else if (paymentType && paymentType.indexOf('gift-') === 0) {
appState.gift = this.amazonPayments.gift; appState.gift = this.amazonPayments.gift;
appState.giftReceiver = this.amazonPayments.giftReceiver; appState.giftReceiver = this.amazonPayments.giftReceiver;
} else if (paymentType === 'gems') { } else if (paymentType === 'gems') {

View File

@@ -1,5 +1,6 @@
<template> <template>
<div class="payments-column mx-auto mt-auto"> <div class="payments-column mx-auto mt-auto">
<h4>{{ $t('choosePaymentMethod') }}</h4>
<button <button
v-if="stripeAvailable" v-if="stripeAvailable"
class="btn btn-primary payment-button payment-item with-icon" class="btn btn-primary payment-button payment-item with-icon"
@@ -80,6 +81,13 @@
cursor: default !important; cursor: default !important;
} }
} }
h4 {
font-size: 0.875rem;
font-weight: bold;
text-align: center;
margin-bottom: 1rem;
}
</style> </style>
<script> <script>

View File

@@ -14,15 +14,44 @@
</div> </div>
<h2 <h2
v-else v-else
class="ml-2" class="d-flex flex-column mx-auto align-items-center"
> >
{{ $t('sendGift') }} {{ $t('sendAGift') }}
</h2> </h2>
<div
v-if="currentEvent && currentEvent.promo === 'g1g1'"
class="g1g1-margin d-flex flex-column align-items-center"
>
<div
class="svg-big-gift"
v-once
v-html="icons.bigGift"
></div>
</div>
<div
v-else
class="d-flex flex-column align-items-center">
<div
class="svg-big-gift"
v-once
v-html="icons.bigGift"
></div>
</div>
<div class="d-flex flex-column align-items-center"> <div class="d-flex flex-column align-items-center">
<div <div
class="modal-close" v-if="currentEvent && currentEvent.promo === 'g1g1'"
class="g1g1-modal-close"
@click="close()" @click="close()"
> >
<div
class="g1g1-svg-icon"
v-html="icons.close"
></div>
</div>
<div
v-else
class="modal-close"
@click="close()">
<div <div
class="svg-icon" class="svg-icon"
v-html="icons.close" v-html="icons.close"
@@ -42,6 +71,7 @@
v-model="userSearchTerm" v-model="userSearchTerm"
class="form-control" class="form-control"
type="text" type="text"
ref="textBox"
:placeholder="$t('usernameOrUserId')" :placeholder="$t('usernameOrUserId')"
:class="{ :class="{
'input-valid': foundUser._id, 'input-valid': foundUser._id,
@@ -70,15 +100,20 @@
<div <div
v-else v-else
> >
{{ $t('selectGift') }} {{ $t('next') }}
</div> </div>
</button> </button>
<a <div
class="cancel-link mx-auto mt-3" v-if="currentEvent && currentEvent.promo ==='g1g1'"
@click="close()" class="g1g1-cancel d-flex justify-content-center"
v-html="$t('cancel')"
@click="close()"
> >
{{ $t('cancel') }} {{ $t('cancel') }}
</a> </div>
<div
v-else>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -110,13 +145,16 @@
@import '~@/assets/scss/mixins.scss'; @import '~@/assets/scss/mixins.scss';
#select-user-modal { #select-user-modal {
.modal-content {
width:448px;
}
.input-group { .input-group {
margin-top: 0rem; margin-top: 0rem;
} }
.modal-dialog { .modal-dialog {
width: 29.5rem; width: 448px;
margin-top: 25vh;
} }
.modal-footer { .modal-footer {
@@ -126,7 +164,16 @@
margin: 0rem 0.25rem 0.25rem 0.25rem; margin: 0rem 0.25rem 0.25rem 0.25rem;
} }
} }
}
body.modal-open .modal {
display: flex !important;
height: 100%;
}
body.modal-open .modal .modal-dialog {
margin: auto;
}
}
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -146,12 +193,12 @@
.g1g1 { .g1g1 {
background-image: url('~@/assets/images/g1g1-send.png'); background-image: url('~@/assets/images/g1g1-send.png');
background-size: 472px 152px; background-size: 446px 152px;
width: 470px; width: 446px;
height: 152px; height: 152px;
margin: -1rem 0rem 0rem -1rem; margin: -16px 0px 0px -16px;
border-radius: 0.3rem 0.3rem 0rem 0rem; border-radius: 4.8px 4.8px 0px 0px;
padding: 1.5rem; padding: 24px;
color: $white; color: $white;
h1 { h1 {
@@ -169,6 +216,16 @@
} }
} }
.g1g1-margin {
margin-top: 24px;
}
.g1g1-cancel {
margin-top: 16px;
color: $blue-10;
cursor: pointer;
}
.g1g1-fine-print { .g1g1-fine-print {
color: $gray-100; color: $gray-100;
background-color: $gray-700; background-color: $gray-700;
@@ -176,6 +233,29 @@
line-height: 1.33; line-height: 1.33;
} }
.g1g1-modal-close {
position: absolute;
width: 18px;
height: 18px;
padding: 4px;
right: 16px;
top: 16px;
cursor: pointer;
.g1g1-svg-icon {
width: 12px;
height: 12px;
& ::v-deep svg path {
fill: #FFFFFF;
}
}
}
.g1g1-modal-dialog {
margin-top: 10vh;
}
.input-error { .input-error {
color: $red-50; color: $red-50;
font-size: 90%; font-size: 90%;
@@ -192,6 +272,18 @@
border-color: $purple-500; border-color: $purple-500;
} }
h2 {
font-size: 1.25rem;
line-height: 1.75rem;
color: $purple-300;
padding-top: 1rem;
}
.svg-big-gift {
width: 176px;
height: 64px;
}
.modal-close { .modal-close {
position: absolute; position: absolute;
width: 18px; width: 18px;
@@ -206,14 +298,17 @@
height: 12px; height: 12px;
} }
} }
</style> </style>
<script> <script>
// import { nextTick } from 'vue'; // may not need this? I don't know!
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import find from 'lodash/find'; import find from 'lodash/find';
import isUUID from 'validator/lib/isUUID'; import isUUID from 'validator/lib/isUUID';
import { mapState } from '@/libs/store'; import { mapState } from '@/libs/store';
import closeIcon from '@/assets/svg/close.svg'; import closeIcon from '@/assets/svg/close.svg';
import bigGiftIcon from '@/assets/svg/big-gift.svg';
export default { export default {
data () { data () {
@@ -223,6 +318,7 @@ export default {
foundUser: {}, foundUser: {},
icons: Object.freeze({ icons: Object.freeze({
close: closeIcon, close: closeIcon,
bigGift: bigGiftIcon,
}), }),
}; };
}, },
@@ -281,7 +377,7 @@ export default {
this.foundUser = result; this.foundUser = result;
}, 500), }, 500),
selectUser () { selectUser () {
this.$root.$emit('habitica::send-gems', this.foundUser); this.$root.$emit('habitica::send-gift', this.foundUser);
this.close(); this.close();
}, },
onHide () { onHide () {

View File

@@ -0,0 +1,659 @@
<template>
<b-modal
id="send-gift"
:hide-footer="true"
:hide-header="true"
size="md"
@hide="onHide()"
>
<div>
<!-- header -->
<div
class="modal-close"
@click="close()"
>
<div
class="icon-close"
v-html="icons.closeIcon"
>
</div>
</div>
<div>
<h2 class="d-flex flex-column mx-auto align-items-center">
{{ $t('sendAGift') }}
</h2>
</div>
<!-- user avatar -->
<div
v-if="userReceivingGift"
class="modal-body"
>
<avatar
:member="userReceivingGift"
:hideClassBadge="true"
class="d-flex flex-column mx-auto align-items-center"
/>
<div class="avatar-spacer"></div>
<div class="d-flex flex-column mx-auto align-items-center display-name">
<!-- user display name and username -->
<user-link
:user-id="displayName"
:name="displayName"
:backer="userBacker"
:contributor="userContributor"
:class="display-name"
/>
</div>
<div class="d-flex flex-column mx-auto align-items-center user-name">
@{{ userName }}
</div>
</div>
<!-- menu area -->
<div class="row">
<div class="col-md-8 offset-md-2 text-center nav">
<div
class="nav-link"
:class="{active: selectedPage === 'subscription'}"
@click="selectPage('subscription')"
>
{{ $t('subscription') }}
</div>
<div
class="nav-link"
:class="{active: selectedPage !== 'subscription'}"
@click="selectPage('buyGems')"
>
{{ $t('gems') }}
</div>
</div>
</div>
<!-- subscriber block -->
<subscription-options
v-show="selectedPage === 'subscription'"
class="subscribe-option"
:userReceivingGift="userReceivingGift"
/>
<!-- gem block -->
<div
v-show="selectedPage === 'buyGems'"
>
<div class="gem-group">
<!-- buy gems with money -->
<label v-once>
{{ $t('howManyGemsPurchase') }}
</label>
<div class="d-flex flex-row align-items-center justify-content-center">
<div
class="gray-circle"
@click="gift.gems.amount <= 0
? gift.gems.amount = 0
: gift.gems.amount--"
>
<div
class="icon-negative"
v-html="icons.negativeIcon"
></div>
</div>
<div class="input-group">
<div class="input-group-prepend input-group-icon align-items-center">
<div
class="icon-gem"
v-html="icons.gemIcon"
></div>
</div>
<input
id="gemsForm"
v-model.number="gift.gems.amount"
class="form-control"
max="9999"
>
</div>
<div
class="gray-circle"
@click="gift.gems.amount++"
>
<div
class="icon-positive"
v-html="icons.positiveIcon"
></div>
</div>
</div>
<!-- the word "total" -->
<div class="buy-gem-total">
{{ $t('sendGiftTotal') }}
</div>
<!-- the actual dollar amount -->
<div class="buy-gem-amount">
<span>
{{ formatter.format(totalGems) }}
</span>
</div>
<!-- change to sending own gems page -->
<div
:class="{active: selectedPage === 'ownGems'}"
class="gem-state-change"
@click="selectPage('ownGems')"
>
{{ $t('wantToSendOwnGems') }}
</div>
</div>
<!-- paying for gems -->
<payments-buttons
class="payment-buttons"
:stripe-fn="() => redirectToStripe({gift, uuid: userReceivingGift._id, receiverName})"
:paypal-fn="() => openPaypalGift({
gift: gift, giftedTo: userReceivingGift._id, receiverName,
})"
:amazon-data="{type: 'single', gift, giftedTo: userReceivingGift._id, receiverName}"
/>
</div>
</div>
<!-- send gems from balance -->
<div
v-show="selectedPage === 'ownGems'"
>
<div class="gem-group">
<label v-once>
{{ $t('howManyGemsSend') }}
</label>
<div class="d-flex align-items-center justify-content-center">
<div
class="gray-circle"
@click="gift.gems.amount <= 0
? gift.gems.amount = 0
: gift.gems.amount--"
>
<div
class="icon-negative"
v-html="icons.negativeIcon"
></div>
</div>
<div class="input-group">
<div class="input-group-prepend input-group-icon align-items-center">
<div
class="icon-gem"
v-html="icons.gemIcon"
></div>
</div>
<input
id="gemsForm"
v-model="gift.gems.amount"
class="form-control"
:max="maxGems"
>
</div>
<div
class="gray-circle"
@click="gift.gems.amount < maxGems
? gift.gems.amount++
: gift.gems.amount = maxGems"
>
<div
class="icon-positive"
v-html="icons.positiveIcon"
></div>
</div>
</div>
<div class="align-items-middle">
<div class="d-flex justify-content-center align-items-middle">
<span class="balance-text">
{{ $t('yourBalance') }}
</span>
<span
class="icon-gem balance-gem-margin"
style="display: inline-block;"
v-html="icons.gemIcon"
></span>
<span
class="balance-gems"
>
{{ maxGems }}
</span>
</div>
</div>
<div class="d-flex flex-column justify-content-center align-items-middle mt-3">
<button
v-if="fromBal"
class="btn btn-primary mx-auto mt-2"
type="submit"
:disabled="sendingInProgress"
@click="sendGift()"
>
{{ $t("send") }}
</button>
</div>
<!-- change to buying gems page -->
<div
:class="{active: selectedPage === 'buyGems'}"
class="gem-state-change"
@click="selectPage('buyGems')"
>
{{ $t('needToPurchaseGems') }}
</div>
</div>
</div>
</b-modal>
</template>
<style lang="scss">
@import '~@/assets/scss/mixins.scss';
#send-gift {
.modal-dialog {
max-width: 448px;
}
.modal-content {
width: 448px;
border-radius: 8px;
box-shadow: 0 14px 28px 0 rgba(26, 24, 29, 0.24), 0 10px 10px 0 rgba(26, 24, 29, 0.28);
}
.modal-body{
padding: 0px;
}
.modal-close {
position: absolute;
right: 16px;
top: 16px;
cursor: pointer;
.icon-close {
width: 18px;
height: 18px;
vertical-align: middle;
& ::v-deep svg path {
fill: #878190;
}
& :hover {
fill: #686274;
}
}
}
#subscription-form .subscribe-option {
background: #F9F9F9;
}
#subscription-form .selected {
background: rgba(213, 200, 255, 0.32);
// using rgba for transparency
}
}
</style>
<style scoped lang="scss">
@import '~@/assets/scss/colors.scss';
h2 {
color: $purple-300;
padding-top: 2rem;
}
.avatar-spacer {
height: 9px;
}
.display-name {
font-size: 0.875rem;
font-weight: bold;
line-height: 1.71;
margin: 0px 6px 0 20px;
}
.display-name a:hover{
text-decoration: none;
}
.user-name {
font-size: 0.75rem;
line-height: 1.33;
text-align: center;
color: $gray-100;
padding-bottom: 16px;
}
.row {
background-color: $gray-700;
margin: 0 0 0 0;
min-height: 32px;
}
.nav {
font-weight: bold;
font-size: 0.75rem;
min-height: 32px;
padding: 16px 0 0 0;
justify-content: center;
}
.nav-link {
color: $gray-100;
display: inline-block;
padding: 0px 8px 6px 8px;
&.active {
color: $purple-300;
border-bottom: 2px solid $purple-400;
}
&:hover {
color: $purple-300;
border-bottom: 2px solid $purple-400;
cursor: pointer;
}
}
.gem-group {
padding: 0 0 24px 0;
background-color: $gray-700;
margin: 0 0 0 0;
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px
}
label {
color: $gray-50;
font-size: 0.875rem;
font-weight: bold;
line-height: 1.71;
margin: 12px 0 16px 0;
width: 100%;
text-align: center;
}
.input-group {
width: 94px;
height: 32px;
margin: 0px 16px 0px 16px;
padding: 0;
border-radius: 2px;
border: solid 1px $gray-400;
background-color: $white;
}
.gray-circle {
border-radius: 100%;
border: solid 2px $gray-300;
width: 32px;
height: 32px;
cursor: pointer;
&:hover {
border-color: $purple-400;
}
}
.gray-circle:hover{
.icon-positive, .icon-negative {
& ::v-deep svg path {
fill: $purple-400;
}
}
}
.icon-gem {
width: 16px;
height: 16px;
margin-bottom: 4px;
}
.icon-positive, .icon-negative {
width: 10px;
height: 10px;
margin: 4px auto;
& ::v-deep svg path {
fill: $gray-300;
}
}
.buy-gem-total {
font-size: 0.875rem;
font-weight: bold;
line-height: 1.71;
padding-top: 24px;
text-align: center;
height: 28px;
}
.buy-gem-amount {
font-size: 1.25rem;
font-weight: bold;
line-height: 1.4;
margin: 16px 0 24px 0;
text-align: center;
height: 28px;
color: $green-10;
}
.balance-text {
font-size: 0.75rem;
font-weight: bold;
color: $gray-100;
line-height: 1.33;
margin: 12px 0px 0px 70px;
}
.balance-gem-margin {
margin: 8px 4px 0px 8px;
}
.balance-gems {
font-size: 0.75rem;
color: $gray-100;
line-height: 1.33;
margin: 12px 71px 0px 4px;
}
.gem-state-change {
color: $blue-10;
font-size: 0.875rem;
min-height: 24px;
margin: 16px 0 0;
text-align: center;
cursor: pointer;
}
.subscribe-option {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
padding-bottom: 24px;
}
.payment-buttons {
padding: 24px 0;
}
</style>
<script>
// libs imports
import { mapState } from '@/libs/store';
// mixins imports
import paymentsMixin from '../../mixins/payments';
// component imports
import avatar from '../avatar';
import userLink from '../userLink';
import subscriptionOptions from '../settings/subscriptionOptions.vue';
import paymentsButtons from '@/components/payments/buttons/list';
// svg imports
import closeIcon from '@/assets/svg/close.svg';
import gemIcon from '@/assets/svg/gem.svg';
import positiveIcon from '@/assets/svg/positive.svg';
import negativeIcon from '@/assets/svg/negative.svg';
export default {
components: {
avatar,
subscriptionOptions,
paymentsButtons,
userLink,
},
mixins: [
paymentsMixin,
],
data () {
return {
subscription: {
key: '',
},
icons: Object.freeze({
closeIcon,
gemIcon,
positiveIcon,
negativeIcon,
}),
userReceivingGift: {
profile: '',
},
name: '',
display: '',
selectedPage: 'subscription',
gift: {
type: 'gems',
gems: {
amount: 0,
fromBalance: true,
},
},
sendingInProgress: false,
amazonPayments: {},
gemCost: 1,
};
},
computed: {
...mapState({
userLoggedIn: 'user.data',
}),
userName () {
const userName = this.userReceivingGift.auth
&& this.userReceivingGift.auth.local
&& this.userReceivingGift.auth.local.username;
return userName;
},
displayName () {
const displayName = this.userReceivingGift.profile.name;
return displayName;
},
userBacker () {
const userBacker = this.userReceivingGift.backer;
return userBacker;
},
userContributor () {
const userContributor = this.userReceivingGift.contributor;
return userContributor;
},
tierIcon () {
if (this.isNPC) {
return this.icons.tierNPC;
}
return this.icons[`tier${this.level}`];
},
fromBal () {
return this.gift.type === 'gems' && this.gift.gems.fromBalance;
},
maxGems () {
const maxGems = this.fromBal ? this.userLoggedIn.balance * 4 : 9999;
return maxGems;
},
formatter () {
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
});
return formatter;
},
totalGems () {
const totalGems = this.gift.gems.amount * 0.25;
return totalGems;
},
receiverName () {
if (
this.userReceivingGift.auth
&& this.userReceivingGift.auth.local
&& this.userReceivingGift.auth.local.username
) {
return this.userReceivingGift.auth.local.username;
}
return this.userReceivingGift.profile.name;
},
},
watch: {
startingPage () {
this.selectedPage = this.startingPage;
},
},
mounted () {
this.$root.$on('habitica::send-gift', data => {
this.userReceivingGift = data;
if (this.$store.state.giftModalOptions.startingPage) {
this.selectedPage = this.$store.state.giftModalOptions.startingPage;
this.$store.state.giftModalOptions.startingPage = '';
this.selectPage(this.selectedPage);
} else {
this.selectPage(this.startingPage);
}
this.setGemDefaults();
this.$root.$emit('bv::show::modal', 'send-gift');
});
},
methods: {
close () {
this.$root.$emit('bv::hide::modal', 'send-gift');
},
setGemDefaults () {
if (this.selectedPage === 'buyGems') {
this.gift.gems.amount = 20;
} else if (this.selectedPage === 'ownGems') {
this.gift.gems.amount = 1;
} else {
this.gift.gems.amount = 0;
}
},
selectPage (page) {
if (page === this.selectedPage) return;
if (page === 'buyGems') this.selectedPage = 'buyGems';
if (page === 'buyGems' && this.selectedPage === 'ownGems') return;
this.selectedPage = page || 'subscription';
this.setGemDefaults();
},
async sendGift () {
this.sendingInProgress = true;
await this.$store.dispatch('members:transferGems', {
toUserId: this.userReceivingGift._id,
gemAmount: this.gift.gems.amount,
});
this.close();
setTimeout(() => { // wait for the send gem modal to be closed
this.$root.$emit('habitica:payment-success', {
paymentMethod: 'balance',
paymentCompleted: true,
paymentType: 'gift-gems-balance',
gift: {
gems: {
amount: this.gift.gems.amount,
},
},
giftReceiver: this.receiverName,
});
}, 500);
},
onHide () {
this.sendingInProgress = false;
},
},
};
</script>

View File

@@ -450,10 +450,6 @@
background-color: $white; background-color: $white;
} }
.subscribe-option {
border-bottom: 1px solid $gray-600;
}
.svg-amazon-pay { .svg-amazon-pay {
width: 208px; width: 208px;
} }

View File

@@ -10,10 +10,17 @@
:value="block.key" :value="block.key"
class="subscribe-option pt-2 pl-5 pb-3 mb-0" class="subscribe-option pt-2 pl-5 pb-3 mb-0"
:class="{selected: subscription.key === block.key}" :class="{selected: subscription.key === block.key}"
@click.native="subscription.key = block.key" @click.native="updateSubscriptionData(block.key)"
> >
<!-- eslint-enable vue/no-use-v-if-with-v-for --> <!-- eslint-enable vue/no-use-v-if-with-v-for -->
<div <div
v-if="userReceivingGift && userReceivingGift._id"
class="subscription-text ml-2 mb-1"
v-html="$t('giftSubscriptionRateText', {price: block.price, months: block.months})"
>
</div>
<div
v-else
class="subscription-text ml-2 mb-1" class="subscription-text ml-2 mb-1"
v-html="$t('subscriptionRateText', {price: block.price, months: block.months})" v-html="$t('subscriptionRateText', {price: block.price, months: block.months})"
> >
@@ -25,7 +32,18 @@
</div> </div>
</b-form-radio> </b-form-radio>
</b-form-group> </b-form-group>
<!-- payment buttons first is for gift subs and the second is for renewing subs -->
<payments-buttons <payments-buttons
v-if="userReceivingGift && userReceivingGift._id"
:disabled="!subscription.key"
:stripe-fn="() => redirectToStripe({gift, uuid: userReceivingGift._id, receiverName})"
:paypal-fn="() => openPaypalGift({
gift: gift, giftedTo: userReceivingGift._id, receiverName,
})"
:amazon-data="{type: 'single', gift, giftedTo: userReceivingGift._id, receiverName}"
/>
<payments-buttons
v-else
:disabled="!subscription.key" :disabled="!subscription.key"
:stripe-fn="() => redirectToStripe({ :stripe-fn="() => redirectToStripe({
subscription: subscription.key, subscription: subscription.key,
@@ -43,6 +61,7 @@
<style lang="scss"> <style lang="scss">
@import '~@/assets/scss/colors.scss'; @import '~@/assets/scss/colors.scss';
#subscription-form { #subscription-form {
.custom-control .custom-control-label::before, .custom-control .custom-control-label::before,
.custom-radio .custom-control-input:checked ~ .custom-control-label::after { .custom-radio .custom-control-input:checked ~ .custom-control-label::after {
@@ -101,11 +120,22 @@ export default {
mixins: [ mixins: [
paymentsMixin, paymentsMixin,
], ],
props: {
userReceivingGift: {
type: Object,
default () {},
},
},
data () { data () {
return { return {
subscription: { subscription: {
key: null, key: 'basic_earned',
}, },
gift: {
type: 'subscription',
subscription: { key: 'basic_earned' },
},
receiverName: '',
}; };
}, },
computed: { computed: {
@@ -114,7 +144,6 @@ export default {
}, },
subscriptionBlocksOrdered () { subscriptionBlocksOrdered () {
const subscriptions = filter(subscriptionBlocks, o => o.discount !== true); const subscriptions = filter(subscriptionBlocks, o => o.discount !== true);
return sortBy(subscriptions, [o => o.months]); return sortBy(subscriptions, [o => o.months]);
}, },
}, },
@@ -131,6 +160,10 @@ export default {
return '<span class="subscription-bubble px-2 py-1">Gem cap at 25</span>'; return '<span class="subscription-bubble px-2 py-1">Gem cap at 25</span>';
} }
}, },
updateSubscriptionData (key) {
this.subscription.key = key;
if (this.userReceivingGift._id) this.gift.subscription.key = key;
},
}, },
}; };
</script> </script>

View File

@@ -625,6 +625,11 @@
margin-bottom: 0; margin-bottom: 0;
} }
.custom-control-input {
z-index: -1;
opacity: 0;
}
.modal-header { .modal-header {
.form-group { .form-group {
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@@ -992,7 +992,8 @@ export default {
axios.post(`/api/v4/user/block/${this.user._id}`); axios.post(`/api/v4/user/block/${this.user._id}`);
}, },
openSendGemsModal () { openSendGemsModal () {
this.$root.$emit('habitica::send-gems', this.user); this.$store.state.giftModalOptions.startingPage = 'buyGems';
this.$root.$emit('habitica::send-gift', this.user);
}, },
adminTurnOnShadowMuting () { adminTurnOnShadowMuting () {
if (!this.hero.flags) { if (!this.hero.flags) {

View File

@@ -124,6 +124,9 @@ export default function () {
profileOptions: { profileOptions: {
startingPage: '', startingPage: '',
}, },
giftModalOptions: {
startingPage: '',
},
rageModalOptions: { rageModalOptions: {
npc: '', npc: '',
}, },

View File

@@ -129,6 +129,7 @@
"sendGiftHeading": "Send Gift to <%= name %>", "sendGiftHeading": "Send Gift to <%= name %>",
"sendGiftGemsBalance": "From <%= number %> Gems", "sendGiftGemsBalance": "From <%= number %> Gems",
"sendGiftCost": "Total: $<%= cost %> USD", "sendGiftCost": "Total: $<%= cost %> USD",
"sendGiftTotal": "Total:",
"sendGiftFromBalance": "From Balance", "sendGiftFromBalance": "From Balance",
"sendGiftPurchase": "Purchase", "sendGiftPurchase": "Purchase",
"sendGiftMessagePlaceholder": "Personal message (optional)", "sendGiftMessagePlaceholder": "Personal message (optional)",
@@ -367,5 +368,6 @@
"dayStart": "<strong>Day start</strong>: <%= startTime %>", "dayStart": "<strong>Day start</strong>: <%= startTime %>",
"viewStatus": "Status", "viewStatus": "Status",
"lastCompleted": "Last completed", "lastCompleted": "Last completed",
"you": "<strong>You</strong>" "you": "<strong>You</strong>",
"chatTemporarilyUnavailable": "Chat is temporarily unavailable. Please try again later."
} }

View File

@@ -125,6 +125,7 @@
"unsubscribeAllPush": "Check to Unsubscribe from all Push Notifications", "unsubscribeAllPush": "Check to Unsubscribe from all Push Notifications",
"correctlyUnsubscribedEmailType": "Correctly unsubscribed from \"<%= emailType %>\" emails.", "correctlyUnsubscribedEmailType": "Correctly unsubscribed from \"<%= emailType %>\" emails.",
"subscriptionRateText": "Recurring <strong>$<%= price %> USD</strong> every <strong><%= months %> months</strong>", "subscriptionRateText": "Recurring <strong>$<%= price %> USD</strong> every <strong><%= months %> months</strong>",
"giftSubscriptionRateText": "<strong>$<%= price %> USD</strong> for <strong><%= months %> months</strong>",
"benefits": "Benefits", "benefits": "Benefits",
"coupon": "Coupon", "coupon": "Coupon",
"couponText": "We sometimes have events and give out promo codes for special gear. (eg, those who stop by our Wondercon booth)", "couponText": "We sometimes have events and give out promo codes for special gear. (eg, those who stop by our Wondercon booth)",
@@ -204,11 +205,13 @@
"transaction_gift_send": "Gifted to", "transaction_gift_send": "Gifted to",
"transaction_gift_receive": "Received from", "transaction_gift_receive": "Received from",
"transaction_create_challenge": "Created challenge", "transaction_create_challenge": "Created challenge",
"transaction_create_bank_challenge": "Created bank challenge",
"transaction_create_guild": "Created guild", "transaction_create_guild": "Created guild",
"transaction_change_class": "Changed class", "transaction_change_class": "Changed class",
"transaction_rebirth": "Used Orb of Rebirth", "transaction_rebirth": "Used Orb of Rebirth",
"transaction_release_pets": "Released pets", "transaction_release_pets": "Released pets",
"transaction_release_mounts": "Released mounts", "transaction_release_mounts": "Released mounts",
"transaction_reroll": "Used Fortify Potion", "transaction_reroll": "Used Fortify Potion",
"transaction_subscription_perks": "From subscription perk" "transaction_subscription_perks": "From subscription perk",
"transaction_admin_update_balance": "Admin given"
} }

View File

@@ -3,6 +3,10 @@
"subscriptions": "Subscriptions", "subscriptions": "Subscriptions",
"viewSubscriptions": "View Subscriptions", "viewSubscriptions": "View Subscriptions",
"sendGems": "Send Gems", "sendGems": "Send Gems",
"howManyGemsPurchase": "How many Gems would you like to purchase?",
"howManyGemsSend":"How many Gems would you like to send?",
"needToPurchaseGems": "Need to purchase Gems as a gift?",
"wantToSendOwnGems": "Want to send your own Gems?",
"buyGemsGold": "Buy Gems with Gold", "buyGemsGold": "Buy Gems with Gold",
"mustSubscribeToPurchaseGems": "Must subscribe to purchase gems with GP", "mustSubscribeToPurchaseGems": "Must subscribe to purchase gems with GP",
"reachedGoldToGemCapQuantity": "Your requested amount <%= quantity %> exceeds the amount you can buy for this month (<%= convCap %>). The full amount becomes available within the first three days of each month. Thanks for subscribing!", "reachedGoldToGemCapQuantity": "Your requested amount <%= quantity %> exceeds the amount you can buy for this month (<%= convCap %>). The full amount becomes available within the first three days of each month. Thanks for subscribing!",
@@ -198,5 +202,6 @@
"needToUpdateCard": "Need to update your card?", "needToUpdateCard": "Need to update your card?",
"readyToResubscribe": "Are you ready to resubscribe?", "readyToResubscribe": "Are you ready to resubscribe?",
"cancelYourSubscription": "Cancel your subscription?", "cancelYourSubscription": "Cancel your subscription?",
"cancelSubAlternatives": "If you're having technical problems or Habitica doesn't seem to be working out for you, please consider <a href='mailto:admin@habitica.com'>contacting us</a>. We want to help you get the most from Habitica." "cancelSubAlternatives": "If you're having technical problems or Habitica doesn't seem to be working out for you, please consider <a href='mailto:admin@habitica.com'>contacting us</a>. We want to help you get the most from Habitica.",
"sendAGift": "Send Gift"
} }

View File

@@ -42,6 +42,18 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation { // esli
this.canUserPurchase(user, item); this.canUserPurchase(user, item);
} }
canUserPurchase (user, item) {
if (item && item.prereqQuests) {
for (const prereq of item.prereqQuests) {
if (!user.achievements.quests[prereq]) {
throw new NotAuthorized(this.i18n('mustComplete', { quest: prereq }));
}
}
}
super.canUserPurchase(user, item);
}
async executeChanges (user, item, req) { async executeChanges (user, item, req) {
if ( if (
!user.items.quests[item.key] !user.items.quests[item.key]

View File

@@ -46,20 +46,22 @@ export class BuyQuestWithGoldOperation extends AbstractGoldItemOperation { // es
throw new NotAuthorized(this.i18n('questNotGoldPurchasable', { key })); throw new NotAuthorized(this.i18n('questNotGoldPurchasable', { key }));
} }
this.checkPrerequisites(user, key);
this.canUserPurchase(user, item); this.canUserPurchase(user, item);
} }
checkPrerequisites (user, questKey) { canUserPurchase (user, item) {
const item = content.quests[questKey]; if (this.getItemKey() === 'lostMasterclasser1' && !this.userAbleToStartMasterClasser(user)) {
if (questKey === 'lostMasterclasser1' && !this.userAbleToStartMasterClasser(user)) {
throw new NotAuthorized(this.i18n('questUnlockLostMasterclasser')); throw new NotAuthorized(this.i18n('questUnlockLostMasterclasser'));
} }
if (item && item.previous && !user.achievements.quests[item.previous]) { if (item && item.prereqQuests) {
throw new NotAuthorized(this.i18n('mustComplete', { quest: item.previous })); for (const prereq of item.prereqQuests) {
if (!user.achievements.quests[prereq]) {
throw new NotAuthorized(this.i18n('mustComplete', { quest: prereq }));
}
}
} }
super.canUserPurchase(user, item);
} }
executeChanges (user, item, req) { executeChanges (user, item, req) {

View File

@@ -1,3 +1,4 @@
import moment from 'moment';
import nconf from 'nconf'; import nconf from 'nconf';
import { authWithHeaders } from '../../middlewares/auth'; import { authWithHeaders } from '../../middlewares/auth';
import { model as Group } from '../../models/group'; import { model as Group } from '../../models/group';
@@ -22,7 +23,11 @@ import { getMatchesByWordArray } from '../../libs/stringUtils';
import bannedSlurs from '../../libs/bannedSlurs'; import bannedSlurs from '../../libs/bannedSlurs';
import apiError from '../../libs/apiError'; import apiError from '../../libs/apiError';
import highlightMentions from '../../libs/highlightMentions'; import highlightMentions from '../../libs/highlightMentions';
import { getAnalyticsServiceByEnvironment } from '../../libs/analyticsService';
const analytics = getAnalyticsServiceByEnvironment();
const ACCOUNT_MIN_CHAT_AGE = Number(nconf.get('ACCOUNT_MIN_CHAT_AGE'));
const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map(email => ({ email, canSend: true })); const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map(email => ({ email, canSend: true }));
/** /**
@@ -188,6 +193,17 @@ api.postChat = {
throw new NotAuthorized(res.t('messageGroupChatSpam')); throw new NotAuthorized(res.t('messageGroupChatSpam'));
} }
// Check if account is newer than the minimum age for chat participation
if (moment().diff(user.auth.timestamps.created, 'minutes') < ACCOUNT_MIN_CHAT_AGE) {
analytics.track('chat age error', {
uuid: user._id,
hitType: 'event',
category: 'behavior',
headers: req.headers,
});
throw new BadRequest(res.t('chatTemporarilyUnavailable'));
}
const sanitizedMessageText = sanitizeMessageText(req.body.message); const sanitizedMessageText = sanitizeMessageText(req.body.message);
const [message, mentions, mentionedMembers] = await highlightMentions(sanitizedMessageText); const [message, mentions, mentionedMembers] = await highlightMentions(sanitizedMessageText);
let client = req.headers['x-client'] || '3rd Party'; let client = req.headers['x-client'] || '3rd Party';

View File

@@ -267,7 +267,11 @@ api.updateHero = {
const hero = await User.findById(heroId).exec(); const hero = await User.findById(heroId).exec();
if (!hero) throw new NotFound(res.t('userWithIDNotFound', { userId: heroId })); if (!hero) throw new NotFound(res.t('userWithIDNotFound', { userId: heroId }));
if (updateData.balance) hero.balance = updateData.balance; if (updateData.balance) {
await hero.updateBalance(updateData.balance - hero.balance, 'admin_update_balance', '', 'Given by Habitica staff');
hero.balance = updateData.balance;
}
// give them gems if they got an higher level // give them gems if they got an higher level
// tier = level in this context // tier = level in this context
@@ -323,7 +327,9 @@ api.updateHero = {
} }
} }
if (updateData.changeApiToken) hero.apiToken = common.uuid(); if (updateData.changeApiToken) {
hero.apiToken = common.uuid();
}
const savedHero = await hero.save(); const savedHero = await hero.save();
const heroJSON = savedHero.toJSON(); const heroJSON = savedHero.toJSON();

View File

@@ -714,8 +714,8 @@ api.transferGems = {
throw new NotAuthorized(res.t('badAmountOfGemsToSend')); throw new NotAuthorized(res.t('badAmountOfGemsToSend'));
} }
await receiver.updateBalance(amount, 'gift_receive', sender._id, sender.profile.name); await receiver.updateBalance(amount, 'gift_receive', sender._id, sender.auth.local.username);
await sender.updateBalance(-amount, 'gift_send', sender._id, receiver.profile.name); await sender.updateBalance(-amount, 'gift_send', sender._id, receiver.auth.local.username);
// @TODO necessary? Also saved when sending the inbox message // @TODO necessary? Also saved when sending the inbox message
const promises = [receiver.save(), sender.save()]; const promises = [receiver.save(), sender.save()];
await Promise.all(promises); await Promise.all(promises);

View File

@@ -64,9 +64,15 @@ api.purchaseHistory = {
req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID(); req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID();
const validationErrors = req.validationErrors(); const validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors; if (validationErrors) throw validationErrors;
const transactions = await Transaction let transactions = await Transaction
.find({ userId: req.params.memberId }) .find({ userId: req.params.memberId })
.sort({ createdAt: -1 }); .sort({ createdAt: -1 })
.exec();
if (!res.locals.user.hasPermission('userSupport')) {
transactions = transactions.filter(t => t.transactionType !== 'create_bank_challenge');
}
res.respond(200, transactions); res.respond(200, transactions);
}, },
}; };

View File

@@ -74,14 +74,16 @@ export async function createChallenge (user, req, res) {
if (groupBalance >= prizeCost) { if (groupBalance >= prizeCost) {
// Group pays for all of prize // Group pays for all of prize
group.balance -= prizeCost; group.balance -= prizeCost;
await user.updateBalance(0, 'create_bank_challenge', challenge._id, challenge.name);
} else if (groupBalance > 0) { } else if (groupBalance > 0) {
// User pays remainder of prize cost after group // User pays remainder of prize cost after group
const remainder = prizeCost - group.balance; const remainder = prizeCost - group.balance;
group.balance = 0; group.balance = 0;
await user.updateBalance(-remainder, 'create_challenge', challenge._id, challenge.text); await user.updateBalance(-remainder, 'create_challenge', challenge._id, challenge.name);
} else { } else {
// User pays for all of prize // User pays for all of prize
await user.updateBalance(-prizeCost, 'create_challenge', challenge._id, challenge.text); await user.updateBalance(-prizeCost, 'create_challenge', challenge._id, challenge.name);
} }
} }

View File

@@ -58,6 +58,8 @@ schema.methods.updateHourglasses = async function updateHourglasses (userId,
amount, amount,
reference, reference,
referenceText, referenceText,
currentAmount: this.consecutive.trinkets,
}); });
}; };

View File

@@ -5,7 +5,7 @@ import baseModel from '../libs/baseModel';
const { Schema } = mongoose; const { Schema } = mongoose;
export const currencies = ['gems', 'hourglasses']; export const currencies = ['gems', 'hourglasses'];
export const transactionTypes = ['buy_money', 'buy_gold', 'contribution', 'spend', 'gift_send', 'gift_receive', 'debug', 'create_challenge', 'create_guild', 'change_class', 'rebirth', 'release_pets', 'release_mounts', 'reroll', 'contribution', 'subscription_perks']; export const transactionTypes = ['buy_money', 'buy_gold', 'spend', 'gift_send', 'gift_receive', 'debug', 'create_challenge', 'create_bank_challenge', 'create_guild', 'change_class', 'rebirth', 'release_pets', 'release_mounts', 'reroll', 'contribution', 'subscription_perks', 'admin_update_balance'];
export const schema = new Schema({ export const schema = new Schema({
currency: { $type: String, enum: currencies, required: true }, currency: { $type: String, enum: currencies, required: true },
@@ -13,6 +13,7 @@ export const schema = new Schema({
reference: { $type: String }, reference: { $type: String },
referenceText: { $type: String }, referenceText: { $type: String },
amount: { $type: Number, required: true }, amount: { $type: Number, required: true },
currentAmount: { $type: Number },
userId: { userId: {
$type: String, ref: 'User', required: true, validate: [v => validator.isUUID(v), 'Invalid uuid for Transaction.'], $type: String, ref: 'User', required: true, validate: [v => validator.isUUID(v), 'Invalid uuid for Transaction.'],
}, },
@@ -23,7 +24,17 @@ export const schema = new Schema({
}); });
schema.plugin(baseModel, { schema.plugin(baseModel, {
noSet: ['id', '_id', 'userId', 'currency', 'transactionType', 'reference', 'referenceText', 'amount'], // Nothing can be set from the client noSet: [
'id',
'_id',
'userId',
'currency',
'transactionType',
'reference',
'referenceText',
'amount',
'currentAmount',
], // Nothing can be set from the client
timestamps: true, timestamps: true,
_id: false, // using custom _id _id: false, // using custom _id
}); });

View File

@@ -574,5 +574,6 @@ schema.methods.updateBalance = async function updateBalance (amount,
amount, amount,
reference, reference,
referenceText, referenceText,
currentAmount: this.balance,
}); });
}; };