Run ESLint and update deps

This commit is contained in:
ElectricS01 2023-12-10 14:14:30 +11:00
parent ff7a4d0949
commit 43ccd477d4
No known key found for this signature in database
40 changed files with 1666 additions and 2438 deletions

View File

@ -9,7 +9,7 @@
},
"dependencies": {
"argon2": "^0.28.7",
"axios": "^0.27.2",
"axios": "^1.6.2",
"clean-css": "^4.1.11",
"constantinople": "3.1.1",
"cookie-parser": "^1.4.6",

View File

@ -402,13 +402,14 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3"
integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
axios@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
dependencies:
follow-redirects "^1.14.9"
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
b4a@^1.6.4:
version "1.6.4"
@ -1538,7 +1539,7 @@ find-yarn-workspace-root@^2.0.0:
dependencies:
micromatch "^4.0.2"
follow-redirects@^1.14.9:
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
@ -3086,6 +3087,11 @@ proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
psl@^1.1.28:
version "1.9.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"

View File

@ -2,13 +2,13 @@
"name": "colubrina-cli",
"private": true,
"dependencies": {
"argon2": "^0.28.7",
"axios": "^0.27.2",
"argon2": "^0.31.2",
"axios": "^1.6.2",
"input": "^1.0.1",
"mariadb": "^3.0.1",
"pg": "^8.7.3",
"sequelize": "^6.21.3",
"sqlite3": "^5.0.10",
"umzug": "^3.1.1"
"mariadb": "^3.2.2",
"pg": "^8.11.3",
"sequelize": "^6.35.1",
"sqlite3": "^5.1.6",
"umzug": "^3.4.0"
}
}

View File

@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.9":
version "1.0.10"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c"
integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==
"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.11":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==
dependencies:
detect-libc "^2.0.0"
https-proxy-agent "^5.0.0"
@ -44,9 +44,9 @@
integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==
"@rushstack/ts-command-line@^4.12.2":
version "4.14.0"
resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.14.0.tgz#1719595bffc61e7a03e1df29a0fd29319f2e4843"
integrity sha512-DWozCsKg+ALgrsul+6vJhyB7ZogqSycRlnqULjGsJ9dLRv+Pc0Wj6J7pX0xarmgX2kH3tTf0rXgBcl8QjJULIQ==
version "4.17.1"
resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.17.1.tgz#c78db928ce5b93f2e98fd9e14c24f3f3876e57f1"
integrity sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==
dependencies:
"@types/argparse" "1.0.38"
argparse "~1.0.9"
@ -63,37 +63,39 @@
resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9"
integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==
"@types/debug@^4.1.7":
version "4.1.8"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317"
integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==
"@types/debug@^4.1.8":
version "4.1.12"
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917"
integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==
dependencies:
"@types/ms" "*"
"@types/geojson@^7946.0.10":
version "7946.0.10"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249"
integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==
version "7946.0.13"
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.13.tgz#e6e77ea9ecf36564980a861e24e62a095988775e"
integrity sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
version "0.7.34"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433"
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
"@types/node@*":
version "20.3.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.0.tgz#719498898d5defab83c3560f45d8498f58d11938"
integrity sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==
version "20.10.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198"
integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==
dependencies:
undici-types "~5.26.4"
"@types/node@^17.0.45":
version "17.0.45"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190"
integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==
"@types/validator@^13.7.1":
version "13.7.17"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.17.tgz#0a6d1510395065171e3378a4afc587a3aefa7cc1"
integrity sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==
"@types/validator@^13.7.17":
version "13.11.7"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.7.tgz#99e19760297667ae46b7069ec8b96cbfe0a08b98"
integrity sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==
abbrev@1:
version "1.1.1"
@ -108,12 +110,10 @@ agent-base@6, agent-base@^6.0.2:
debug "4"
agentkeepalive@^4.1.3:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255"
integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==
version "4.5.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==
dependencies:
debug "^4.1.0"
depd "^2.0.0"
humanize-ms "^1.2.1"
aggregate-error@^3.0.0:
@ -165,14 +165,14 @@ are-we-there-yet@^3.0.0:
delegates "^1.0.0"
readable-stream "^3.6.0"
argon2@^0.28.7:
version "0.28.7"
resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.28.7.tgz#f6cc1f1c9e3ef5455d2186d4ec5b27cfeda41591"
integrity sha512-pvsScM3Fq7b+jolXkZHh8nRQx0uD/WeelnwYPMRpn4pAydoa1gqeL/KRdWAag4Hnu1TJNBTAfqyTjV+ZHwNnYA==
argon2@^0.31.2:
version "0.31.2"
resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.31.2.tgz#bb6590a63b8ff71c4a3a1907572893ff8dd9ffff"
integrity sha512-QSnJ8By5Mth60IEte45w9Y7v6bWcQw3YhRtJKKN8oNCxnTLDiv/AXXkDPf2srTMfxFVn3QJdVv2nhXESsUa+Yg==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.9"
"@mapbox/node-pre-gyp" "^1.0.11"
"@phc/format" "^1.0.0"
node-addon-api "^5.0.0"
node-addon-api "^7.0.0"
argparse@~1.0.9:
version "1.0.10"
@ -186,13 +186,14 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
axios@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
dependencies:
follow-redirects "^1.14.9"
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-runtime@^6.6.1:
version "6.26.0"
@ -321,7 +322,7 @@ core-js@^2.4.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
debug@4, debug@^4.1.0, debug@^4.3.3:
debug@4, debug@^4.3.3, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -343,25 +344,20 @@ denque@^2.1.0:
resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1"
integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==
depd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
detect-libc@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
version "2.0.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d"
integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==
dottie@^2.0.2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.4.tgz#9ce42965f45e577a6fa7d988d47852fac70c4e82"
integrity sha512-iz64WUOmp/ECQhWMJjTWFzJN/wQ7RJ5v/a6A2OiCwjaGCpNo66WGIjlSf+IULO9DQd0b4cFawLOTbiKSrpKodw==
dottie@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.6.tgz#34564ebfc6ec5e5772272d466424ad5b696484d4"
integrity sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==
emittery@^0.12.1:
version "0.12.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.12.1.tgz#cb9a4a18745816f7a1fa03a8953e7eaededb45f2"
integrity sha512-pYyW59MIZo0HxPFf+Vb3+gacUu0gxVS3TZwB2ClwkEZywgF9f9OJDoVmNLojTn0vKX3tO9LC+pdQEcLP4Oz/bQ==
emittery@^0.13.0:
version "0.13.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
emoji-regex@^8.0.0:
version "8.0.0"
@ -403,10 +399,10 @@ figures@^1.3.5:
escape-string-regexp "^1.0.5"
object-assign "^4.1.0"
follow-redirects@^1.14.9:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
form-data@^4.0.0:
version "4.0.0"
@ -417,14 +413,6 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs-jetpack@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/fs-jetpack/-/fs-jetpack-4.3.1.tgz#cdfd4b64e6bfdec7c7dc55c76b39efaa7853bb20"
integrity sha512-dbeOK84F6BiQzk2yqqCVwCPWTxAvVGJ3fMQc6E2wuEohS28mR6yHngbrKuVCK1KHRx/ccByDylqu4H5PCP2urQ==
dependencies:
minimatch "^3.0.2"
rimraf "^2.6.3"
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@ -557,7 +545,7 @@ infer-owner@^1.0.4:
resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467"
integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
inflection@^1.13.2:
inflection@^1.13.4:
version "1.13.4"
resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.13.4.tgz#65aa696c4e2da6225b148d7a154c449366633a32"
integrity sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==
@ -636,6 +624,11 @@ lodash@^4.17.21, lodash@^4.3.0, lodash@^4.6.1:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lru-cache@^10.0.1:
version "10.1.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484"
integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@ -643,11 +636,6 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
lru-cache@^7.14.0:
version "7.18.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@ -677,16 +665,16 @@ make-fetch-happen@^9.1.0:
socks-proxy-agent "^6.0.0"
ssri "^8.0.0"
mariadb@^3.0.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/mariadb/-/mariadb-3.1.2.tgz#96f9f6c3f280a3a3c37b56ff7974400edbb88bda"
integrity sha512-ILlC54fkXkvizTJZC1uP7f/REBxuu1k+OWzpiIITIEdS+dGIjFe/Ob3EW9KrdtBa38l3z+odz6elva0RG/y5og==
mariadb@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/mariadb/-/mariadb-3.2.2.tgz#320a991c708c737e5ddeefa1852ddf925f2dcd8c"
integrity sha512-9ClJCFsLcK7WnPXVxuKGd7p0CBvNch3i5nwAf1HEqERj7RV60DG/0dJu4DfO33gpYQd9Cr4jq17O76/2VjSbkg==
dependencies:
"@types/geojson" "^7946.0.10"
"@types/node" "^17.0.45"
denque "^2.1.0"
iconv-lite "^0.6.3"
lru-cache "^7.14.0"
lru-cache "^10.0.1"
mime-db@1.52.0:
version "1.52.0"
@ -700,7 +688,7 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.52.0"
minimatch@^3.0.2, minimatch@^3.1.1:
minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@ -778,14 +766,14 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
moment-timezone@^0.5.35:
moment-timezone@^0.5.43:
version "0.5.43"
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790"
integrity sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==
dependencies:
moment "^2.29.4"
moment@^2.29.1, moment@^2.29.4:
moment@^2.29.4:
version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
@ -815,15 +803,15 @@ node-addon-api@^4.2.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
node-addon-api@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
node-addon-api@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e"
integrity sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==
node-fetch@^2.6.7:
version "2.6.11"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
@ -909,25 +897,25 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
pg-cloudflare@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.0.tgz#833d70870d610d14bf9df7afb40e1cba310c17a0"
integrity sha512-tGM8/s6frwuAIyRcJ6nWcIvd3+3NmUKIs6OjviIm1HPPFEt5MzQDOTBQyhPWg/m0kCl95M6gA1JaIXtS8KovOA==
pg-cloudflare@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==
pg-connection-string@^2.5.0, pg-connection-string@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.0.tgz#12a36cc4627df19c25cc1b9b736cc39ee1f73ae8"
integrity sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg==
pg-connection-string@^2.6.1, pg-connection-string@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
pg-pool@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.0.tgz#3190df3e4747a0d23e5e9e8045bcd99bda0a712e"
integrity sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ==
pg-pool@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.1.tgz#5a902eda79a8d7e3c928b77abf776b3cb7d351f7"
integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==
pg-protocol@^1.6.0:
version "1.6.0"
@ -945,20 +933,20 @@ pg-types@^2.1.0:
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
pg@^8.7.3:
version "8.11.0"
resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.0.tgz#a37e534e94b57a7ed811e926f23a7c56385f55d9"
integrity sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA==
pg@^8.11.3:
version "8.11.3"
resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.3.tgz#d7db6e3fe268fcedd65b8e4599cda0b8b4bf76cb"
integrity sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==
dependencies:
buffer-writer "2.0.0"
packet-reader "1.0.0"
pg-connection-string "^2.6.0"
pg-pool "^3.6.0"
pg-connection-string "^2.6.2"
pg-pool "^3.6.1"
pg-protocol "^1.6.0"
pg-types "^2.1.0"
pgpass "1.x"
optionalDependencies:
pg-cloudflare "^1.1.0"
pg-cloudflare "^1.1.1"
pgpass@1.x:
version "1.0.5"
@ -967,7 +955,7 @@ pgpass@1.x:
dependencies:
split2 "^4.1.0"
pony-cause@^2.1.2:
pony-cause@^2.1.4:
version "2.1.10"
resolved "https://registry.yarnpkg.com/pony-cause/-/pony-cause-2.1.10.tgz#828457ad6f13be401a075dbf14107a9057945174"
integrity sha512-3IKLNXclQgkU++2fSi93sQ6BznFuxSLB11HdvZQ6JW/spahf/P1pAHBQEahr20rs0htZW0UDkM1HmA+nZkXKsw==
@ -1007,6 +995,11 @@ promise-retry@^2.0.1:
err-code "^2.0.2"
retry "^0.12.0"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
readable-stream@^3.6.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
@ -1038,7 +1031,7 @@ restore-cursor@^1.0.1:
exit-hook "^1.0.0"
onetime "^1.0.0"
retry-as-promised@^7.0.3:
retry-as-promised@^7.0.4:
version "7.0.4"
resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-7.0.4.tgz#9df73adaeea08cb2948b9d34990549dc13d800a2"
integrity sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==
@ -1048,13 +1041,6 @@ retry@^0.12.0:
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
@ -1085,14 +1071,14 @@ safe-buffer@~5.2.0:
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
semver@^6.0.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.5:
version "7.5.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.1.tgz#c90c4d631cf74720e46b21c1d37ea07edfab91ec"
integrity sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==
semver@^7.3.5, semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
@ -1101,26 +1087,26 @@ sequelize-pool@^7.1.0:
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-7.1.0.tgz#210b391af4002762f823188fd6ecfc7413020768"
integrity sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==
sequelize@^6.21.3:
version "6.32.0"
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-6.32.0.tgz#566488aa0fcf32c1baa2ff2344f462a4c9eb4a01"
integrity sha512-gMd1M6kPANyrCeU/vtgEP5gnse7sVsiKbJyz7p4huuW8zZcRopj47UlglvdrMuIoqksZmsUPfApmMo6ZlJpcvg==
sequelize@^6.35.1:
version "6.35.1"
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-6.35.1.tgz#e640bc11a7a37f6fa23a92974b5e5ca20affd150"
integrity sha512-UlP5k33nJsN11wCDLaWZXw9bB8w4ESKc5QmG6D04qMimwBwKVNeqRJiaaBlEJdtg8cRK+OJh95dliP+uEi+g9Q==
dependencies:
"@types/debug" "^4.1.7"
"@types/validator" "^13.7.1"
debug "^4.3.3"
dottie "^2.0.2"
inflection "^1.13.2"
"@types/debug" "^4.1.8"
"@types/validator" "^13.7.17"
debug "^4.3.4"
dottie "^2.0.6"
inflection "^1.13.4"
lodash "^4.17.21"
moment "^2.29.1"
moment-timezone "^0.5.35"
pg-connection-string "^2.5.0"
retry-as-promised "^7.0.3"
semver "^7.3.5"
moment "^2.29.4"
moment-timezone "^0.5.43"
pg-connection-string "^2.6.1"
retry-as-promised "^7.0.4"
semver "^7.5.4"
sequelize-pool "^7.1.0"
toposort-class "^1.0.1"
uuid "^8.3.2"
validator "^13.7.0"
validator "^13.9.0"
wkx "^0.5.0"
set-blocking@^2.0.0:
@ -1165,7 +1151,7 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
sqlite3@^5.0.10:
sqlite3@^5.1.6:
version "5.1.6"
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.6.tgz#1d4fbc90fe4fbd51e952e0a90fd8f6c2b9098e97"
integrity sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==
@ -1233,9 +1219,9 @@ supports-color@^2.0.0:
integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
tar@^6.0.2, tar@^6.1.11, tar@^6.1.2:
version "6.1.15"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
version "6.2.0"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
@ -1259,22 +1245,26 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
type-fest@^2.18.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
type-fest@^3.0.0:
version "3.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706"
integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==
umzug@^3.1.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/umzug/-/umzug-3.2.1.tgz#01c3a109efb037a10a317d4191be22810c37b195"
integrity sha512-XyWQowvP9CKZycKc/Zg9SYWrAWX/gJCE799AUTFqk8yC3tp44K1xWr3LoFF0MNEjClKOo1suCr5ASnoy+KltdA==
umzug@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/umzug/-/umzug-3.4.0.tgz#97819a2d2288c8ce2c156c3be8b18b02f2208423"
integrity sha512-bTen9ElCBoWU1mhcaXqVZWXxB1PojsBQBs/4vW0YV8f5CfhuhkfRjQZj6SCb6IuHWPkccDzF+T+RGZCYUiXaKg==
dependencies:
"@rushstack/ts-command-line" "^4.12.2"
emittery "^0.12.1"
fs-jetpack "^4.3.1"
emittery "^0.13.0"
glob "^8.0.3"
pony-cause "^2.1.2"
type-fest "^2.18.0"
pony-cause "^2.1.4"
type-fest "^3.0.0"
undici-types@~5.26.4:
version "5.26.5"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
unique-filename@^1.1.1:
version "1.1.1"
@ -1300,10 +1290,10 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
validator@^13.7.0:
version "13.9.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855"
integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==
validator@^13.9.0:
version "13.11.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b"
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
webidl-conversions@^3.0.0:
version "3.0.1"

View File

@ -4,7 +4,7 @@ module.exports = {
es2020: true,
node: true
},
extends: ["plugin:vue/essential", "eslint:recommended"],
extends: ["plugin:vue/recommended", "eslint:recommended"],
parserOptions: {
parser: "@babel/eslint-parser"
},
@ -15,6 +15,7 @@ module.exports = {
"error",
{ "invalid-first-character-of-tag-name": false }
],
"vue/no-reserved-component-names": "off",
"vue/no-mutating-props": "off",
"vue/multi-word-component-names": "off"
}

View File

@ -5,6 +5,7 @@
"private": true,
"author": "Troplo <troplo@troplo.com>",
"scripts": {
"dev": "vue-cli-service serve",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --no-clean",
"lint": "vue-cli-service lint",
@ -19,7 +20,7 @@
"@babel/preset-env": "^7.17.10",
"@mdi/font": "6.5.95",
"@mdi/js": "^6.6.95",
"axios": "^0.26.1",
"axios": "^1.6.2",
"brace": "^0.11.1",
"chart.js": "^3.7.1",
"core-js": "^3.6.5",
@ -36,12 +37,11 @@
"patch-package": "^6.4.7",
"prettier": "^2.6.1",
"register-service-worker": "^1.7.1",
"request": "^2.88.2",
"selfsigned": "^2.0.1",
"socket.io-client": "^4.5.1",
"twemoji": "^14.0.2",
"vue": "^2.6.11",
"vue-axios": "^3.4.1",
"vue-axios": "^3.5.2",
"vue-chartjs": "^4.0.1",
"vue-final-modal": "^2.4.1",
"vue-native-notification": "^1.1.1",
@ -66,12 +66,12 @@
"dotenv-webpack": "^7.1.0",
"electron": "^28.0.0",
"electron-devtools-installer": "^3.1.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"eslint": "^8.55.0",
"eslint-plugin-vue": "^9.19.2",
"material-design-icons-iconfont": "^6.5.0",
"sass": "~1.32.0",
"sass-loader": "^10.0.0",
"vue-cli-plugin-electron-builder": "^2.1.1",
"vue-cli-plugin-electron-builder": "https://github.com/nklayman/vue-cli-plugin-electron-builder.git#b51147f",
"vue-cli-plugin-vuetify": "~2.5.8",
"vue-template-babel-compiler": "^1.1.3",
"vue-template-compiler": "^2.6.11",

View File

@ -6,11 +6,11 @@
"
>
<v-overlay :value="!$store.state.wsConnected" absolute style="z-index: 69">
<v-progress-circular indeterminate size="64"></v-progress-circular>
<v-progress-circular indeterminate size="64" />
</v-overlay>
<DevOverlay v-if="$store.state.site.release === 'dev'"></DevOverlay>
<DevOverlay v-if="$store.state.site.release === 'dev'" />
<v-overlay :value="$store.state.site.loading">
<v-progress-circular indeterminate size="64"></v-progress-circular>
<v-progress-circular indeterminate size="64" />
</v-overlay>
<!-- theme engine editors -->
<vue-final-modal
@ -39,8 +39,8 @@
$store.state.themeEngine.type === "create" ? "Creator" : "Editor"
}}
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<span v-on="on" v-bind="attrs">
<template #activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">
<v-btn
fab
small
@ -54,7 +54,7 @@
<span> Randomize theme </span>
</v-tooltip>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click="$store.state.themeEngine.editor = false">
<v-icon>mdi-close</v-icon>
</v-btn>
@ -72,27 +72,30 @@
$store.dispatch('saveTheme', { theme: null, type: null })
$store.state.themeEngine.editor = false
"
>{{
>
{{
$store.state.themeEngine.type === "create"
? "Create & Apply"
: "Save Edits"
}}</v-btn
>
}}
</v-btn>
<v-btn
v-if="$store.state.themeEngine.type === 'edit'"
color="primary"
text
@click="
$store.dispatch('saveTheme', { theme: null, type: 'copy' })
"
v-if="$store.state.themeEngine.type === 'edit'"
>Save a Copy</v-btn
>
Save a Copy
</v-btn>
<v-btn
color="error darken-1"
text
@click="$store.dispatch('discardTheme')"
>Discard</v-btn
>
Discard
</v-btn>
</v-card-actions>
<v-form>
<v-text-field
@ -100,28 +103,23 @@
class="mx-3"
label="Theme Name"
required
></v-text-field>
/>
<v-select
v-model="$store.state.themeEngine.theme.primaryType"
:items="intendedFor"
label="Intended for"
class="mx-3"
v-model="$store.state.themeEngine.theme.primaryType"
>
</v-select>
<v-text-field
v-model="creatorJSON"
label="JSON"
class="mx-3"
></v-text-field>
/>
<v-text-field v-model="creatorJSON" label="JSON" class="mx-3" />
<v-btn @click="$store.state.themeEngine.cssEditor = true">
Custom CSS
</v-btn>
<h2
class="ml-2 mt-2 mb-3"
v-if="
$store.state.themeEngine.theme.primaryType === 'dark' ||
$store.state.themeEngine.theme.primaryType === 'all'
"
class="ml-2 mt-2 mb-3"
>
Dark:
</h2>
@ -132,44 +130,44 @@
"
>
<v-col
sm="3"
v-for="(item, index) in $store.state.themeEngine.theme.dark"
:key="index + '-dark-card'"
sm="3"
>
<v-card color="card">
<h3 class="ml-2 mt-2 mb-2">
{{ friendlyName(index) }}
</h3>
<v-menu offset-y>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-card
class="mb-2 mx-2"
:color="$store.state.themeEngine.theme.dark[index]"
v-on="on"
>
<v-container></v-container>
<v-container />
</v-card>
</template>
<v-color-picker
v-model="$store.state.themeEngine.theme.dark[index]"
show-swatches
hide-inputs
></v-color-picker>
/>
</v-menu>
<v-text-field
v-model="$store.state.themeEngine.theme.dark[index]"
class="mx-2"
label="#HEX"
v-model="$store.state.themeEngine.theme.dark[index]"
></v-text-field>
/>
</v-card>
</v-col>
</v-row>
<h2
class="ml-2 mt-2 mb-3"
v-if="
$store.state.themeEngine.theme.primaryType === 'light' ||
$store.state.themeEngine.theme.primaryType === 'all'
"
class="ml-2 mt-2 mb-3"
>
Light:
</h2>
@ -180,35 +178,35 @@
"
>
<v-col
sm="3"
v-for="(item, index) in $store.state.themeEngine.theme.light"
:key="index + '-light-card'"
sm="3"
>
<v-card color="card">
<h3 class="ml-2 mt-2 mb-2">
{{ friendlyName(index) }}
</h3>
<v-menu offset-y>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-card
class="mb-2 mx-2"
:color="$store.state.themeEngine.theme.light[index]"
v-on="on"
>
<v-container></v-container>
<v-container />
</v-card>
</template>
<v-color-picker
v-model="$store.state.themeEngine.theme.light[index]"
show-swatches
hide-inputs
></v-color-picker>
/>
</v-menu>
<v-text-field
v-model="$store.state.themeEngine.theme.light[index]"
class="mx-2"
label="#HEX"
v-model="$store.state.themeEngine.theme.light[index]"
></v-text-field>
/>
</v-card>
</v-col>
</v-row>
@ -221,27 +219,30 @@
$store.dispatch('saveTheme', { theme: null, type: null })
$store.state.themeEngine.editor = false
"
>{{
>
{{
$store.state.themeEngine.type === "create"
? "Create & Apply"
: "Save Edits"
}}</v-btn
>
}}
</v-btn>
<v-btn
v-if="$store.state.themeEngine.type === 'edit'"
color="primary"
text
@click="
$store.dispatch('saveTheme', { theme: null, type: 'copy' })
"
v-if="$store.state.themeEngine.type === 'edit'"
>Save a Copy</v-btn
>
Save a Copy
</v-btn>
<v-btn
color="error darken-1"
text
@click="$store.dispatch('discardTheme')"
>Discard</v-btn
>
Discard
</v-btn>
</v-card-actions>
</v-container>
</v-card>
@ -271,7 +272,7 @@
>
<v-card-title color="toolbar" class="editor__toolbar v-toolbar">
<v-toolbar-title>CSS Editor</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click="$store.state.themeEngine.cssEditor = false">
<v-icon>mdi-close</v-icon>
</v-btn>
@ -281,13 +282,13 @@
<v-col>
<v-alert type="info" text class="editor__toolbar">
CTRL + ALT + D / F9 will toggle all custom CSS styling, works
anywhere, even outside the editor.</v-alert
>
anywhere, even outside the editor.
</v-alert>
<v-switch
v-model="$store.state.themeEngine.autoCSS"
inset
label="Live update"
v-model="$store.state.themeEngine.autoCSS"
></v-switch>
/>
<v-btn
icon
class="mb-2"
@ -306,26 +307,26 @@
<v-icon>mdi-refresh</v-icon>
</v-btn>
<editor
class="editor"
v-model="$store.state.themeEngine.theme.css"
@init="editorInit"
class="editor"
lang="css"
:theme="$vuetify.theme.dark ? 'monokai' : 'chrome'"
height="350"
></editor>
@init="editorInit"
/>
</v-col>
<v-col v-if="cssTips">
<v-card-title>
Tips
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click="cssTips = false">
<v-icon>mdi-close</v-icon>
</v-btn></v-card-title
>
</v-btn>
</v-card-title>
<v-alert type="error" text>
This is an alert.<br />
Try to style it with .v-alert</v-alert
>
Try to style it with .v-alert
</v-alert>
Here's an example:<br />
<code class="block"
>.v-alert {<br />
@ -334,15 +335,17 @@
<v-btn
class="mt-2 mr-2"
@click="$toast.success('I have been pressed.')"
>Here's a button</v-btn
>
Here's a button
</v-btn>
<v-btn
class="mt-2"
text
color="info"
@click="$toast.info('This is the second button\'s action.')"
>Here's another one</v-btn
>
Here's another one
</v-btn>
<v-card-title> Fonts </v-card-title>
<code
class="block"
@ -392,9 +395,8 @@
</v-toolbar>
<v-container v-if="$store.state.modals.search">
<v-autocomplete
@keydown.enter="handleEnter"
auto-select-first
v-model="search"
auto-select-first
:items="$store.state.quickSwitchCache"
item-text="subjectLongName"
label="Search"
@ -402,13 +404,13 @@
autofocus
return-object
:search-input.sync="searchInput"
>
</v-autocomplete>
@keydown.enter="handleEnter"
/>
</v-container>
</v-card>
</v-dialog>
<v-main>
<Header></Header>
<Header />
<v-container
v-if="
$store.state.site.latestVersion > $store.state.versioning.version &&
@ -453,7 +455,6 @@
</v-main>
</v-app>
</template>
<style></style>
<script>
import AjaxErrorHandler from "@/lib/errorHandler.js"
import { VueFinalModal } from "vue-final-modal"
@ -529,6 +530,123 @@ export default {
}
}
},
watch: {
"$store.state.userPanel"(val) {
localStorage.setItem("userPanel", val)
},
cssTips(val) {
localStorage.setItem("cssTipsDismissed", !val)
},
"$store.state.themeEngine.theme": {
handler() {
this.$vuetify.theme.themes.dark =
this.$store.state.themeEngine.theme.dark
this.$vuetify.theme.themes.light =
this.$store.state.themeEngine.theme.light
this.$vuetify.theme.themes.name = this.$store.state.themeEngine.theme.id
},
deep: true
},
"$store.state.themeEngine.theme.css"() {
if (this.$store.state.themeEngine.autoCSS) {
this.$store.dispatch("applyCSS", null)
}
},
"$store.state.user.theme": {
handler() {
if (this.$store.state.user?.theme) {
this.$vuetify.theme.dark = this.$store.state.user.theme === "dark"
}
},
deep: true
},
$route(to, from) {
document.title = to.name + " - " + this.$store.state.site.name
this.$store.state.lastRoute = from.path
},
search() {
if (this.search) {
if (this.search.id) {
this.$router.push("/communications/" + this.search.id)
this.$store.commit("setSearch", false)
this.search = null
this.$nextTick(() => {
this.searchInput = null
})
} else if (this.search.customType === 1) {
this.$router.push(this.search.route)
this.$store.commit("setSearch", false)
this.search = null
this.$nextTick(() => {
this.searchInput = null
})
}
}
}
},
async mounted() {
if (localStorage.getItem("forceEnableDevMode"))
this.$store.state.release = "dev"
await this.$store.dispatch("doInit")
if (this.$vuetify.breakpoint.mobile) {
this.$store.state.drawer = false
}
if (localStorage.getItem("cssTipsDismissed")) {
this.cssTips = false
}
if (localStorage.getItem("userPanel")) {
this.$store.state.userPanel = JSON.parse(
localStorage.getItem("userPanel")
)
} else {
this.$store.state.userPanel = true
}
if (this.$vuetify.breakpoint.mobile) {
this.$store.state.userPanel = false
}
window.addEventListener("offline", () => {
this.$store.commit("setOnline", false)
this.$store.dispatch("getState")
})
window.addEventListener("online", () => {
this.$store.commit("setOnline", true)
this.$store.dispatch("getState")
this.$store.dispatch("getUserInfo")
})
this.$socket.connect()
this.$socket.on("unauthorized", () => {
console.log("Reauth requested")
this.$socket.emit("token", localStorage.getItem("token"))
})
document.title = this.$route.name
? this.$route.name + " - " + this.$store.state.site.name
: this.$store.state.site.name || "Colubrina"
this.$store.commit("setLoading", true)
this.$vuetify.theme.dark = this.$store.state.user?.theme === "dark" || true
this.$store.dispatch("getState")
this.$store
.dispatch("getUserInfo")
.then(() => {
console.log(window.location.pathname)
if (
!window.location.pathname.includes("/email/confirm/") &&
!window.location.pathname.includes("/email/verify") &&
!this.$store.state.user.emailVerified &&
this.$store.state.site.emailVerification
) {
this.$router.push("/email/verify")
}
})
.catch(() => {
this.$store.dispatch("logout")
if (!window.location.pathname.includes("/reset")) {
this.$router.push("/login")
}
})
this.getThemes()
this.communicationsIdleCheck()
this.registerSocket()
},
methods: {
handleEnter() {
if (
@ -766,126 +884,10 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
async mounted() {
if (localStorage.getItem("forceEnableDevMode"))
this.$store.state.release = "dev"
await this.$store.dispatch("doInit")
if (this.$vuetify.breakpoint.mobile) {
this.$store.state.drawer = false
}
if (localStorage.getItem("cssTipsDismissed")) {
this.cssTips = false
}
if (localStorage.getItem("userPanel")) {
this.$store.state.userPanel = JSON.parse(
localStorage.getItem("userPanel")
)
} else {
this.$store.state.userPanel = true
}
if (this.$vuetify.breakpoint.mobile) {
this.$store.state.userPanel = false
}
window.addEventListener("offline", () => {
this.$store.commit("setOnline", false)
this.$store.dispatch("getState")
})
window.addEventListener("online", () => {
this.$store.commit("setOnline", true)
this.$store.dispatch("getState")
this.$store.dispatch("getUserInfo")
})
this.$socket.connect()
this.$socket.on("unauthorized", () => {
console.log("Reauth requested")
this.$socket.emit("token", localStorage.getItem("token"))
})
document.title = this.$route.name
? this.$route.name + " - " + this.$store.state.site.name
: this.$store.state.site.name || "Colubrina"
this.$store.commit("setLoading", true)
this.$vuetify.theme.dark = this.$store.state.user?.theme === "dark" || true
this.$store.dispatch("getState")
this.$store
.dispatch("getUserInfo")
.then(() => {
console.log(window.location.pathname)
if (
!window.location.pathname.includes("/email/confirm/") &&
!window.location.pathname.includes("/email/verify") &&
!this.$store.state.user.emailVerified &&
this.$store.state.site.emailVerification
) {
this.$router.push("/email/verify")
}
})
.catch(() => {
this.$store.dispatch("logout")
if (!window.location.pathname.includes("/reset")) {
this.$router.push("/login")
}
})
this.getThemes()
this.communicationsIdleCheck()
this.registerSocket()
},
watch: {
"$store.state.userPanel"(val) {
localStorage.setItem("userPanel", val)
},
cssTips(val) {
localStorage.setItem("cssTipsDismissed", !val)
},
"$store.state.themeEngine.theme": {
handler() {
this.$vuetify.theme.themes.dark =
this.$store.state.themeEngine.theme.dark
this.$vuetify.theme.themes.light =
this.$store.state.themeEngine.theme.light
this.$vuetify.theme.themes.name = this.$store.state.themeEngine.theme.id
},
deep: true
},
"$store.state.themeEngine.theme.css"() {
if (this.$store.state.themeEngine.autoCSS) {
this.$store.dispatch("applyCSS", null)
}
},
"$store.state.user.theme": {
handler() {
if (this.$store.state.user?.theme) {
this.$vuetify.theme.dark = this.$store.state.user.theme === "dark"
}
},
deep: true
},
$route(to, from) {
document.title = to.name + " - " + this.$store.state.site.name
this.$store.state.lastRoute = from.path
},
search() {
if (this.search) {
if (this.search.id) {
this.$router.push("/communications/" + this.search.id)
this.$store.commit("setSearch", false)
this.search = null
this.$nextTick(() => {
this.searchInput = null
})
} else if (this.search.customType === 1) {
this.$router.push(this.search.route)
this.$store.commit("setSearch", false)
this.search = null
this.$nextTick(() => {
this.searchInput = null
})
}
}
}
}
}
</script>
<style></style>
<style scoped>
::v-deep .modal-container {

View File

@ -18,7 +18,7 @@
padding: 10px;
cursor: move;
z-index: 2001;
background-color: #2196F3;
background-color: #2196f3;
}
.chat-col {
@ -28,7 +28,7 @@
height: 100%;
}
>>>.max-v-list-height {
>>> .max-v-list-height {
max-height: 10px;
overflow-y: auto;
}
@ -89,8 +89,8 @@ img.emoji {
pointer-events: none;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('./fonts/JetBrainsMono-Regular.ttf') format('truetype');
font-family: "JetBrains Mono";
src: url("./fonts/JetBrainsMono-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}

View File

@ -7,11 +7,8 @@
</v-toolbar>
<v-card-text>
<v-container fluid>
<v-text-field v-model="poll.title" label="Title"></v-text-field>
<v-textarea
v-model="poll.description"
label="Description"
></v-textarea>
<v-text-field v-model="poll.title" label="Title" />
<v-textarea v-model="poll.description" label="Description" />
<v-text-field
v-for="(value, index) in poll.options"
:key="index"
@ -20,19 +17,19 @@
:maxlength="30"
:append-outer-icon="poll.options.length > 2 ? 'mdi-close' : ''"
@click:append-outer="poll.options.splice(index, 1)"
></v-text-field>
/>
<v-btn
@click="poll.options.push('')"
v-if="poll.options.length <= 3"
text
block
@click="poll.options.push('')"
>
<v-icon> mdi-plus </v-icon>
</v-btn>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn color="red" text @click="poll.dialog = false"> Cancel </v-btn>
<v-btn
color="blue darken-1"
@ -48,27 +45,27 @@
</v-card>
</v-dialog>
<v-toolbar
v-for="(embed, index) in embeds"
:key="index"
elevation="0"
outlined
height="40"
color="card"
v-for="(embed, index) in embeds"
style="cursor: pointer; overflow: hidden"
class="mb-2"
:key="index"
>
<v-toolbar-title>
<v-icon> mdi-attachment </v-icon>
{{ embedName(embed.type) }}
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="embeds.splice(index, 1)" small>
<v-spacer />
<v-btn icon small @click="embeds.splice(index, 1)">
<v-icon> mdi-close </v-icon>
</v-btn>
</v-toolbar>
<v-progress-linear
v-model="uploadPercentage"
v-if="uploading"
v-model="uploadPercentage"
height="15"
color="toolbar"
disabled
@ -77,11 +74,11 @@
<small>{{ uploadPercentage }}%</small>
</v-progress-linear>
<v-toolbar
v-if="file"
elevation="0"
outlined
height="40"
color="card"
v-if="file"
style="cursor: pointer; overflow: hidden"
class="mb-2"
>
@ -89,12 +86,14 @@
<v-icon> mdi-attachment </v-icon>
{{ file.name }}
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="file = null" small>
<v-spacer />
<v-btn icon small @click="file = null">
<v-icon> mdi-close </v-icon>
</v-btn>
</v-toolbar>
<v-textarea
:id="edit ? 'edit-input' : 'message-input'"
ref="message-input"
:style="
!$vuetify.breakpoint.mobile
? 'margin-bottom: 5px'
@ -103,12 +102,15 @@
autofocus
label="Type a message"
placeholder="Keep it civil"
v-model="message"
type="text"
ref="message-input"
:id="edit ? 'edit-input' : 'message-input'"
outlined
append-outer-icon="mdi-send"
auto-grow
rows="1"
single-line
dense
hide-details
@keyup.up="handleEditMessage"
@keyup.esc="handleEsc"
@click:append-outer="handleMessage()"
@ -118,27 +120,22 @@
completions = []
completionWord = ''
"
v-model="message"
@paste="handlePaste"
rows="1"
single-line
dense
hide-details
@keydown.enter.shift.exact.prevent="newLine($event)"
>
<template v-slot:append-outer>
<template #append-outer>
<v-btn
v-if="!edit"
icon
small
@click="handleMessage()"
v-if="!edit"
:retain-focus-on-click="false"
class="no-focus"
@click="handleMessage()"
>
<v-icon> mdi-send </v-icon>
</v-btn>
</template>
<template v-slot:prepend-inner>
<template #prepend-inner>
<v-menu
:nudge-top="10"
:nudge-left="5"
@ -148,13 +145,13 @@
offset-y
top
>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-btn
v-on="on"
id="attachment-button"
icon
style="margin-top: -3px; margin-left: -4px"
small
v-on="on"
@dblclick.stop="openFileInput"
>
<v-icon>mdi-plus-circle</v-icon>
@ -168,21 +165,22 @@
</v-list-item>
<v-list-item @click="openFileInput">
<v-file-input
v-if="!edit"
ref="file-input"
v-model="file"
style="margin-top: -10px"
single-line
hide-input
v-model="file"
@change="getURLForImage"
v-if="!edit"
ref="file-input"
st
></v-file-input>
@change="getURLForImage"
/>
Upload a file
</v-list-item>
</v-list>
</div>
</v-menu>
<v-menu
v-if="false"
:nudge-top="10"
:nudge-left="5"
:close-delay="100"
@ -190,19 +188,18 @@
bottom
offset-y
top
v-if="false"
>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-btn
id="emoji-button"
icon
v-on="on"
style="
margin-top: -2px;
margin-left: 1px;
filter: grayscale(100%);
"
small
v-on="on"
@dblclick.stop="openFileInput"
>
<img
@ -230,11 +227,10 @@
v-for="emoji in emojisByCategory[category]"
:key="emoji.emoji"
sm="4"
v-html="twemoji(emoji.emoji)"
@click="addEmoji(emoji.emoji)"
style="cursor: pointer"
>
</v-col>
@click="addEmoji(emoji.emoji)"
v-html="twemoji(emoji.emoji)"
/>
</v-row>
</v-container>
</v-card>
@ -304,6 +300,16 @@ export default {
completionWord: ""
}
},
watch: {
message() {
this.handleChange()
}
},
mounted() {
if (this.edit) {
this.message = this.edit.content
}
},
/*
computed: {
emojis() {
@ -582,16 +588,6 @@ export default {
}
}
}
},
mounted() {
if (this.edit) {
this.message = this.edit.content
}
},
watch: {
message() {
this.handleChange()
}
}
}
</script>

View File

@ -1,15 +1,15 @@
<template>
<div class="dev-overlay" id="dev-overlay">
<div class="dev-header" id="dev-header">Colubrina DevTools</div>
<div id="dev-overlay" class="dev-overlay">
<div id="dev-header" class="dev-header">Colubrina DevTools</div>
<v-container>
<v-btn @click="benchmark"
>Benchmark
<template v-if="isBenchmarking">({{ multiplier }}x)</template></v-btn
<v-btn @click="benchmark">
Benchmark
<template v-if="isBenchmarking"> ({{ multiplier }}x) </template> </v-btn
><br />
<template v-if="isBenchmarking"
>Please restart the Colubrina server to stop the benchmark.</template
<template v-if="isBenchmarking">
Please restart the Colubrina server to stop the benchmark. </template
><br />
<v-btn @click="deleteBenchmark">Delete benchuser messages</v-btn>
<v-btn @click="deleteBenchmark"> Delete benchuser messages </v-btn>
</v-container>
</div>
</template>
@ -24,6 +24,9 @@ export default {
multiplier: 0
}
},
mounted() {
this.drag(document.getElementById("dev-overlay"))
},
methods: {
deleteBenchmark() {
this.axios.delete("/api/v1/debug/bench").catch((e) => {
@ -81,9 +84,6 @@ export default {
document.onmousemove = null
}
}
},
mounted() {
this.drag(document.getElementById("dev-overlay"))
}
}
</script>

View File

@ -1,32 +1,29 @@
<template>
<span>
<v-card
v-if="embed.type === 'image'"
:min-width="!$vuetify.breakpoint.mobile ? 400 : 0"
:max-width="500"
elevation="0"
color="card"
v-if="embed.type === 'image'"
>
<v-hover v-slot="{ hover }">
<div>
<v-img
@click="setImagePreview(embed)"
contain
:max-width="500"
:max-height="500"
:min-height="250"
:min-width="250"
:src="$store.state.baseURL + embed.mediaProxyLink"
@click="setImagePreview(embed)"
>
<template v-slot:placeholder>
<template #placeholder>
<v-row class="fill-height ma-0" align="center" justify="center">
<v-progress-circular
indeterminate
color="grey lighten-5"
></v-progress-circular>
<v-progress-circular indeterminate color="grey lighten-5" />
</v-row>
</template>
<template v-slot:default>
<template #default>
<v-fade-transition v-if="hover">
<v-overlay absolute>
<v-icon large>mdi-arrow-expand-all</v-icon>
@ -48,9 +45,9 @@
<v-container fluid>
<v-row v-if="embed.type === 'openGraph'">
<v-col
v-if="embed.openGraph.ogImage"
cols="12"
class="text-xs-center"
v-if="embed.openGraph.ogImage"
>
<v-img
:src="
@ -60,12 +57,9 @@
contain
:aspect-ratio="16 / 9"
>
<template v-slot:placeholder>
<template #placeholder>
<v-row class="fill-height ma-0" align="center" justify="center">
<v-progress-circular
indeterminate
color="grey lighten-5"
></v-progress-circular>
<v-progress-circular indeterminate color="grey lighten-5" />
</v-row>
</template>
</v-img>
@ -85,7 +79,7 @@
</v-col>
</v-row>
<v-row v-else-if="embed.type === 'embed-v1'">
<v-col cols="12" class="text-xs-center" v-if="embed.headerImage">
<v-col v-if="embed.headerImage" cols="12" class="text-xs-center">
<v-img
:src="
embed.openGraph.headerImage?.url ||
@ -95,12 +89,9 @@
contain
:aspect-ratio="16 / 9"
>
<template v-slot:placeholder>
<template #placeholder>
<v-row class="fill-height ma-0" align="center" justify="center">
<v-progress-circular
indeterminate
color="grey lighten-5"
></v-progress-circular>
<v-progress-circular indeterminate color="grey lighten-5" />
</v-row>
</template>
</v-img>
@ -121,17 +112,17 @@
{{ graph.name }}
</h3>
<Chart
:chart-data="graph.data"
v-if="graph.data"
:chart-data="graph.data"
:options="graphOptions"
></Chart>
/>
<p v-else>Chart data could not be loaded.</p>
</v-col>
</v-row>
<v-row
v-for="(field, index) in embed.fields"
:key="'field-' + index"
:id="'field-' + index"
:key="'field-' + index"
class="mt-1"
>
<v-col
@ -144,8 +135,8 @@
</v-col>
</v-row>
<a
:href="embed.link.url"
v-if="embed.link"
:href="embed.link.url"
target="_blank"
style="text-decoration: none"
>
@ -193,10 +184,10 @@ ChartJS.register(
export default {
name: "Embed",
props: ["embed", "setImagePreview"],
components: {
Chart
}
},
props: ["embed", "setImagePreview"]
}
</script>

View File

@ -10,7 +10,7 @@
class="rounded-l"
style="z-index: 20"
>
<v-list class="rounded-l" v-if="context.user.item">
<v-list v-if="context.user.item" class="rounded-l">
<v-menu
:nudge-right="10"
:close-delay="100"
@ -20,42 +20,43 @@
offset-x
open-on-hover
>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-list-item v-on="on">
<v-list-item-title>Notification Settings</v-list-item-title
><v-icon class="ml-2">mdi-arrow-right</v-icon>
><v-icon class="ml-2"> mdi-arrow-right </v-icon>
</v-list-item>
</template>
<div>
<v-list>
<v-list-item @click="setNotifications('all')">
<v-list-item-title>All messages</v-list-item-title>
<v-icon v-if="context.user.raw.notifications === 'all'"
>mdi-check</v-icon
>
<v-icon v-if="context.user.raw.notifications === 'all'">
mdi-check
</v-icon>
</v-list-item>
<v-list-item two-line @click="setNotifications('mentions')">
<v-list-item-content>
<v-list-item-title
>Mentions only
<v-list-item-title>
Mentions only
<v-icon
style="float: right"
v-if="context.user.raw.notifications === 'mentions'"
>mdi-check</v-icon
></v-list-item-title
>
style="float: right"
>
mdi-check
</v-icon>
</v-list-item-title>
<v-list-item-subtitle
>Mentions are performed when your username is sent in the
chat.</v-list-item-subtitle
>
<v-list-item-subtitle>
Mentions are performed when your username is sent in the
chat.
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item @click="setNotifications('none')">
<v-list-item-title>None</v-list-item-title>
<v-icon v-if="context.user.raw.notifications === 'none'"
>mdi-check</v-icon
>
<v-icon v-if="context.user.raw.notifications === 'none'">
mdi-check
</v-icon>
</v-list-item>
</v-list>
</div>
@ -72,22 +73,22 @@
<v-list-item @click="groupSettings(context.user.raw.id)">
<v-list-item-title>Group Settings</v-list-item-title>
</v-list-item>
<v-list-item @click="groupLeave(context.user.raw.id)" color="error">
<v-list-item color="error" @click="groupLeave(context.user.raw.id)">
<v-list-item-title>Leave Group</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-dialog
v-if="settings.item"
v-model="settings.addMembers.dialog"
max-width="400px"
v-if="settings.item"
>
<v-card color="card">
<v-toolbar color="toolbar">
<v-toolbar-title
>Add User to {{ settings.item.chat.name }}</v-toolbar-title
>
<v-spacer></v-spacer>
<v-toolbar-title>
Add User to {{ settings.item.chat.name }}
</v-toolbar-title>
<v-spacer />
<v-btn icon @click.native="settings.addMembers.dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
@ -105,7 +106,7 @@
item-value="id"
multiple
>
<template v-slot:selection="data">
<template #selection="data">
<v-chip
v-bind="data.attrs"
:input-value="data.selected"
@ -113,25 +114,25 @@
@click="data.select"
@click:close="remove(data.item)"
>
<v-avatar left v-if="data.item.avatar">
<v-avatar v-if="data.item.avatar" left>
<v-img
:src="
$store.state.baseURL + '/usercontent/' + data.item.avatar
"
></v-img>
/>
</v-avatar>
@{{ data.item.username }}
</v-chip>
</template>
<template v-slot:item="data">
<v-avatar left v-if="data.item.avatar" class="mr-3 mb-2 mt-2">
<template #item="data">
<v-avatar v-if="data.item.avatar" left class="mr-3 mb-2 mt-2">
<v-img
:src="
$store.state.baseURL + '/usercontent/' + data.item.avatar
"
></v-img>
/>
</v-avatar>
<v-avatar left v-else class="mr-3 mb-2 mt-2">
<v-avatar v-else left class="mr-3 mb-2 mt-2">
<v-icon>mdi-account</v-icon>
</v-avatar>
<v-list-item-content>
@ -141,60 +142,60 @@
</v-autocomplete>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn color="error" text @click="settings.addMembers.dialog = false">
Cancel
</v-btn>
<v-btn
color="primary"
text
@click="addMembersToGroup"
:disabled="!settings.addMembers.users.length"
@click="addMembersToGroup"
>
Add
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="settings.dialog" max-width="500px" v-if="settings.item">
<v-dialog v-if="settings.item" v-model="settings.dialog" max-width="500px">
<v-card color="card">
<v-toolbar color="toolbar">
<v-toolbar-title>Group Settings</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click.native="settings.dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-container>
<v-alert text type="info" v-if="settings.item.rank !== 'admin'">
<v-alert v-if="settings.item.rank !== 'admin'" text type="info">
You need to be an administrator of this group to change settings.
</v-alert>
<v-card-title v-if="settings.item.rank === 'admin'"
>General</v-card-title
>
<v-card-title v-if="settings.item.rank === 'admin'">
General
</v-card-title>
<v-text-field
label="Group Name"
v-if="
settings.item.chat.type === 'group' &&
settings.item.rank === 'admin'
"
v-model="settings.item.chat.name"
></v-text-field>
label="Group Name"
/>
<v-file-input
ref="avatarUpload"
v-model="settings.avatar"
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
placeholder="Avatar"
prepend-icon=""
label="Profile Picture"
v-model="settings.avatar"
@change="doUpload"
></v-file-input>
<v-card-title
>Members
/>
<v-card-title>
Members
<v-btn
v-if="settings.item.rank === 'admin'"
icon
@click.native="settings.addMembers.dialog = true"
v-if="settings.item.rank === 'admin'"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
@ -206,25 +207,25 @@
>
<v-list-item-avatar :color="$vuetify.theme.themes.dark?.primary">
<v-img
v-if="user.user.avatar"
:src="
$store.state.baseURL + '/usercontent/' + user.user.avatar
"
v-if="user.user.avatar"
/>
<v-icon v-else> mdi-account </v-icon>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title
>{{ user.user.username }}
<v-btn text icon v-if="user.rank === 'admin'">
<v-list-item-title>
{{ user.user.username }}
<v-btn v-if="user.rank === 'admin'" text icon>
<v-icon> mdi-gavel </v-icon>
</v-btn>
</v-list-item-title>
</v-list-item-content>
<v-list-item-action v-if="settings.item.rank === 'admin'">
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<div v-on="on" v-bind="attrs">
<template #activator="{ on, attrs }">
<div v-bind="attrs" v-on="on">
<v-btn icon @click="giveUserAdmin(user)">
<v-icon>mdi-account-arrow-up</v-icon>
</v-btn>
@ -258,7 +259,7 @@
<v-card color="card">
<v-toolbar color="toolbar">
<v-toolbar-title>Are you sure you want to leave?</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
icon
@click.native="
@ -276,7 +277,7 @@
</p>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn color="primary" text @click.native="leave.dialog = false">
Cancel
</v-btn>
@ -288,7 +289,7 @@
<v-card color="card">
<v-toolbar color="toolbar">
<v-toolbar-title>New Communication</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click.native="dialogs.new = false">
<v-icon>mdi-close</v-icon>
</v-btn>
@ -306,7 +307,7 @@
item-value="id"
multiple
>
<template v-slot:selection="data">
<template #selection="data">
<v-chip
v-bind="data.attrs"
:input-value="data.selected"
@ -314,25 +315,25 @@
@click="data.select"
@click:close="remove(data.item)"
>
<v-avatar left v-if="data.item.avatar">
<v-avatar v-if="data.item.avatar" left>
<v-img
:src="
$store.state.baseURL + '/usercontent/' + data.item.avatar
"
></v-img>
/>
</v-avatar>
@{{ data.item.username }}
</v-chip>
</template>
<template v-slot:item="data">
<v-avatar left v-if="data.item.avatar" class="mr-3 mb-2 mt-2">
<template #item="data">
<v-avatar v-if="data.item.avatar" left class="mr-3 mb-2 mt-2">
<v-img
:src="
$store.state.baseURL + '/usercontent/' + data.item.avatar
"
></v-img>
/>
</v-avatar>
<v-avatar left v-else class="mr-3 mb-2 mt-2">
<v-avatar v-else left class="mr-3 mb-2 mt-2">
<v-icon>mdi-account</v-icon>
</v-avatar>
<v-list-item-content>
@ -347,7 +348,7 @@
>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn color="error" text @click="dialogs.new = false">
Cancel
</v-btn>
@ -355,8 +356,8 @@
color="primary"
:loading="newConversation.loading"
text
@click="createConversation"
:disabled="!newConversation.users.length"
@click="createConversation"
>
Create
</v-btn>
@ -423,17 +424,17 @@
color="yellow darken-3"
empty-icon="$ratingFull"
hover
></v-rating>
/>
</v-col>
<v-col cols="12">
<v-textarea
class="rounded-xl"
v-model="feedback.text"
class="rounded-xl"
label="Enter your Feedback"
required
autofocus
placeholder="Enter your Feedback"
></v-textarea>
/>
</v-col>
<small
>Your feedback will be used to make
@ -443,7 +444,7 @@
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
class="rounded-xl"
color="blue darken-1"
@ -471,17 +472,17 @@
<v-card-text>
<v-container>
<v-text-field
class="rounded-xl"
v-model="route.value"
class="rounded-xl"
autofocus
@keyup.enter="goToRoute()"
label="Route"
required
></v-text-field>
@keyup.enter="goToRoute()"
/>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn class="rounded-xl" text @click="route.modal = false">
Close
</v-btn>
@ -498,54 +499,54 @@
</v-dialog>
<v-app-bar app color="dark" elevation="5" style="z-index: 15">
<v-app-bar-nav-icon
@click.stop="$store.state.drawer = !$store.state.drawer"
v-if="$vuetify.breakpoint.mobile || !$store.state.drawer"
></v-app-bar-nav-icon>
@click.stop="$store.state.drawer = !$store.state.drawer"
/>
<button
style="display: none"
v-shortkey="['ctrl', 'k']"
style="display: none"
@shortkey="$store.commit('setSearch', true)"
>
Debug
</button>
<button
style="display: none"
v-shortkey="['meta', 'k']"
style="display: none"
@shortkey="$store.commit('setSearch', true)"
>
Debug
</button>
<button
style="display: none"
v-shortkey="['ctrl', 'alt', 'd']"
style="display: none"
@shortkey="$store.dispatch('toggleCSS')"
>
Style Toggle
</button>
<button
style="display: none"
v-shortkey="['f9']"
style="display: none"
@shortkey="$store.dispatch('toggleCSS')"
>
Style Toggle
</button>
<button
style="display: none"
v-shortkey="['ctrl', 'f']"
style="display: none"
@shortkey="$store.state.searchPanel = true"
>
Debug
</button>
<button
style="display: none"
v-shortkey="['ctrl', 'b']"
style="display: none"
@shortkey="route.modal = true"
>
Debug
</button>
<button
style="display: none"
v-shortkey="['ctrl', '/']"
style="display: none"
@shortkey="shortcuts = true"
>
Debug
@ -558,22 +559,22 @@
: chat?.chat?.name
}}
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn v-on="on" icon @click="feedback.modal = true">
<template #activator="{ on }">
<v-btn icon v-on="on" @click="feedback.modal = true">
<v-icon>mdi-bug</v-icon>
</v-btn>
</template>
<span>Provide Feedback</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-btn
icon
v-model="$store.state.context.pins.value"
@click="show($event, 'pins', null, null, true)"
id="pin-button"
v-model="$store.state.context.pins.value"
icon
@click="show($event, 'pins', null, null, true)"
v-on="on"
>
<v-icon>mdi-pin-outline</v-icon>
@ -582,7 +583,7 @@
<span>Chat Pins</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-btn
icon
@click="$store.state.searchPanel = !$store.state.searchPanel"
@ -594,10 +595,10 @@
<span>Search Messages</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-btn
v-on="on"
icon
v-on="on"
@click="$store.state.userPanel = !$store.state.userPanel"
>
<v-icon>mdi-account-group-outline</v-icon>
@ -616,9 +617,10 @@
?.primary
"
class="troplo-title"
@click="$router.push('/')"
style="cursor: pointer"
>{{ $store.state.site.name }}</v-toolbar-title
@click="$router.push('/')"
>
{{ $store.state.site.name }} </v-toolbar-title
><v-app-bar-nav-icon
v-if="
!$vuetify.breakpoint.mobile &&
@ -626,12 +628,13 @@
"
style="z-index: 1000"
disabled
>{{ $store.state.site.release }}</v-app-bar-nav-icon
>
<v-spacer></v-spacer>
{{ $store.state.site.release }}
</v-app-bar-nav-icon>
<v-spacer />
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn v-on="on" icon @click="feedback.modal = true">
<template #activator="{ on }">
<v-btn icon v-on="on" @click="feedback.modal = true">
<v-icon>mdi-bug</v-icon>
</v-btn>
</template>
@ -640,16 +643,16 @@
</template>
</v-app-bar>
<v-navigation-drawer
v-model="$store.state.drawer"
color="dark"
app
floating
style="max-height: 100%; z-index: 15"
class="elevation-5"
v-model="$store.state.drawer"
:width="$vuetify.breakpoint.mobile ? 270 : 320"
>
<v-container>
<v-list dense nav id="comms-sidebar-list">
<v-list id="comms-sidebar-list" dense nav>
<template v-if="$vuetify.breakpoint.mobile">
<v-btn
color="toolbar"
@ -658,7 +661,7 @@
class="mb-3 mr-1 rounded-xl"
elevation="2"
>
<v-icon left>mdi-account-multiple</v-icon>
<v-icon left> mdi-account-multiple </v-icon>
Friends
</v-btn>
<v-btn
@ -668,7 +671,7 @@
elevation="2"
@click="dialogs.new = true"
>
<v-icon left>mdi-plus</v-icon>
<v-icon left> mdi-plus </v-icon>
New
</v-btn>
</template>
@ -680,10 +683,11 @@
class="mb-3 rounded-xl"
elevation="2"
>
<v-icon left>mdi-account-multiple</v-icon>
<v-icon left> mdi-account-multiple </v-icon>
Friends
</v-btn>
<v-text-field
v-model="search"
class="rounded-xl"
filled
solo
@ -692,19 +696,18 @@
background-color="sheet"
style="margin-bottom: -18px"
elevation="2"
v-model="search"
autocomplete="none"
></v-text-field>
/>
<v-toolbar color="sheet" class="rounded-xl mb-3" elevation="2">
<v-toolbar-title class="subtitle-1">
CHATS ({{ chats.length }})
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click="dialogs.new = true">
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-toolbar></template
>
</v-toolbar>
</template>
<v-list v-for="item in chats" :key="item.id">
<template>
<v-list-item
@ -714,10 +717,10 @@
"
>
<v-badge
v-if="item.chat.type === 'direct'"
bordered
bottom
:color="getStatus(item)"
v-if="item.chat.type === 'direct'"
dot
offset-x="24"
offset-y="20"
@ -741,7 +744,7 @@
</v-icon>
</v-list-item-avatar>
</v-badge>
<v-badge dot color="none" v-else>
<v-badge v-else dot color="none">
<v-list-item-avatar
:color="$vuetify.theme.themes.dark?.primary"
>
@ -777,8 +780,7 @@
$route.params.id !== item.id.toString()
"
>
<v-badge color="red" inline :content="item.unread">
</v-badge>
<v-badge color="red" inline :content="item.unread" />
</v-list-item-action>
</template>
</v-list-item>
@ -786,12 +788,12 @@
</v-list>
</v-list>
</v-container>
<template v-slot:append>
<template #append>
<v-card tile color="bg" elevation="0">
<v-divider></v-divider>
<v-divider />
<v-list-item>
<v-menu top offset-y>
<template v-slot:activator="{ on, attrs }">
<template #activator="{ on, attrs }">
<v-badge
bordered
bottom
@ -799,13 +801,13 @@
dot
offset-x="26"
offset-y="19"
v-on="on"
v-bind="attrs"
v-on="on"
>
<v-list-item-avatar
:color="$vuetify.theme.themes.dark?.primary"
v-on="on"
v-bind="attrs"
v-on="on"
>
<v-img
v-if="$store.state.user.avatar"
@ -830,29 +832,28 @@
<v-list-item two-line @click="setStatus('busy')">
<v-list-item-content>
<v-list-item-title>Do not Disturb</v-list-item-title>
<v-list-item-subtitle class="text-wrap"
>You will not receive any
notifications.</v-list-item-subtitle
>
<v-list-item-subtitle class="text-wrap">
You will not receive any notifications.
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-list-item two-line @click="setStatus('invisible')">
<v-list-item-content>
<v-list-item-title>Invisible</v-list-item-title>
<v-list-item-subtitle class="text-wrap"
>You will appear as offline, and the typing indicator will
be disabled.</v-list-item-subtitle
>
<v-list-item-subtitle class="text-wrap">
You will appear as offline, and the typing indicator will
be disabled.
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
<v-tooltip v-model="copyTooltip" top>
<template v-slot:activator="{ attrs }">
<template #activator="{ attrs }">
<v-list-item-content
@click="copyUsername"
v-bind="attrs"
style="cursor: pointer; min-width: 100px"
@click="copyUsername"
>
<v-list-item-title>
{{ $store.state.user.username }}
@ -864,12 +865,12 @@
</template>
<span>Copied!</span>
</v-tooltip>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
v-if="$store.state.user.admin"
icon
text
to="/admin"
v-if="$store.state.user.admin"
style="margin-right: 0; padding-right: 0"
>
<v-icon>mdi-gavel</v-icon>
@ -989,6 +990,73 @@ export default {
})
}
},
watch: {
$route(to) {
this.feedback.route = to.path
}
},
mounted() {
this.feedback.route = this.$route.path
Vue.axios.defaults.headers.common["Authorization"] =
localStorage.getItem("token")
this.searchUsers()
this.searchUsersForGroupAdmin()
this.$store.dispatch("getChats")
this.$socket.on("friendUpdate", () => {
this.searchUsers()
this.searchUsersForGroupAdmin()
})
this.$socket.on("siteState", () => {
this.searchUsers()
this.searchUsersForGroupAdmin()
})
this.$socket.on("userSettings", () => {
this.$store.dispatch("getChats")
})
this.$socket.on("chatUpdated", () => {
this.$store.dispatch("getChats")
})
this.$socket.on("chatAdded", (chat) => {
this.$store.commit("addChat", chat)
})
this.$socket.on("userStatus", (event) => {
this.$store.state.chats.forEach((item) => {
item.chat.associations.forEach((a) => {
if (a.user.id === event.userId) {
a.user.status = event.status
}
})
item.chat.users.forEach((u) => {
if (u.id === event.userId) {
u.status = event.status
}
})
})
})
this.$socket.on("message", (message) => {
const chat = this.$store.state.chats.find(
(item) => item.chatId === message.chatId
)
if (chat) {
const index = this.$store.state.chats.indexOf(chat)
this.$store.state.chats.splice(index, 1)
this.$store.state.chats.unshift(chat)
}
})
this.$socket.on("readChat", (chat) => {
const item = this.$store.state.chats.find((item) => item.id === chat.id)
if (item) {
const index = this.$store.state.chats.indexOf(item)
console.log(this.$store.state.chats[index].lastRead)
this.$store.state.chats[index].lastRead = chat.lastRead
this.$store.state.communicationNotifications = 0
this.$store.state.chats.forEach((item) => {
this.$store.state.communicationNotifications +=
this.getLastRead(item).count
})
}
})
},
methods: {
goToRoute() {
if (this.route.value === "forceEnableDevMode") {
@ -1332,73 +1400,6 @@ export default {
})
.catch(() => {})
}
},
mounted() {
this.feedback.route = this.$route.path
Vue.axios.defaults.headers.common["Authorization"] =
localStorage.getItem("token")
this.searchUsers()
this.searchUsersForGroupAdmin()
this.$store.dispatch("getChats")
this.$socket.on("friendUpdate", () => {
this.searchUsers()
this.searchUsersForGroupAdmin()
})
this.$socket.on("siteState", () => {
this.searchUsers()
this.searchUsersForGroupAdmin()
})
this.$socket.on("userSettings", () => {
this.$store.dispatch("getChats")
})
this.$socket.on("chatUpdated", () => {
this.$store.dispatch("getChats")
})
this.$socket.on("chatAdded", (chat) => {
this.$store.commit("addChat", chat)
})
this.$socket.on("userStatus", (event) => {
this.$store.state.chats.forEach((item) => {
item.chat.associations.forEach((a) => {
if (a.user.id === event.userId) {
a.user.status = event.status
}
})
item.chat.users.forEach((u) => {
if (u.id === event.userId) {
u.status = event.status
}
})
})
})
this.$socket.on("message", (message) => {
const chat = this.$store.state.chats.find(
(item) => item.chatId === message.chatId
)
if (chat) {
const index = this.$store.state.chats.indexOf(chat)
this.$store.state.chats.splice(index, 1)
this.$store.state.chats.unshift(chat)
}
})
this.$socket.on("readChat", (chat) => {
const item = this.$store.state.chats.find((item) => item.id === chat.id)
if (item) {
const index = this.$store.state.chats.indexOf(item)
console.log(this.$store.state.chats[index].lastRead)
this.$store.state.chats[index].lastRead = chat.lastRead
this.$store.state.communicationNotifications = 0
this.$store.state.chats.forEach((item) => {
this.$store.state.communicationNotifications +=
this.getLastRead(item).count
})
}
})
},
watch: {
$route(to) {
this.feedback.route = to.path
}
}
}
</script>

View File

@ -2,27 +2,27 @@
<div>
<template>
<v-toolbar
@click="jumpToMessage(message.replyId)"
v-if="message.reply"
:key="message.keyId + '-reply-toolbar'"
elevation="0"
height="40"
color="card"
v-if="message.reply"
style="cursor: pointer"
@click="jumpToMessage(message.replyId)"
>
<v-icon class="mr-2">mdi-reply</v-icon>
<v-icon class="mr-2"> mdi-reply </v-icon>
<v-avatar size="24" class="mr-2">
<v-img
v-if="message.reply.user.avatar"
:src="
$store.state.baseURL + '/usercontent/' + message.reply.user.avatar
"
v-if="message.reply.user.avatar"
class="elevation-1"
/>
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
</v-avatar>
<template v-if="message.reply.attachments.length">
<v-icon class="mr-2">mdi-file-image</v-icon>
<v-icon class="mr-2"> mdi-file-image </v-icon>
</template>
<template
v-if="!message.reply.content && message.reply.attachments.length"
@ -32,6 +32,7 @@
{{ message.reply.content.substring(0, 100) }}
</v-toolbar>
<v-list-item
:id="'message-' + index"
:key="message.keyId"
:class="{
'pa-0': $vuetify.breakpoint.mobile,
@ -39,22 +40,21 @@
}"
class="message"
:dense="lastMessage"
:id="'message-' + index"
@contextmenu="show($event, 'message', message)"
:style="lastMessage ? 'margin-bottom: -5px; margin-top: -5px;' : ''"
@contextmenu="show($event, 'message', message)"
>
<v-avatar
v-if="!lastMessage"
size="45"
class="mr-2"
v-if="!lastMessage"
:class="{ 'hide-on-hover': message.type }"
>
<v-img
:src="$store.state.baseURL + '/usercontent/' + message.user.avatar"
v-if="message.user.avatar && !message.type"
:src="$store.state.baseURL + '/usercontent/' + message.user.avatar"
class="elevation-1"
/>
<v-icon class="elevation-1" v-else-if="!message.type">
<v-icon v-else-if="!message.type" class="elevation-1">
mdi-account
</v-icon>
<v-icon
@ -86,12 +86,12 @@
</v-icon>
</v-avatar>
<v-tooltip top style="z-index: 15">
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<small
v-on="on"
v-if="lastMessage || message.type"
style="font-size: 9px; position: absolute"
class="grey--text message-date"
v-if="lastMessage || message.type"
v-on="on"
>
{{ $date(message.createdAt).format("hh:mm A") }}
</small>
@ -110,14 +110,14 @@
color="calendarNormalActivity"
small
>
<v-icon small>mdi-robot</v-icon>&nbsp;
<v-icon small> mdi-robot </v-icon>&nbsp;
</v-chip>
<small>
{{ $date(message.createdAt).format("hh:mm A, DD/MM/YYYY") }}
</small>
<v-tooltip top v-if="message.edited">
<template v-slot:activator="{ on, attrs }">
<span v-on="on" v-bind="attrs">
<v-tooltip v-if="message.edited" top>
<template #activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">
<v-icon
color="grey"
small
@ -140,9 +140,9 @@
<span v-markdown>
{{ message.content }}
</span>
<v-tooltip top v-if="message.edited && lastMessage">
<template v-slot:activator="{ on }">
<v-icon color="grey" small v-on="on" style="">
<v-tooltip v-if="message.edited && lastMessage" top>
<template #activator="{ on }">
<v-icon color="grey" small style="" v-on="on">
mdi-pencil
</v-icon>
</template>
@ -155,37 +155,36 @@
<template v-if="edit.id !== message.id">
<Embed
v-for="(embed, index) in message.embeds"
:key="index"
:id="'embed-' + index"
:key="index"
:embed="embed"
:setImagePreview="setImagePreview"
></Embed>
:set-image-preview="setImagePreview"
/>
<v-row v-if="message.poll" no-gutters>
<Poll :message="message"></Poll>
<Poll :message="message" />
</v-row>
</template>
<template v-if="edit.id !== message.id">
<v-card
v-for="(attachment, index) in message.attachments"
:key="attachment.id"
:id="'attachment-' + index"
:key="attachment.id"
:max-width="500"
:min-width="!$vuetify.breakpoint.mobile ? 400 : 0"
elevation="0"
color="card"
>
<v-hover
v-slot="{ hover }"
v-if="
attachment.extension === 'jpg' ||
attachment.extension === 'png' ||
attachment.extension === 'jpeg' ||
attachment.extension === 'gif'
"
v-slot="{ hover }"
>
<div>
<v-img
@click="setImagePreview(attachment)"
contain
:max-width="500"
:max-height="500"
@ -196,8 +195,9 @@
"
:min-height="250"
:min-width="250"
@click="setImagePreview(attachment)"
>
<template v-slot:placeholder>
<template #placeholder>
<v-row
class="fill-height ma-0"
align="center"
@ -206,13 +206,13 @@
<v-progress-circular
indeterminate
color="grey lighten-5"
></v-progress-circular>
/>
</v-row>
</template>
<template v-slot:default>
<template #default>
<v-fade-transition v-if="hover">
<v-overlay absolute>
<v-icon large>mdi-arrow-expand-all</v-icon>
<v-icon large> mdi-arrow-expand-all </v-icon>
</v-overlay>
</v-fade-transition>
</template>
@ -254,29 +254,29 @@
</v-card>
</template>
<CommsInput
v-if="edit.id === message.id"
:edit="edit"
:chat="chat"
:auto-scroll="autoScroll"
:end-edit="endEdit"
v-if="edit.id === message.id"
></CommsInput>
/>
</v-list-item-content>
<v-card
v-if="!$vuetify.breakpoint.mobile"
elevation="8"
color="card"
class="message-action-card"
v-if="!$vuetify.breakpoint.mobile"
>
<v-tooltip top>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<span v-on="on">
<v-btn
icon
v-on="on"
v-if="
message.userId === $store.state.user.id ||
chat.rank === 'admin'
"
icon
v-on="on"
@click="deleteMessage(message)"
>
<v-icon> mdi-delete </v-icon>
@ -286,9 +286,13 @@
<span> Delete </span>
</v-tooltip>
<v-tooltip top>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<span v-on="on">
<v-btn
v-if="
message.userId === $store.state.user.id &&
edit.id !== message.id
"
icon
v-on="on"
@click="
@ -296,10 +300,6 @@
edit.editing = true
edit.id = message.id
"
v-if="
message.userId === $store.state.user.id &&
edit.id !== message.id
"
>
<v-icon> mdi-pencil </v-icon>
</v-btn>
@ -308,20 +308,20 @@
<span> Edit </span>
</v-tooltip>
<v-tooltip top>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<span v-on="on">
<v-btn
v-on="on"
v-if="
message.userId === $store.state.user.id &&
edit.id === message.id
"
icon
v-on="on"
@click="
edit.content = ''
edit.editing = false
edit.id = null
"
v-if="
message.userId === $store.state.user.id &&
edit.id === message.id
"
>
<v-icon> mdi-close </v-icon>
</v-btn>
@ -330,7 +330,7 @@
<span> Discard Edits </span>
</v-tooltip>
<v-tooltip top>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<span v-on="on">
<v-btn
icon
@ -347,12 +347,12 @@
<span> Reply </span>
</v-tooltip>
<v-tooltip top>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<span v-on="on">
<v-btn
v-on="on"
icon
v-if="chat.rank === 'admin' || chat.chat.type === 'direct'"
icon
v-on="on"
@click="
pinMessage()
focusInput()
@ -364,9 +364,9 @@
</template>
<span> Pin to Chat </span>
</v-tooltip>
<v-menu offset-y v-if="$store.state.site.release === 'dev'">
<template v-slot:activator="{ on, attrs }">
<v-btn v-on="on" icon v-bind="attrs">
<v-menu v-if="$store.state.site.release === 'dev'" offset-y>
<template #activator="{ on, attrs }">
<v-btn icon v-bind="attrs" v-on="on">
<v-icon> mdi-dots-horizontal </v-icon>
</v-btn>
</template>
@ -388,6 +388,11 @@ import Poll from "@/components/Poll"
export default {
name: "Message",
components: {
Poll,
CommsInput,
Embed
},
props: [
"message",
"edit",
@ -404,11 +409,6 @@ export default {
"deleteMessage",
"lastMessage"
],
components: {
Poll,
CommsInput,
Embed
},
data() {
return {
graphOptions: {

View File

@ -10,11 +10,11 @@
label="Nickname"
autofocus
@keyup.enter="setFriendNickname"
></v-text-field>
/>
<small>Friend nicknames only show to you.</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
color="blue darken-1"
text

View File

@ -74,6 +74,16 @@ export default {
})
}
},
mounted() {
this.$socket.on(`pollAnswer-${this.message.id}`, (data) => {
this.message.poll.answers = this.message.poll.answers.filter(
(answer) => answer.id !== data.id
)
if (data.answer) {
this.message.poll.answers.push(data.answer)
}
})
},
methods: {
votePoll(option) {
this.axios
@ -84,16 +94,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.$socket.on(`pollAnswer-${this.message.id}`, (data) => {
this.message.poll.answers = this.message.poll.answers.filter(
(answer) => answer.id !== data.id
)
if (data.answer) {
this.message.poll.answers.push(data.answer)
}
})
}
}
</script>

View File

@ -1,27 +1,27 @@
<template>
<div v-if="!message.type" class="rounded-l">
<v-toolbar
@click="jumpToMessage(message.replyId)"
v-if="message.reply"
:key="message.keyId + '-reply-toolbar'"
elevation="0"
height="40"
color="card"
v-if="message.reply"
style="cursor: pointer"
@click="jumpToMessage(message.replyId)"
>
<v-icon class="mr-2">mdi-reply</v-icon>
<v-icon class="mr-2"> mdi-reply </v-icon>
<v-avatar size="24" class="mr-2">
<v-img
v-if="message.reply.user.avatar"
:src="
$store.state.baseURL + '/usercontent/' + message.reply.user.avatar
"
v-if="message.reply.user.avatar"
class="elevation-1"
/>
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
</v-avatar>
<template v-if="message.reply.attachments.length">
<v-icon class="mr-2">mdi-file-image</v-icon>
<v-icon class="mr-2"> mdi-file-image </v-icon>
</template>
<template
v-if="!message.reply.content && message.reply.attachments.length"
@ -31,17 +31,17 @@
{{ message.reply.content.substring(0, 100) }}
</v-toolbar>
<v-list-item
:id="'message-' + index"
:key="message.keyId"
:class="{
'text-xs-right': message.userId === $store.state.user.id,
'text-xs-left': message.userId !== $store.state.user.id
}"
:id="'message-' + index"
>
<v-avatar size="48" class="mr-2">
<v-img
:src="$store.state.baseURL + '/usercontent/' + message.user.avatar"
v-if="message.user.avatar"
:src="$store.state.baseURL + '/usercontent/' + message.user.avatar"
class="elevation-1"
/>
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
@ -50,14 +50,14 @@
<v-list-item-subtitle>
{{ getName(message.user) }}
<v-chip v-if="message.user.bot" color="calendarNormalActivity" small>
<v-icon small>mdi-robot</v-icon>&nbsp;
<v-icon small> mdi-robot </v-icon>&nbsp;
</v-chip>
<small>
{{ $date(message.createdAt).format("hh:mm A, DD/MM/YYYY") }}
</small>
<v-tooltip top v-if="message.edited">
<template v-slot:activator="{ on, attrs }">
<span v-on="on" v-bind="attrs">
<v-tooltip v-if="message.edited" top>
<template #activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">
<v-icon
color="grey"
small

View File

@ -4,42 +4,42 @@
<v-toolbar color="toolbar" height="100">
<v-avatar size="64" class="mr-3 mb-2 mt-2">
<v-img
:src="$store.state.baseURL + '/usercontent/' + user.item.avatar"
v-if="user.item.avatar"
:src="$store.state.baseURL + '/usercontent/' + user.item.avatar"
class="elevation-1"
/>
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
</v-avatar>
<v-toolbar-title>
{{ getName(user.item) }}
<v-tooltip top v-if="user.item.admin">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" small>
<v-tooltip v-if="user.item.admin" top>
<template #activator="{ on }">
<v-btn icon small v-on="on">
<v-icon> mdi-crown </v-icon>
</v-btn>
</template>
<span>Colubrina Instance Administrator</span>
</v-tooltip>
<v-tooltip top v-if="user.item.id < 35">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" small>
<v-tooltip v-if="user.item.id < 35" top>
<template #activator="{ on }">
<v-btn icon small v-on="on">
<v-icon> mdi-alpha-a-circle </v-icon>
</v-btn>
</template>
<span>Early User</span>
</v-tooltip>
<v-tooltip top v-if="user.item.bot">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" small>
<v-tooltip v-if="user.item.bot" top>
<template #activator="{ on }">
<v-btn icon small v-on="on">
<v-icon> mdi-robot </v-icon>
</v-btn>
</template>
<span>Bot</span>
</v-tooltip>
<div class="subheading subtitle-1 text--lighten-2">
<template v-if="user.item.nickname">{{
user.item.username
}}</template>
<template v-if="user.item.nickname">
{{ user.item.username }}
</template>
</div>
</v-toolbar-title>
</v-toolbar>
@ -62,10 +62,7 @@
"
>
<v-overlay :value="loading.mutualFriends" absolute>
<v-progress-circular
indeterminate
size="64"
></v-progress-circular>
<v-progress-circular indeterminate size="64" />
</v-overlay>
<v-list-item
v-for="item in mutualFriends"
@ -75,8 +72,8 @@
<v-list-item-title>
<v-avatar size="24">
<v-img
:src="$store.state.baseURL + '/usercontent/' + item.avatar"
v-if="item.avatar"
:src="$store.state.baseURL + '/usercontent/' + item.avatar"
class="elevation-1"
/>
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
@ -100,10 +97,7 @@
"
>
<v-overlay :value="loading.mutualFriends" absolute>
<v-progress-circular
indeterminate
size="64"
></v-progress-circular>
<v-progress-circular indeterminate size="64" />
</v-overlay>
<v-list-item
v-for="item in mutualGroups"
@ -126,7 +120,7 @@
$vuetify.theme.themes[$vuetify.theme.dark ? 'dark' : 'light'].card
"
>
<v-spacer></v-spacer>
<v-spacer />
<v-btn color="primary" text @click="user.value = false"> Close </v-btn>
</v-card-actions>
</v-card>
@ -149,6 +143,9 @@ export default {
}
}
},
mounted() {
this.onMounted()
},
methods: {
openUserPanel(user) {
this.user.item = user
@ -199,9 +196,6 @@ export default {
})
}
}
},
mounted() {
this.onMounted()
}
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<div id="admin">
<v-overlay :value="loading" absolute>
<v-progress-circular indeterminate size="64"></v-progress-circular>
<v-progress-circular indeterminate size="64" />
</v-overlay>
<v-container v-if="admin">
<v-card color="card" class="rounded-xl">
@ -30,7 +30,7 @@
<span>Config</span>
</v-tab>
</v-tabs>
<router-view :admin="admin" :metrics="metrics"></router-view>
<router-view :admin="admin" :metrics="metrics" />
</v-card>
</v-container>
</div>
@ -48,6 +48,10 @@ export default {
loading: true
}
},
mounted() {
this.getAdminInfo()
this.getAdminMetrics()
},
methods: {
getAdminMetrics() {
this.axios
@ -73,10 +77,6 @@ export default {
this.$router.push("/")
})
}
},
mounted() {
this.getAdminInfo()
this.getAdminMetrics()
}
}
</script>

View File

@ -2,8 +2,8 @@
<div id="admin-feedback">
<v-toolbar color="toolbar">
<v-toolbar-title>Feedback ({{ feedback.count }})</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn @click="getFeedback" icon>
<v-spacer />
<v-btn icon @click="getFeedback">
<v-icon>mdi-refresh</v-icon>
</v-btn>
</v-toolbar>
@ -43,6 +43,9 @@ export default {
feedback: []
}
},
mounted() {
this.getFeedback()
},
methods: {
getFeedback() {
this.axios
@ -51,9 +54,6 @@ export default {
this.feedback = res.data
})
}
},
mounted() {
this.getFeedback()
}
}
</script>

View File

@ -109,10 +109,10 @@ ChartJS.register(
)
export default {
name: "AdminHome",
props: ["admin", "metrics"],
components: {
Chart
},
props: ["admin", "metrics"],
data() {
return {
options: {

View File

@ -12,6 +12,9 @@ export default {
logLines: []
}
},
mounted() {
this.getLogs()
},
methods: {
getLogs() {
this.axios
@ -23,9 +26,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.getLogs()
}
}
</script>

View File

@ -3,46 +3,44 @@
<v-toolbar color="toolbar">
<v-toolbar-title>Site Config</v-toolbar-title>
</v-toolbar>
<v-text-field
class="mx-3"
label="Notification"
v-model="notification"
></v-text-field>
<v-text-field v-model="notification" class="mx-3" label="Notification" />
<v-select
v-model="notificationType"
:items="notificationTypes"
label="Notification Type"
class="mx-3"
v-model="notificationType"
text-text="text"
text-value="value"
>
</v-select>
/>
<v-select
v-model="broadcastType"
:items="broadcastTypes"
label="Broadcast Type"
class="mx-3"
v-model="broadcastType"
text-text="text"
text-value="value"
>
</v-select>
/>
<v-switch
v-model="allowRegistrations"
inset
class="mx-3"
label="Allow registrations"
v-model="allowRegistrations"
></v-switch>
<v-textarea ref="rules" label="Instance Rules" v-model="rules" class="mx-3">
</v-textarea>
/>
<v-textarea
ref="rules"
v-model="rules"
label="Instance Rules"
class="mx-3"
/>
<v-card-title>
<v-icon class="mr-1">mdi-language-markdown</v-icon>Rules Preview:
<v-icon class="mr-1"> mdi-language-markdown </v-icon>Rules Preview:
</v-card-title>
<v-card-text>
<span v-markdown class="mx-3" :key="rules">{{ rules }}</span>
<span :key="rules" v-markdown class="mx-3">{{ rules }}</span>
</v-card-text>
<v-btn text class="mx-3 mb-3" color="primary" @click="updateState"
>Save</v-btn
>
<v-btn text class="mx-3 mb-3" color="primary" @click="updateState">
Save
</v-btn>
</div>
</template>
@ -75,6 +73,12 @@ export default {
]
}
},
mounted() {
this.notification = this.$store.state.site.notification
this.notificationType = this.$store.state.site.notificationType
this.allowRegistrations = this.$store.state.site.allowRegistrations
this.rules = this.$store.state.site.rules
},
methods: {
updateState() {
this.axios
@ -92,12 +96,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.notification = this.$store.state.site.notification
this.notificationType = this.$store.state.site.notificationType
this.allowRegistrations = this.$store.state.site.allowRegistrations
this.rules = this.$store.state.site.rules
}
}
</script>

View File

@ -2,8 +2,8 @@
<div id="admin-themes">
<v-toolbar color="toolbar">
<v-toolbar-title>Themes ({{ themes.count }})</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn @click="getThemes" icon>
<v-spacer />
<v-btn icon @click="getThemes">
<v-icon>mdi-refresh</v-icon>
</v-btn>
</v-toolbar>
@ -16,10 +16,10 @@
$vuetify.theme.themes[$vuetify.theme.dark ? 'dark' : 'light'].card
"
>
<template v-slot:[`item.actions`]="{ item }">
<template #[`item.actions`]="{ item }">
<v-btn text color="primary" @click="applyTheme(item)"> Apply </v-btn>
</template>
<template v-slot:[`item.theme.css`]="{ item }">
<template #[`item.theme.css`]="{ item }">
{{ item.theme.css ? "Yes" : "No" }}
</template>
</v-data-table>
@ -78,6 +78,9 @@ export default {
]
}
},
mounted() {
this.getThemes()
},
methods: {
applyTheme(theme) {
this.axios
@ -110,9 +113,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.getThemes()
}
}
</script>

View File

@ -1,39 +1,36 @@
<template>
<div id="admin-users">
<v-dialog width="500" v-model="create.dialog">
<v-dialog v-model="create.dialog" width="500">
<v-card color="card">
<v-toolbar color="toolbar">
<v-toolbar-title>Create User</v-toolbar-title>
</v-toolbar>
<v-container>
<v-form @submit.prevent="createUser">
<v-text-field v-model="create.username" label="Username" />
<v-text-field v-model="create.email" label="Email" />
<v-text-field
label="Username"
v-model="create.username"
></v-text-field>
<v-text-field label="Email" v-model="create.email"></v-text-field>
<v-text-field
v-model="create.password"
label="Password"
type="password"
v-model="create.password"
></v-text-field>
/>
<v-switch
v-model="create.emailVerified"
inset
label="Email Verified?"
v-model="create.emailVerified"
></v-switch>
<v-btn text type="submit" color="primary">Create</v-btn>
/>
<v-btn text type="submit" color="primary"> Create </v-btn>
</v-form>
</v-container>
</v-card>
</v-dialog>
<v-toolbar color="toolbar">
<v-toolbar-title>Users ({{ users.count }})</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn @click="create.dialog = true" icon>
<v-spacer />
<v-btn icon @click="create.dialog = true">
<v-icon>mdi-plus</v-icon>
</v-btn>
<v-btn @click="getUsers" icon>
<v-btn icon @click="getUsers">
<v-icon>mdi-refresh</v-icon>
</v-btn>
</v-toolbar>
@ -46,17 +43,17 @@
$vuetify.theme.themes[$vuetify.theme.dark ? 'dark' : 'light'].card
"
>
<template v-slot:[`item.index`]="{ index }">
<template #[`item.index`]="{ index }">
{{ index }}
</template>
<template v-slot:[`item.actions`]="{ item }">
<template #[`item.actions`]="{ item }">
<v-tooltip top>
<template v-slot:activator="{ on }">
<template #activator="{ on }">
<v-btn
v-on="on"
icon
@click="banUser(item)"
:disabled="item.id === $store.state.user.id || item.admin"
v-on="on"
@click="banUser(item)"
>
<v-icon>mdi-gavel</v-icon>
</v-btn>
@ -141,6 +138,9 @@ export default {
]
}
},
mounted() {
this.getUsers()
},
methods: {
createUser() {
this.axios
@ -197,9 +197,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.getUsers()
}
}
</script>

View File

@ -5,7 +5,7 @@
:chat="selectedChat"
:loading="false"
:items="$store.state.chats"
></router-view>
/>
</div>
</template>
@ -23,6 +23,11 @@ export default {
}
}
},
watch: {
selectedChat() {
this.$store.commit("setSelectedChat", this.selectedChat)
}
},
mounted() {
this.$socket.on("memberListUpdate", () => {
this.$store.dispatch("getChats")
@ -30,11 +35,6 @@ export default {
if (!this.$route.params.id) {
this.$router.push("/communications/friends")
}
},
watch: {
selectedChat() {
this.$store.commit("setSelectedChat", this.selectedChat)
}
}
}
</script>

View File

@ -1,9 +1,9 @@
<template>
<div id="communications-chat" @dragover.prevent @drop.prevent="handleDrag">
<v-menu
v-model="$store.state.context.pins.value"
:position-x="$store.state.context.pins.x"
:position-y="60"
v-model="$store.state.context.pins.value"
class="rounded-l elevation-7"
absolute
transition="scroll-y-transition"
@ -12,24 +12,24 @@
>
<v-card min-width="400" max-width="400" color="toolbar">
<v-toolbar color="toolbar lighten-1">
<v-spacer></v-spacer>
<v-spacer />
<v-toolbar-title> Pins </v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
</v-toolbar>
<v-divider></v-divider>
<v-divider />
<v-container>
<v-list dense v-if="pins.length" :max-height="600">
<v-list v-if="pins.length" dense :max-height="600">
<v-list-item
@click="jumpToMessage(pin.message.id)"
v-for="(pin, index) in pins"
:key="index"
@click="jumpToMessage(pin.message.id)"
>
<SimpleMessage
:key="pin.message.keyId"
:message="pin.message"
:index="index"
:key="pin.message.keyId"
></SimpleMessage>
<v-spacer></v-spacer>
/>
<v-spacer />
<v-btn icon text @click.stop="removePin(pin.messageId)">
<v-icon> mdi-close </v-icon>
</v-btn>
@ -52,7 +52,7 @@
offset-y
class="rounded-l"
>
<v-list class="rounded-l" v-if="context.message.item">
<v-list v-if="context.message.item" class="rounded-l">
<v-list-item @click="copy(context.message.item.content)">
<v-list-item-title>Copy Message Content</v-list-item-title>
</v-list-item>
@ -60,15 +60,15 @@
<v-list-item-title>Reply to Message</v-list-item-title>
</v-list-item>
<v-list-item
v-if="
context.message.item.userId === $store.state.user.id &&
edit.id !== context.message.item.id
"
@click="
edit.content = context.message.item.content
edit.editing = true
edit.id = context.message.item.id
"
v-if="
context.message.item.userId === $store.state.user.id &&
edit.id !== context.message.item.id
"
>
<v-list-item-title>Edit Message</v-list-item-title>
</v-list-item>
@ -81,9 +81,9 @@
</v-list>
</v-menu>
<UserDialog
:user="context.userPopout"
:key="context.userPopout.item?.id || 0"
></UserDialog>
:user="context.userPopout"
/>
<NicknameDialog :nickname="nickname" />
<v-dialog
v-model="preview.dialog"
@ -100,7 +100,7 @@
:max-height="600"
:min-height="300"
contain
></v-img>
/>
<v-container>
<a :href="preview.src" style="text-decoration: none" target="_blank">
<small> Open Externally </small>
@ -115,10 +115,10 @@
</v-card>
</v-dialog>
<v-navigation-drawer
v-if="$vuetify.breakpoint.mobile"
v-model="$store.state.userPanel"
color="bg"
floating
v-if="$vuetify.breakpoint.mobile"
app
right
style="z-index: 100"
@ -127,10 +127,10 @@
<v-list-item-group class="rounded-xl">
<template v-for="item in associations">
<v-list-item
:id="'user-popout-' + item.userId"
:key="item.title"
@contextmenu="show($event, 'user', item.user)"
@click="openUserPanel(item.user)"
:id="'user-popout-' + item.userId"
>
<v-badge
bordered
@ -162,10 +162,10 @@
</v-list-item-group>
</v-list>
</v-navigation-drawer>
<v-row @drop="handleDrag" no-gutters style="overflow: hidden">
<v-row no-gutters style="overflow: hidden" @drop="handleDrag">
<v-col
class="flex-grow-1 flex-shrink-1 pb-0"
id="chat-col"
class="flex-grow-1 flex-shrink-1 pb-0"
style="overflow: hidden"
>
<v-card
@ -176,40 +176,41 @@
>
<v-card-text>
<v-toolbar
@click="jumpToMessage(replying?.id)"
v-if="replying"
elevation="0"
height="35"
color="card"
v-if="replying"
style="cursor: pointer; overflow: hidden"
@click="jumpToMessage(replying?.id)"
>
<v-icon class="mr-2">mdi-reply</v-icon>
<v-icon class="mr-2"> mdi-reply </v-icon>
<v-avatar size="24" class="mr-2">
<v-img
v-if="replying.user.avatar"
:src="
$store.state.baseURL +
'/usercontent/' +
replying.user.avatar
"
v-if="replying.user.avatar"
class="elevation-1"
/>
<v-icon v-else class="elevation-1"> mdi-account </v-icon>
</v-avatar>
<template v-if="replying.attachments.length">
<v-icon class="mr-2">mdi-file-image</v-icon>
<v-icon class="mr-2"> mdi-file-image </v-icon>
</template>
<template v-if="!replying.content && replying.attachments.length">
Click to view attachment
</template>
{{ replying.content.substring(0, 100) }}
<v-spacer></v-spacer>
<v-btn icon @click="replying = null" class="mr-2" small>
<v-spacer />
<v-btn icon class="mr-2" small @click="replying = null">
<v-icon> mdi-close </v-icon>
</v-btn>
</v-toolbar>
<v-fade-transition v-model="avoidAutoScroll">
<v-toolbar
v-if="avoidAutoScroll"
height="24"
color="toolbar"
elevation="0"
@ -223,7 +224,6 @@
"
width="100%"
@click="forceScroll"
v-if="avoidAutoScroll"
>
<div>
<v-icon size="16px"> mdi-arrow-down </v-icon>
@ -232,17 +232,17 @@
</v-toolbar>
</v-fade-transition>
<v-fade-transition
v-model="usersTyping.length"
v-if="$vuetify.breakpoint.mobile"
v-model="usersTyping.length"
>
<div
v-if="usersTyping.length"
style="
border-radius: 0 0 20px 20px;
position: relative;
top: -30px;
margin-bottom: -22px;
"
v-if="usersTyping.length"
>
{{ usersTyping.map((user) => getName(user)).join(", ") }}
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
@ -251,29 +251,29 @@
<CommsInput
:chat="chat"
:replying="replying"
:editLastMessage="editLastMessage"
:autoScroll="autoScroll"
:endSend="endSend"
></CommsInput>
:edit-last-message="editLastMessage"
:auto-scroll="autoScroll"
:end-send="endSend"
/>
<v-fade-transition
v-model="usersTyping.length"
v-if="!$vuetify.breakpoint.mobile"
v-model="usersTyping.length"
>
<div
v-if="usersTyping.length"
style="
border-radius: 0 0 20px 20px;
position: absolute;
margin-top: -2px;
bottom: 1px;
"
v-if="usersTyping.length"
>
{{ usersTyping.map((user) => getName(user)).join(", ") }}
{{ usersTyping.length > 1 ? " are" : " is" }} typing...
</div>
</v-fade-transition>
</v-card-text>
<v-card-text class="flex-grow-1 overflow-y-auto" id="message-list">
<v-card-text id="message-list" class="flex-grow-1 overflow-y-auto">
<v-card-title
v-if="
reachedTop && $store.state.selectedChat?.chat?.type === 'group'
@ -299,11 +299,11 @@
indeterminate
size="64"
style="display: block; width: 100px; margin: 0 auto"
></v-progress-circular>
/>
<template v-for="(message, index) in messages">
<div
:key="'div2-' + message.keyId"
v-if="message.readReceipts.length"
:key="'div2-' + message.keyId"
>
<v-tooltip
v-for="association in message.readReceipts"
@ -311,8 +311,8 @@
top
>
<template
v-slot:activator="{ on }"
v-if="association.user.id !== $store.state.user.id"
#activator="{ on }"
>
<v-btn
icon
@ -324,7 +324,7 @@
style="float: right"
@click="openUserPanel(association.user)"
>
<v-avatar size="20" v-on="on" color="primary">
<v-avatar size="20" color="primary" v-on="on">
<img
v-if="association.user.avatar"
:src="
@ -371,16 +371,16 @@
!message.replyId &&
!message.type
"
></Message>
/>
</template>
</v-card-text>
</v-card>
</v-col>
<v-col
v-if="$store.state.searchPanel && !$vuetify.breakpoint.mobile"
id="search-col"
cols="3"
class=""
id="search-col"
v-if="$store.state.searchPanel && !$vuetify.breakpoint.mobile"
style="z-index: 15"
>
<v-card
@ -393,7 +393,7 @@
<v-toolbar-title>
Search ({{ search.pager.totalItems || 0 }})
</v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
<v-btn icon @click="$store.state.searchPanel = false">
<v-icon>mdi-close</v-icon>
</v-btn>
@ -405,13 +405,13 @@
outlined
autofocus
@keydown.enter="doSearch"
></v-text-field>
<v-list two-line color="card" ref="message-list-search">
/>
<v-list ref="message-list-search" two-line color="card">
<template v-for="(message, index) in search.results">
<div
@click="jumpToMessage(message.id)"
:key="message.keyId"
style="cursor: pointer"
@click="jumpToMessage(message.id)"
>
<Message
:message="message"
@ -428,7 +428,7 @@
:set-image-preview="setImagePreview"
:delete-message="deleteMessage"
:last-message="false"
></Message>
/>
</div>
</template>
<v-pagination
@ -436,19 +436,19 @@
class="my-4"
:length="search.pager.totalPages"
@input="doSearch"
></v-pagination>
/>
</v-list>
</v-card-text>
</v-card>
</v-col>
<v-col
:cols="$vuetify.breakpoint.xl ? 2 : 3"
id="user-col"
v-if="
$store.state.userPanel &&
!$vuetify.breakpoint.mobile &&
!$store.state.searchPanel
"
id="user-col"
:cols="$vuetify.breakpoint.xl ? 2 : 3"
>
<v-card
class="d-flex flex-column fill-height rounded-0"
@ -464,17 +464,17 @@
offset-y
class="rounded-l"
>
<v-list class="rounded-l" v-if="context.user.item">
<v-list v-if="context.user.item" class="rounded-l">
<v-list-item
@click="
nickname.dialog = true
nickname.user = context.user.item
"
>
<v-list-item-title
>Change Friend Nickname for
{{ context.user.item.username }}</v-list-item-title
>
<v-list-item-title>
Change Friend Nickname for
{{ context.user.item.username }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
@ -482,10 +482,10 @@
<v-list-item-group class="rounded-xl">
<template v-for="item in associations">
<v-list-item
:id="'user-popout-' + item.userId"
:key="item.title"
@contextmenu="show($event, 'user', item.user)"
@click="openUserPanel(item.user)"
:id="'user-popout-' + item.userId"
>
<v-badge
bordered
@ -513,25 +513,25 @@
<v-list-item-content>
<v-list-item-title>
{{ getName(item.user) }}
<v-tooltip top v-if="item.user.admin">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" small>
<v-tooltip v-if="item.user.admin" top>
<template #activator="{ on }">
<v-btn icon small v-on="on">
<v-icon> mdi-crown </v-icon>
</v-btn>
</template>
<span>Colubrina Instance Administrator</span>
</v-tooltip>
<v-tooltip top v-if="item.user.bot">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" small>
<v-tooltip v-if="item.user.bot" top>
<template #activator="{ on }">
<v-btn icon small v-on="on">
<v-icon> mdi-robot </v-icon>
</v-btn>
</template>
<span>Bot</span>
</v-tooltip>
<v-tooltip top v-if="item.user.id < 35">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" small>
<v-tooltip v-if="item.user.id < 35" top>
<template #activator="{ on }">
<v-btn icon small v-on="on">
<v-icon> mdi-alpha-a-circle </v-icon>
</v-btn>
</template>
@ -709,6 +709,178 @@ export default {
}
}
},
watch: {
"$store.state.context.pins.value"(val) {
if (val) {
this.getPins()
}
},
userPanel() {
localStorage.setItem("userPanel", JSON.stringify(this.userPanel))
},
"$route.path"() {
const tryParse = this.$route.params.id
if (!tryParse) {
// remove event listeners
document.removeEventListener("keypress", this.focusKey)
document.removeEventListener("keydown", this.escPressed)
document
.getElementById("message-list")
.removeEventListener("scroll", this.scrollEvent)
clearInterval(this.interval)
}
},
"$route.params.id"(val, oldVal) {
this.focusInput()
let drafts = {}
if (localStorage.getItem("drafts")) {
drafts = JSON.parse(localStorage.getItem("drafts"))
}
if (this.message && drafts[oldVal]) {
drafts[oldVal] = this.message
localStorage.setItem("drafts", JSON.stringify(drafts))
} else if (!this.message && drafts[oldVal]) {
drafts[oldVal] = ""
}
this.message = drafts[val] || ""
this.usersTyping = []
this.replying = null
this.reachedTop = false
this.avoidAutoScroll = false
this.offset = null
this.pins = []
this.messages = []
this.getMessages()
}
},
mounted() {
this.$socket.on("memberListUpdate", () => {
this.$store.dispatch("getChats")
})
if (!this.$route.params.id) {
this.$router.push("/communications/friends")
return
}
document.addEventListener("keypress", this.focusKey)
document.addEventListener("keydown", this.escPressed)
document
.getElementById("message-list")
.addEventListener("scroll", this.scrollEvent)
this.interval = setInterval(() => {
this.typing()
if (
document.hasFocus() &&
this.messages[this.messages.length - 1]?.id !== this.lastRead
) {
this.markRead()
}
}, 1000)
this.getMessages()
if (localStorage.getItem("userPanel")) {
this.userPanel = JSON.parse(localStorage.getItem("userPanel"))
} else {
localStorage.setItem("userPanel", true)
}
let drafts = {}
if (localStorage.getItem("drafts")) {
drafts = JSON.parse(localStorage.getItem("drafts"))
}
if (drafts[this.$route.params.id]) {
this.message = drafts[this.$route.params.id]
}
this.$socket.on("readChat", (data) => {
if (!this.chat) return
if (data.id === this.chat?.id) {
this.lastRead = data.lastRead
}
})
this.$socket.on("readReceipt", (data) => {
if (!this.chat) return
try {
if (
data.messageId &&
data.chatId === this.chat.chatId &&
this.messages?.length
) {
this.messages.forEach((message) => {
message.readReceipts = message.readReceipts.filter(
(readReceipt) => readReceipt.id !== data.id
)
})
this.messages
.find((message) => message.id === data.messageId)
.readReceipts?.push(data)
this.autoScroll()
}
} catch (e) {
console.log("Read receipt error", e)
}
})
this.$socket.on("message", (message) => {
try {
if (!this.chat) return
if (message.chatId === this.chat.chatId) {
this.messages.push(message)
this.autoScroll()
if (document.hasFocus()) {
this.markRead()
}
if (this.messages.length > 50 && !this.avoidAutoScroll) {
this.messages.shift()
this.reachedTop = false
}
}
} catch (e) {
console.log("Message error", e)
}
})
this.$socket.on("editMessage", (message) => {
if (message.chatId === this.chat.chatId) {
const index = this.messages.findIndex((item) => item.id === message.id)
if (index !== -1) {
this.messages[index].content = message.content
this.messages[index].edited = message.edited
this.messages[index].editedAt = message.editedAt
this.messages[index].keyId = message.id + "-" + message.editedAt
}
}
})
this.$socket.on("messageEmbedResolved", (message) => {
if (message.chatId === this.chat.chatId) {
const index = this.messages.findIndex((item) => item.id === message.id)
if (index !== -1) {
this.messages[index].keyId = message.id + "-" + message.editedAt
this.messages[index].embeds = message.embeds
this.autoScroll()
}
}
})
this.$socket.on("typing", (event) => {
if (event.chatId === this.chat.chatId) {
const index = this.usersTyping.findIndex(
(item) => item.userId === event.userId
)
if (index > -1) {
this.usersTyping.splice(index, 1)
}
this.usersTyping.push(event)
}
})
this.$socket.on("deleteMessage", (message) => {
if (message.chatId === this.chat.chatId) {
const index = this.messages.findIndex((item) => item.id === message.id)
if (index !== -1) {
this.messages.splice(index, 1)
}
}
})
},
destroyed() {
document.removeEventListener("keypress", this.focusKey)
document.removeEventListener("keydown", this.escPressed)
document.removeEventListener("scroll", this.scrollEvent)
clearInterval(this.interval)
},
methods: {
copy(content) {
navigator.clipboard.writeText(content)
@ -1090,178 +1262,6 @@ export default {
this.$store.state.searchPanel = false
}
}
},
mounted() {
this.$socket.on("memberListUpdate", () => {
this.$store.dispatch("getChats")
})
if (!this.$route.params.id) {
this.$router.push("/communications/friends")
return
}
document.addEventListener("keypress", this.focusKey)
document.addEventListener("keydown", this.escPressed)
document
.getElementById("message-list")
.addEventListener("scroll", this.scrollEvent)
this.interval = setInterval(() => {
this.typing()
if (
document.hasFocus() &&
this.messages[this.messages.length - 1]?.id !== this.lastRead
) {
this.markRead()
}
}, 1000)
this.getMessages()
if (localStorage.getItem("userPanel")) {
this.userPanel = JSON.parse(localStorage.getItem("userPanel"))
} else {
localStorage.setItem("userPanel", true)
}
let drafts = {}
if (localStorage.getItem("drafts")) {
drafts = JSON.parse(localStorage.getItem("drafts"))
}
if (drafts[this.$route.params.id]) {
this.message = drafts[this.$route.params.id]
}
this.$socket.on("readChat", (data) => {
if (!this.chat) return
if (data.id === this.chat?.id) {
this.lastRead = data.lastRead
}
})
this.$socket.on("readReceipt", (data) => {
if (!this.chat) return
try {
if (
data.messageId &&
data.chatId === this.chat.chatId &&
this.messages?.length
) {
this.messages.forEach((message) => {
message.readReceipts = message.readReceipts.filter(
(readReceipt) => readReceipt.id !== data.id
)
})
this.messages
.find((message) => message.id === data.messageId)
.readReceipts?.push(data)
this.autoScroll()
}
} catch (e) {
console.log("Read receipt error", e)
}
})
this.$socket.on("message", (message) => {
try {
if (!this.chat) return
if (message.chatId === this.chat.chatId) {
this.messages.push(message)
this.autoScroll()
if (document.hasFocus()) {
this.markRead()
}
if (this.messages.length > 50 && !this.avoidAutoScroll) {
this.messages.shift()
this.reachedTop = false
}
}
} catch (e) {
console.log("Message error", e)
}
})
this.$socket.on("editMessage", (message) => {
if (message.chatId === this.chat.chatId) {
const index = this.messages.findIndex((item) => item.id === message.id)
if (index !== -1) {
this.messages[index].content = message.content
this.messages[index].edited = message.edited
this.messages[index].editedAt = message.editedAt
this.messages[index].keyId = message.id + "-" + message.editedAt
}
}
})
this.$socket.on("messageEmbedResolved", (message) => {
if (message.chatId === this.chat.chatId) {
const index = this.messages.findIndex((item) => item.id === message.id)
if (index !== -1) {
this.messages[index].keyId = message.id + "-" + message.editedAt
this.messages[index].embeds = message.embeds
this.autoScroll()
}
}
})
this.$socket.on("typing", (event) => {
if (event.chatId === this.chat.chatId) {
const index = this.usersTyping.findIndex(
(item) => item.userId === event.userId
)
if (index > -1) {
this.usersTyping.splice(index, 1)
}
this.usersTyping.push(event)
}
})
this.$socket.on("deleteMessage", (message) => {
if (message.chatId === this.chat.chatId) {
const index = this.messages.findIndex((item) => item.id === message.id)
if (index !== -1) {
this.messages.splice(index, 1)
}
}
})
},
watch: {
"$store.state.context.pins.value"(val) {
if (val) {
this.getPins()
}
},
userPanel() {
localStorage.setItem("userPanel", JSON.stringify(this.userPanel))
},
"$route.path"() {
const tryParse = this.$route.params.id
if (!tryParse) {
// remove event listeners
document.removeEventListener("keypress", this.focusKey)
document.removeEventListener("keydown", this.escPressed)
document
.getElementById("message-list")
.removeEventListener("scroll", this.scrollEvent)
clearInterval(this.interval)
}
},
"$route.params.id"(val, oldVal) {
this.focusInput()
let drafts = {}
if (localStorage.getItem("drafts")) {
drafts = JSON.parse(localStorage.getItem("drafts"))
}
if (this.message && drafts[oldVal]) {
drafts[oldVal] = this.message
localStorage.setItem("drafts", JSON.stringify(drafts))
} else if (!this.message && drafts[oldVal]) {
drafts[oldVal] = ""
}
this.message = drafts[val] || ""
this.usersTyping = []
this.replying = null
this.reachedTop = false
this.avoidAutoScroll = false
this.offset = null
this.pins = []
this.messages = []
this.getMessages()
}
},
destroyed() {
document.removeEventListener("keypress", this.focusKey)
document.removeEventListener("keydown", this.escPressed)
document.removeEventListener("scroll", this.scrollEvent)
clearInterval(this.interval)
}
}
</script>

View File

@ -1,8 +1,8 @@
<template>
<div id="communications-friends">
<v-card color="card" class="rounded-0" :height="viewport()" elevation="0">
<v-tabs centered background-color="card" v-model="tab">
<v-tab :key="0">Users</v-tab>
<v-tabs v-model="tab" centered background-color="card">
<v-tab :key="0"> Users </v-tab>
<v-tab :key="1"> Friends </v-tab>
<v-tab :key="2"> Add new friend </v-tab>
<v-tab-item
@ -16,31 +16,31 @@
<v-toolbar-title> Users </v-toolbar-title>
</v-toolbar>
<v-card color="card" elevation="0">
<v-list color="card" v-if="$store.state.site.publicUsers">
<v-list v-if="$store.state.site.publicUsers" color="card">
<v-list-item v-for="user in users" :key="user.id">
<v-list-item-avatar
@click="userProfile(user)"
style="cursor: pointer"
:color="$vuetify.theme.themes.dark?.primary"
@click="userProfile(user)"
>
<v-img
v-if="user.avatar"
:src="
$store.state.baseURL + '/usercontent//' + user.avatar
"
v-if="user.avatar"
/>
<v-icon v-else> mdi-account </v-icon>
</v-list-item-avatar>
<v-list-item-content
@click="userProfile(user)"
style="cursor: pointer"
@click="userProfile(user)"
>
<v-list-item-title>
{{ user.username }}
<v-tooltip top v-if="user.admin">
<template v-slot:activator="{ on }">
<v-btn icon v-on="on" class="ml-1">
<v-tooltip v-if="user.admin" top>
<template #activator="{ on }">
<v-btn icon class="ml-1" v-on="on">
<v-icon> mdi-crown </v-icon>
</v-btn>
</template>
@ -55,7 +55,9 @@
"
>
<v-btn icon>
<v-icon @click="addFriend(user)">mdi-account-plus</v-icon>
<v-icon @click="addFriend(user)">
mdi-account-plus
</v-icon>
</v-btn>
</v-list-item-action>
<v-list-item-action
@ -70,9 +72,9 @@
</v-list-item-action>
</v-list-item>
</v-list>
<v-card-title v-else
>Public users are not enabled on this instance.</v-card-title
>
<v-card-title v-else>
Public users are not enabled on this instance.
</v-card-title>
</v-card>
</v-card>
</v-tab-item>
@ -100,16 +102,16 @@
:key="friend.id"
>
<v-list-item-avatar
@click="userProfile(friend.user2)"
:color="$vuetify.theme.themes.dark?.primary"
@click="userProfile(friend.user2)"
>
<v-img
v-if="friend.user2.avatar"
:src="
$store.state.baseURL +
'/usercontent//' +
friend.user2.avatar
"
v-if="friend.user2.avatar"
/>
<v-icon v-else> mdi-account </v-icon>
</v-list-item-avatar>
@ -121,16 +123,16 @@
</v-list-item-content>
<v-list-item-action>
<v-btn icon>
<v-icon color="error" @click="removeFriend(friend)"
>mdi-close</v-icon
>
<v-icon color="error" @click="removeFriend(friend)">
mdi-close
</v-icon>
</v-btn>
</v-list-item-action>
<v-list-item-action>
<v-btn icon>
<v-icon color="success" @click="acceptFriend(friend)">
mdi-check</v-icon
>
mdi-check
</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
@ -153,24 +155,24 @@
:key="friend.id"
>
<v-list-item-avatar
@click="userProfile(friend.user2)"
style="cursor: pointer"
:color="$vuetify.theme.themes.dark?.primary"
@click="userProfile(friend.user2)"
>
<v-img
v-if="friend.user2.avatar"
:src="
$store.state.baseURL +
'/usercontent//' +
friend.user2.avatar
"
v-if="friend.user2.avatar"
/>
<v-icon v-else> mdi-account </v-icon>
</v-list-item-avatar>
<v-list-item-content
@click="userProfile(friend.user2)"
style="cursor: pointer"
@click="userProfile(friend.user2)"
>
<v-list-item-title>
{{ friend.user2.username }}
@ -178,9 +180,9 @@
</v-list-item-content>
<v-list-item-action>
<v-btn icon>
<v-icon color="error" @click="removeFriend(friend)"
>mdi-close</v-icon
>
<v-icon color="error" @click="removeFriend(friend)">
mdi-close
</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
@ -200,17 +202,17 @@
</v-list-item>
<v-list-item v-for="friend in computeAccepted" :key="friend.id">
<v-list-item-avatar
@click="userProfile(friend.user2)"
style="cursor: pointer"
:color="$vuetify.theme.themes.dark?.primary"
@click="userProfile(friend.user2)"
>
<v-img
v-if="friend.user2.avatar"
:src="
$store.state.baseURL +
'/usercontent//' +
friend.user2.avatar
"
v-if="friend.user2.avatar"
/>
<v-icon v-else> mdi-account </v-icon>
</v-list-item-avatar>
@ -222,9 +224,9 @@
</v-list-item-content>
<v-list-item-action>
<v-btn icon>
<v-icon color="error" @click="removeFriend(friend)"
>mdi-close</v-icon
>
<v-icon color="error" @click="removeFriend(friend)">
mdi-close
</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
@ -246,15 +248,14 @@
friends with them. You can add them here.
</p>
<v-text-field
@keyup.enter="addFriend(null)"
v-model="friend"
label="Friend username"
:placeholder="'BTR0001'"
v-model="friend"
>
</v-text-field>
@keyup.enter="addFriend(null)"
/>
</v-container>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn color="primary" text @click="addFriend(null)">
Send Request
</v-btn>
@ -292,6 +293,19 @@ export default {
)
}
},
async mounted() {
this.getFriends()
this.getUsers()
this.$socket.on("friendRequest", () => {
this.getFriends()
})
this.$socket.on("friendUpdate", () => {
this.getFriends()
})
this.$socket.on("friendAccepted", () => {
this.getFriends()
})
},
methods: {
userProfile() {
// todo
@ -372,19 +386,6 @@ export default {
this.friends = res.data
})
}
},
async mounted() {
this.getFriends()
this.getUsers()
this.$socket.on("friendRequest", () => {
this.getFriends()
})
this.$socket.on("friendUpdate", () => {
this.getFriends()
})
this.$socket.on("friendAccepted", () => {
this.getFriends()
})
}
}
</script>

View File

@ -6,11 +6,7 @@
<v-toolbar-title> Email Confirmation </v-toolbar-title>
</v-toolbar>
<v-container v-if="loading" class="text-center justify-center">
<v-progress-circular
size="64"
:indeterminate="true"
class="mb-3"
></v-progress-circular>
<v-progress-circular size="64" :indeterminate="true" class="mb-3" />
<h3>We're currently confirming your email address. Please wait.</h3>
</v-container>
<v-container v-else-if="failed" class="text-center justify-center">

View File

@ -11,9 +11,9 @@
follow the instructions provided.
</h3>
<p class="mt-2">Haven't received it?</p>
<v-btn color="primary" text @click="resend" :loading="loading"
>Resend</v-btn
>
<v-btn color="primary" text :loading="loading" @click="resend">
Resend
</v-btn>
</v-container>
</v-card>
</v-container>
@ -30,6 +30,9 @@ export default {
loading: true
}
},
mounted() {
this.resend()
},
methods: {
resend() {
this.loading = true
@ -44,9 +47,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.resend()
}
}
</script>

View File

@ -1,24 +1,24 @@
<template>
<div id="login" v-if="!$store.state.user?.bcUser?.id">
<div v-if="!$store.state.user?.bcUser?.id" id="login">
<v-dialog v-model="totpDialog" max-width="500px">
<v-card color="card">
<v-toolbar color="toolbar">
<v-toolbar-title> Two-Factor Authentication </v-toolbar-title>
</v-toolbar>
<v-container>
<v-card-text
>You are seeing this because you have enabled Two-Factor
<v-card-text>
You are seeing this because you have enabled Two-Factor
Authentication on {{ $store.state.site.name }}.<br />
Please check your phone, or authenticator app to obtain the 6 digit
code and enter it here.</v-card-text
>
code and enter it here.
</v-card-text>
<v-otp-input
v-model="totp"
length="6"
:disabled="loading"
@keyup.enter="doLogin()"
@finish="doLogin()"
v-model="totp"
:disabled="loading"
></v-otp-input>
/>
</v-container>
</v-card>
</v-dialog>
@ -33,64 +33,60 @@
<span class="troplo-title">{{ $store.state.site.name }}</span>
</p>
<v-text-field
@keyup.enter="doLogin()"
class="rounded-xl"
v-model="instance"
v-if="isElectron()"
v-model="instance"
class="rounded-xl"
label="Instance URL"
placeholder="https://colubrina.troplo.com"
type="email"
></v-text-field>
<small style="float: right" v-if="isElectron()">{{
@keyup.enter="doLogin()"
/>
<small v-if="isElectron()" style="float: right">{{
instanceString
}}</small
><br v-if="isElectron()" />
<v-text-field
@keyup.enter="doLogin()"
class="rounded-xl"
v-model="customHeaders[header.name]"
v-for="header in $store.state.site.customHeaders"
:key="header.name"
v-model="customHeaders[header.name]"
class="rounded-xl"
:label="header.friendlyName"
:placeholder="header.placeholder"
type="email"
></v-text-field>
<v-text-field
@keyup.enter="doLogin()"
class="rounded-xl"
/>
<v-text-field
v-model="username"
class="rounded-xl"
label="Username"
placeholder="FOO1000"
type="email"
></v-text-field>
<v-text-field
@keyup.enter="doLogin()"
class="rounded-xl"
/>
<v-text-field
v-model="password"
class="rounded-xl"
color="blue accent-7"
label="Password"
type="password"
></v-text-field>
@keyup.enter="doLogin()"
/>
<p
style="float: right; color: #2196f3; cursor: pointer"
@click="doPasswordReset()"
>
Reset your Password
</p>
<v-switch
inset
label="Remember Me"
v-model="rememberMe"
></v-switch>
<v-switch v-model="rememberMe" inset label="Remember Me" />
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
v-if="$store.state.site.allowRegistrations"
class="rounded-xl"
:loading="loading"
color="primary"
text
@click="$router.push('/register')"
v-if="$store.state.site.allowRegistrations"
>
Register
</v-btn>
@ -133,18 +129,40 @@ export default {
customHeaders: {}
}
},
watch: {
instance() {
this.testInstance()
}
},
mounted() {
this.$store
.dispatch("getUserInfo")
.then(() => {
this.$router.push("/")
})
.catch(() => {
this.$store.state.user = {
bcUser: null,
loggedIn: false
}
})
this.testInstance()
},
methods: {
doPasswordReset() {
this.loading = true
this.axios.post("/api/v1/user/reset/send", {
email: this.username
}).then(() => {
this.loading = false
this.$toast.success("Password reset sent, check your email!")
}).catch((e) => {
this.loading = false
AjaxErrorHandler(this.$store)(e)
})
this.axios
.post("/api/v1/user/reset/send", {
email: this.username
})
.then(() => {
this.loading = false
this.$toast.success("Password reset sent, check your email!")
})
.catch((e) => {
this.loading = false
AjaxErrorHandler(this.$store)(e)
})
},
isElectron() {
return process.env.IS_ELECTRON
@ -223,25 +241,6 @@ export default {
})
}
}
},
mounted() {
this.$store
.dispatch("getUserInfo")
.then(() => {
this.$router.push("/")
})
.catch(() => {
this.$store.state.user = {
bcUser: null,
loggedIn: false
}
})
this.testInstance()
},
watch: {
instance() {
this.testInstance()
}
}
}
</script>

View File

@ -3,13 +3,13 @@
<v-container>
<v-card color="card" class="rounded-xl">
<v-toolbar color="toolbar">
<v-spacer></v-spacer>
<v-spacer />
<v-toolbar-title> Not Found </v-toolbar-title>
<v-spacer></v-spacer>
<v-spacer />
</v-toolbar>
<v-container class="text-center justify-center">
<h1>The page you were looking for couldn't be found.</h1>
<v-btn color="primary" text to="/">Home</v-btn>
<v-btn color="primary" text to="/"> Home </v-btn>
</v-container>
</v-card>
</v-container>

View File

@ -8,23 +8,23 @@
<v-card-text>
<v-form>
<v-text-field
@keyup.enter="doPasswordReset()"
class="rounded-xl"
v-model="password"
class="rounded-xl"
label="Password"
type="password"
></v-text-field>
<v-text-field
@keyup.enter="doPasswordReset()"
class="rounded-xl"
/>
<v-text-field
v-model="confirmPassword"
class="rounded-xl"
label="Confirm Password"
type="password"
></v-text-field>
@keyup.enter="doPasswordReset()"
/>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
class="rounded-xl"
:loading="loading"

View File

@ -1,5 +1,5 @@
<template>
<div id="login" v-if="!$store.state.user?.id">
<div v-if="!$store.state.user?.id" id="login">
<v-dialog v-model="rulesDialog" max-width="700px">
<v-card color="card">
<v-toolbar color="toolbar">
@ -8,7 +8,7 @@
</v-toolbar-title>
</v-toolbar>
<v-card-text class="mt-3" style="color: unset">
<span v-markdown :key="$store.state.site.rules">{{
<span :key="$store.state.site.rules" v-markdown>{{
$store.state.site.rules
}}</span>
</v-card-text>
@ -25,80 +25,80 @@
<span class="troplo-title">{{ $store.state.site.name }}</span>
</p>
<v-text-field
@keyup.enter="doRegister()"
class="rounded-xl"
v-model="instance"
v-if="isElectron()"
v-model="instance"
class="rounded-xl"
label="Instance URL"
placeholder="https://colubrina.troplo.com"
type="email"
></v-text-field>
<small style="float: right" v-if="isElectron()">{{
@keyup.enter="doRegister()"
/>
<small v-if="isElectron()" style="float: right">{{
instanceString
}}</small
><br v-if="isElectron()" />
<v-text-field
@keyup.enter="doRegister()"
class="rounded-xl"
v-model="username"
class="rounded-xl"
label="Username"
placeholder="FOO1000"
type="email"
></v-text-field>
<v-text-field
@keyup.enter="doRegister()"
class="rounded-xl"
/>
<v-text-field
v-model="email"
class="rounded-xl"
label="Email"
placeholder="troplo@troplo.com"
type="email"
></v-text-field>
<v-text-field
@keyup.enter="doRegister()"
class="rounded-xl"
/>
<v-text-field
v-model="password"
class="rounded-xl"
color="blue accent-7"
label="Password"
type="password"
></v-text-field>
@keyup.enter="doRegister()"
/>
<small v-if="$store.state.site.emailVerification"
>This instance has email verification enforced, ensure your
email is correct.</small
>
<v-row align="center">
<v-tooltip top v-if="!rulesOpenedOnce">
<template v-slot:activator="{ on }">
<v-tooltip v-if="!rulesOpenedOnce" top>
<template #activator="{ on }">
<div v-on="on">
<v-switch
v-model="rules"
class="ml-4 mt-5"
inset
v-model="rules"
:disabled="!rulesOpenedOnce"
></v-switch>
/>
</div>
</template>
<span>You need to view the rules first.</span>
</v-tooltip>
<v-switch
v-else
v-model="rules"
class="ml-4 mt-5"
inset
v-model="rules"
v-else
:disabled="!rulesOpenedOnce"
></v-switch>
/>
I have read and agree to the&nbsp;
<a
target="_blank"
@click="
rulesDialog = true
rulesOpenedOnce = true
"
target="_blank"
>
instance rules </a
>.
</v-row>
<v-card-actions>
<v-spacer></v-spacer>
<v-spacer />
<v-btn
class="rounded-xl"
:loading="loading"
@ -149,6 +149,25 @@ export default {
instanceString: ""
}
},
watch: {
instance() {
this.testInstance()
}
},
mounted() {
this.$store
.dispatch("getUserInfo")
.then(() => {
this.$router.push("/")
})
.catch(() => {
this.$store.state.user = {
bcUser: null,
loggedIn: false
}
})
this.testInstance()
},
methods: {
isElectron() {
return process.env.IS_ELECTRON
@ -229,25 +248,6 @@ export default {
}
})
}
},
mounted() {
this.$store
.dispatch("getUserInfo")
.then(() => {
this.$router.push("/")
})
.catch(() => {
this.$store.state.user = {
bcUser: null,
loggedIn: false
}
})
this.testInstance()
},
watch: {
instance() {
this.testInstance()
}
}
}
</script>

View File

@ -12,7 +12,7 @@
<v-tab to="/settings/security"> Security </v-tab>
<v-tab to="/settings/sessions"> Sessions </v-tab>
</v-tabs>
<router-view> </router-view>
<router-view />
<div class="mx-4">
<small
>Troplo/Colubrina version {{ $store.state.versioning.version }},

View File

@ -5,10 +5,10 @@
v-model="$store.state.user.theme"
true-value="dark"
false-value="light"
@change="saveSettings"
inset
label="Dark theme"
></v-switch>
@change="saveSettings"
/>
</v-card-text>
<v-alert
v-if="
@ -23,41 +23,41 @@
You currently have a theme enabled that is not designed for your selected
base theme.
</v-alert>
<v-card-text
><v-switch
inset
<v-card-text>
<v-switch
v-model="defineAccent"
inset
label="Use a custom accent color (overrides theme's primary attribute)."
></v-switch>
/>
<v-color-picker
v-if="defineAccent"
v-model="accent"
hide-canvas
value="hex"
hide-inputs
show-swatches
swatches-max-height="132"
v-model="accent"
></v-color-picker>
/>
</v-card-text>
<v-col sm="4">
<v-select
v-model="$store.state.user.font"
:items="fonts"
@change="setFont"
label="Font"
item-text="name"
item-value="name"
></v-select>
@change="setFont"
/>
</v-col>
<v-card-text>
<v-card
class="my-2"
@click="setTheme(theme)"
hover
outlined
v-for="(theme, index) in computeThemes"
:key="index"
class="my-2"
hover
outlined
color="card"
@click="setTheme(theme)"
>
<v-list-item>
<v-list-item-content>
@ -67,20 +67,20 @@
<v-icon>mdi-download</v-icon>
</v-btn>
<v-btn
v-if="theme.userId === $store.state.user.id"
text
fab
small
@click="initEditTheme(theme)"
v-if="theme.userId === $store.state.user.id"
>
<v-icon>mdi-pencil</v-icon>
</v-btn>
<v-btn
v-if="theme.userId === $store.state.user.id"
text
fab
small
@click="doDeleteTheme(theme)"
v-if="theme.userId === $store.state.user.id"
>
<v-icon>mdi-delete</v-icon>
</v-btn>
@ -95,44 +95,44 @@
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-avatar color="success" size="30" v-if="name === theme.id">
<v-avatar v-if="name === theme.id" color="success" size="30">
<v-icon>mdi-check</v-icon>
</v-avatar>
</v-list-item-action>
</v-list-item>
<div class="my-2" v-if="$vuetify.theme.dark">
<div v-if="$vuetify.theme.dark" class="my-2">
<v-chip-group>
<v-chip
v-for="(key, index) in Object.keys(theme.dark)"
:key="index"
disabled
style="opacity: 1"
class="mx-1"
label
:color="theme.dark[key]"
v-for="(key, index) in Object.keys(theme.dark)"
:key="index"
>
{{ friendlyName(key) }}</v-chip
>
{{ friendlyName(key) }}
</v-chip>
</v-chip-group>
</div>
<div class="my-2" v-if="!$vuetify.theme.dark">
<div v-if="!$vuetify.theme.dark" class="my-2">
<v-chip-group column>
<v-chip
v-for="(key, index) in Object.keys(theme.light)"
:key="index"
class="mx-1"
label
:color="theme.light[key]"
v-for="(key, index) in Object.keys(theme.light)"
:key="index"
>
{{ key }}</v-chip
>
{{ key }}
</v-chip>
</v-chip-group>
</div>
</v-card>
<v-container class="text-center justify-center">
<v-chip @click="initThemeCreator" x-large outlined fab
><v-icon x-large>mdi-plus</v-icon></v-chip
>
<v-chip x-large outlined fab @click="initThemeCreator">
<v-icon x-large> mdi-plus </v-icon>
</v-chip>
</v-container>
</v-card-text>
</div>
@ -229,6 +229,52 @@ export default {
}
}
},
watch: {
"$store.state.themeEngine.editor"() {
this.getThemes()
},
"$store.state.themeEngine.cssEditor"() {
this.getThemes()
},
"creator.css"() {
if (this.autoCSS) {
this.applyCSS(null)
}
},
creator() {
this.$vuetify.theme.themes.dark = this.creator.dark
this.$vuetify.theme.themes.light = this.creator.light
this.$vuetify.theme.themes.name = this.creator.id
},
accent() {
this.setTheme(
this.themes.find(
(theme) => theme.id === this.$vuetify.theme.themes.name
)
)
},
defineAccent() {
if (!this.defineAccent) {
this.accent = null
this.$store.state.user.accentColor = null
this.getThemes()
} else {
this.accent = this.$store.state.user.accentColor || "#0190ea"
}
},
"$store.state.user.theme": {
handler() {
this.$vuetify.theme.dark = this.$store.state.user.theme === "dark"
},
deep: true
}
},
mounted() {
this.defineAccent = this.$store.state.user.accentColor !== null
this.accent = this.$store.state.user.accentColor
this.name = this.$vuetify.theme.themes.name
this.getThemes()
},
methods: {
setFont() {
const element = document.getElementById("user-font")
@ -461,52 +507,6 @@ div {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.defineAccent = this.$store.state.user.accentColor !== null
this.accent = this.$store.state.user.accentColor
this.name = this.$vuetify.theme.themes.name
this.getThemes()
},
watch: {
"$store.state.themeEngine.editor"() {
this.getThemes()
},
"$store.state.themeEngine.cssEditor"() {
this.getThemes()
},
"creator.css"() {
if (this.autoCSS) {
this.applyCSS(null)
}
},
creator() {
this.$vuetify.theme.themes.dark = this.creator.dark
this.$vuetify.theme.themes.light = this.creator.light
this.$vuetify.theme.themes.name = this.creator.id
},
accent() {
this.setTheme(
this.themes.find(
(theme) => theme.id === this.$vuetify.theme.themes.name
)
)
},
defineAccent() {
if (!this.defineAccent) {
this.accent = null
this.$store.state.user.accentColor = null
this.getThemes()
} else {
this.accent = this.$store.state.user.accentColor || "#0190ea"
}
},
"$store.state.user.theme": {
handler() {
this.$vuetify.theme.dark = this.$store.state.user.theme === "dark"
},
deep: true
}
}
}
</script>

View File

@ -2,18 +2,16 @@
<div>
<v-card-text>
<v-switch
inset
v-model="messageAudio"
label="Play sound effect on new message"
>
</v-switch>
<v-switch
inset
label="Play sound effect on new message"
/>
<v-switch
v-model="nativeNotifications"
inset
label="Desktop notifications"
@change="setNativeNotifications"
>
</v-switch>
/>
<v-btn v-if="nativeNotifications" @click="testNativeNotification">
Test desktop notifications
</v-btn>
@ -30,6 +28,24 @@ export default {
nativeNotifications: false
}
},
watch: {
nativeNotifications(val) {
localStorage.setItem("nativeNotifications", val)
},
messageAudio(val) {
localStorage.setItem("messageAudio", val)
}
},
mounted() {
if (localStorage.getItem("messageAudio")) {
this.messageAudio = JSON.parse(localStorage.getItem("messageAudio"))
}
if (localStorage.getItem("nativeNotifications")) {
this.nativeNotifications = JSON.parse(
localStorage.getItem("nativeNotifications")
)
}
},
methods: {
setNativeNotifications() {
if (this.nativeNotifications) {
@ -68,24 +84,6 @@ export default {
"A desktop notification has been sent, if you don't see it, check your site permissions."
)
}
},
mounted() {
if (localStorage.getItem("messageAudio")) {
this.messageAudio = JSON.parse(localStorage.getItem("messageAudio"))
}
if (localStorage.getItem("nativeNotifications")) {
this.nativeNotifications = JSON.parse(
localStorage.getItem("nativeNotifications")
)
}
},
watch: {
nativeNotifications(val) {
localStorage.setItem("nativeNotifications", val)
},
messageAudio(val) {
localStorage.setItem("messageAudio", val)
}
}
}
</script>

View File

@ -13,15 +13,14 @@
accessed by anyone else, and that you don't loose it.
</p>
<v-text-field
v-model="totp.password"
type="password"
label="Password"
v-model="totp.password"
color="white"
@keydown.enter="totpEnable"
autocomplete="false"
>
</v-text-field>
<v-btn @click="totpEnable" text>Proceed</v-btn>
@keydown.enter="totpEnable"
/>
<v-btn text @click="totpEnable"> Proceed </v-btn>
</template>
<template v-else-if="totp.stage === 2">
<p class="text-h6">Enable 2 Factor Authentication</p>
@ -29,27 +28,25 @@
<code>{{ totp.secret }}</code>
<p>Please enter the 6 digit code from your authenticator app.</p>
<v-text-field
v-model="totp.code"
type="number"
label="Code"
v-model="totp.code"
@keydown.enter="totpConfirm"
color="white"
>
</v-text-field>
<v-btn @click="totpConfirm" text>Enable</v-btn>
@keydown.enter="totpConfirm"
/>
<v-btn text @click="totpConfirm"> Enable </v-btn>
</template>
<template v-else-if="totp.stage === 3">
<p class="text-h6">2 Factor Authentication Enabled</p>
<p>You have successfully enabled 2 Factor Authentication.</p>
<v-text-field
v-model="totp.code"
type="password"
label="2FA Code"
v-model="totp.code"
color="white"
@keydown.enter="totpDisable"
>
</v-text-field>
<v-btn @click="totpDisable" text>Disable</v-btn>
/>
<v-btn text @click="totpDisable"> Disable </v-btn>
</template>
</v-alert>
<v-alert>
@ -58,15 +55,15 @@
<p class="text-h6">Change Password</p>
<p>You may set a custom password here.</p>
<v-text-field
label="Current Password"
v-model="password.current"
label="Current Password"
type="password"
color="white"
@keydown.enter="passwordChange"
></v-text-field>
/>
<v-text-field
label="New Password"
v-model="password.new"
label="New Password"
type="password"
color="white"
:rules="[
@ -75,10 +72,10 @@
v.length >= 8 || 'Password must be at least 8 characters.'
]"
@keydown.enter="passwordChange"
></v-text-field>
/>
<v-text-field
label="Confirm New Password"
v-model="password.confirm"
label="Confirm New Password"
type="password"
color="white"
:rules="[
@ -87,8 +84,8 @@
v.length >= 8 || 'Password must be at least 8 characters.'
]"
@keydown.enter="passwordChange"
></v-text-field>
<v-btn @click="passwordChange" text>Change</v-btn>
/>
<v-btn text @click="passwordChange"> Change </v-btn>
</v-col>
</v-row>
</v-alert>
@ -121,6 +118,11 @@ export default {
}
}
},
mounted() {
this.$store.state.user.totpEnabled
? (this.totp.stage = 3)
: (this.totp.stage = 1)
},
methods: {
passwordChange() {
if (this.password.new === this.password.confirm) {
@ -188,11 +190,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.$store.state.user.totpEnabled
? (this.totp.stage = 3)
: (this.totp.stage = 1)
}
}
</script>

View File

@ -2,22 +2,22 @@
<div id="settings-sessions">
<v-container fluid>
<v-row>
<v-col md="3" v-for="session in sessions" :key="session.id">
<v-col v-for="session in sessions" :key="session.id" md="3">
<v-card class="mb-2 rounded-xl" color="card">
<v-toolbar color="toolbar">
<v-toolbar-title>{{
session.other.osString || "Unknown Session"
}}</v-toolbar-title>
<v-toolbar-title>
{{ session.other.osString || "Unknown Session" }}
</v-toolbar-title>
</v-toolbar>
<v-container>
<v-card-text>
Browser: {{ session.other.browserString }}
</v-card-text>
<v-card-text
>Login IP address: {{ session.other.ip }}
<v-card-text>
Login IP address: {{ session.other.ip }}
</v-card-text>
<v-card-text
>Operating System: {{ session.other.osString }}
<v-card-text>
Operating System: {{ session.other.osString }}
</v-card-text>
<v-card-text>
Time of login:
@ -30,7 +30,7 @@
<v-card-text> Country: {{ session.other.location }} </v-card-text>
<v-card-text> ISP: {{ session.other.isp }} </v-card-text>
<v-card-actions>
<v-btn @click="deleteSession(session.id)" color="error" text>
<v-btn color="error" text @click="deleteSession(session.id)">
Invalidate
</v-btn>
</v-card-actions>
@ -52,6 +52,9 @@ export default {
sessions: []
}
},
mounted() {
this.getSessions()
},
methods: {
deleteSession(id) {
this.axios
@ -74,9 +77,6 @@ export default {
AjaxErrorHandler(this.$store)(e)
})
}
},
mounted() {
this.getSessions()
}
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<div id="settings-site" v-if="$store.state.user?.id">
<div v-if="$store.state.user?.id" id="settings-site">
<v-card-text>
<div class="d-flex">
<v-btn
@ -22,16 +22,16 @@
>
<v-fade-transition v-if="hover">
<v-overlay absolute>
<v-icon large>mdi-upload</v-icon>
<v-icon large> mdi-upload </v-icon>
</v-overlay>
</v-fade-transition>
<v-img
v-if="$store.state.user.avatar"
:src="
$store.state.baseURL +
'/usercontent/' +
$store.state.user.avatar
"
v-if="$store.state.user.avatar"
class="elevation-1"
/>
<v-icon v-else-if="!hover" class="elevation-1">
@ -40,15 +40,15 @@
</v-avatar>
</v-hover>
<v-file-input
class="ml-3"
ref="avatarUpload"
v-model="avatar.file"
class="ml-3"
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
placeholder="Avatar"
prepend-icon=""
label="Profile Picture"
v-model="avatar.file"
@change="doUpload"
></v-file-input>
/>
</div>
<v-text-field
v-model="$store.state.user.email"
@ -57,7 +57,7 @@
(v) => !!v || 'Email is required',
(v) => /^.+@.+\..+$/.test(v) || 'Email must be valid'
]"
></v-text-field>
/>
<v-text-field
v-model="$store.state.user.username"
label="Username"
@ -67,17 +67,17 @@
(v) => /^[a-zA-Z0-9]+$/.test(v) || 'Username must be alphanumeric',
(v) => v.length >= 2 || 'Username must be at least 2 characters'
]"
></v-text-field>
/>
</v-card-text>
<v-card-actions>
<v-btn
text
color="primary"
:disabled="!$store.state.user.email || !$store.state.user.username"
@click="
$store.dispatch('saveOnlineSettings')
$toast.success('Updated successfully.')
"
:disabled="!$store.state.user.email || !$store.state.user.username"
>
Save
</v-btn>
@ -88,8 +88,9 @@
$store.dispatch('logout')
$router.push('/login')
"
>Logout</v-btn
>
Logout
</v-btn>
</v-card-actions>
</div>
</template>

File diff suppressed because it is too large Load Diff