diff --git a/assets/js/hooks/Mapper/index.tsx b/assets/js/hooks/Mapper/index.tsx index c03010ee..0a3adf2e 100755 --- a/assets/js/hooks/Mapper/index.tsx +++ b/assets/js/hooks/Mapper/index.tsx @@ -1,6 +1,5 @@ import { createRoot } from 'react-dom/client'; import Mapper from './MapRoot'; -import { decompressToJson } from './utils'; export default { _rootEl: null, @@ -28,17 +27,12 @@ export default { handleEventWrapper(event: string, handler: (payload: any) => void) { this.handleEvent(event, (body: any) => { - if (event === 'map_event') { - const { type, body: data } = body; - handler({ type, body: decompressToJson(data) }); - } else { - handler(body); - } + handler(body); }); }, reconnected() { - this.pushEvent('reconnected'); + this.pushEvent('ui_loaded'); }, async pushEventAsync(event: string, payload: any) { diff --git a/assets/js/hooks/Mapper/utils/decompressToJson.ts b/assets/js/hooks/Mapper/utils/decompressToJson.ts deleted file mode 100644 index 6484ddfb..00000000 --- a/assets/js/hooks/Mapper/utils/decompressToJson.ts +++ /dev/null @@ -1,14 +0,0 @@ -import pako from 'pako'; - -export const decompressToJson = (base64string: string) => { - const base64_decoded = atob(base64string); - const charData = base64_decoded.split('').map(function (x) { - return x.charCodeAt(0); - }); - const zlibData = new Uint8Array(charData); - const inflatedData = pako.inflate(zlibData, { - to: 'string', - }); - - return JSON.parse(inflatedData); -}; diff --git a/assets/js/hooks/Mapper/utils/index.ts b/assets/js/hooks/Mapper/utils/index.ts index 0b07e983..5a9c58bd 100644 --- a/assets/js/hooks/Mapper/utils/index.ts +++ b/assets/js/hooks/Mapper/utils/index.ts @@ -1,3 +1,2 @@ export * from './contextStore'; -export * from './decompressToJson'; export * from './getQueryVariable'; diff --git a/assets/package.json b/assets/package.json index 6cf9a9f3..9d0adb24 100644 --- a/assets/package.json +++ b/assets/package.json @@ -21,7 +21,6 @@ "live_select": "file:../deps/live_select", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", - "pako": "^2.1.0", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view", @@ -44,7 +43,6 @@ "@tailwindcss/typography": "^0.5.13", "@types/lodash.debounce": "^4.0.9", "@types/lodash.isequal": "^4.5.8", - "@types/pako": "^2.0.3", "@types/react": "18.2.0", "@types/react-dom": "18.2.1", "@types/react-grid-layout": "^1.3.4", diff --git a/assets/yarn.lock b/assets/yarn.lock index ba1ee5ea..f0849760 100644 --- a/assets/yarn.lock +++ b/assets/yarn.lock @@ -33,7 +33,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz" integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.14.8", "@babel/core@^7.24.5": +"@babel/core@^7.14.8", "@babel/core@^7.24.5": version "7.24.5" resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz" integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== @@ -240,11 +240,121 @@ "@babel/helper-validator-identifier" "^7.24.5" to-fast-properties "^2.0.0" +"@esbuild/aix-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" + integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== + +"@esbuild/android-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" + integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== + +"@esbuild/android-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" + integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== + +"@esbuild/android-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" + integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== + "@esbuild/darwin-arm64@0.20.2": version "0.20.2" resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz" integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== +"@esbuild/darwin-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" + integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== + +"@esbuild/freebsd-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" + integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== + +"@esbuild/freebsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" + integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== + +"@esbuild/linux-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" + integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== + +"@esbuild/linux-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" + integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== + +"@esbuild/linux-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" + integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== + +"@esbuild/linux-loong64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" + integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== + +"@esbuild/linux-mips64el@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" + integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== + +"@esbuild/linux-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" + integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== + +"@esbuild/linux-riscv64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" + integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== + +"@esbuild/linux-s390x@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" + integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== + +"@esbuild/linux-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" + integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== + +"@esbuild/netbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" + integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== + +"@esbuild/openbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" + integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== + +"@esbuild/sunos-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" + integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== + +"@esbuild/win32-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" + integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== + +"@esbuild/win32-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" + integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== + +"@esbuild/win32-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" + integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -341,7 +451,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -359,7 +469,7 @@ resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@react-rxjs/core@^0.10.7", "@react-rxjs/core@>=0.1.0": +"@react-rxjs/core@^0.10.7": version "0.10.7" resolved "https://registry.npmjs.org/@react-rxjs/core/-/core-0.10.7.tgz" integrity sha512-dornp8pUs9OcdqFKKRh9+I2FVe21gWufNun6RYU1ddts7kUy9i4Thvl0iqcPFbGY61cJQMAJF7dxixWMSD/A/A== @@ -455,11 +565,91 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@rollup/rollup-android-arm-eabi@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz#1a32112822660ee104c5dd3a7c595e26100d4c2d" + integrity sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ== + +"@rollup/rollup-android-arm64@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz#5aeef206d65ff4db423f3a93f71af91b28662c5b" + integrity sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw== + "@rollup/rollup-darwin-arm64@4.17.2": version "4.17.2" resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz" integrity sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw== +"@rollup/rollup-darwin-x64@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz#f64fc51ed12b19f883131ccbcea59fc68cbd6c0b" + integrity sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz#1a7641111be67c10111f7122d1e375d1226cbf14" + integrity sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A== + +"@rollup/rollup-linux-arm-musleabihf@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz#c93fd632923e0fee25aacd2ae414288d0b7455bb" + integrity sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg== + +"@rollup/rollup-linux-arm64-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz#fa531425dd21d058a630947527b4612d9d0b4a4a" + integrity sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A== + +"@rollup/rollup-linux-arm64-musl@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz#8acc16f095ceea5854caf7b07e73f7d1802ac5af" + integrity sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA== + +"@rollup/rollup-linux-powerpc64le-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz#94e69a8499b5cf368911b83a44bb230782aeb571" + integrity sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ== + +"@rollup/rollup-linux-riscv64-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz#7ef1c781c7e59e85a6ce261cc95d7f1e0b56db0f" + integrity sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg== + +"@rollup/rollup-linux-s390x-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz#f15775841c3232fca9b78cd25a7a0512c694b354" + integrity sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g== + +"@rollup/rollup-linux-x64-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz#b521d271798d037ad70c9f85dd97d25f8a52e811" + integrity sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ== + +"@rollup/rollup-linux-x64-gnu@4.9.5": + version "4.9.5" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz#85946ee4d068bd12197aeeec2c6f679c94978a49" + integrity sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA== + +"@rollup/rollup-linux-x64-musl@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz#9254019cc4baac35800991315d133cc9fd1bf385" + integrity sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q== + +"@rollup/rollup-win32-arm64-msvc@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz#27f65a89f6f52ee9426ec11e3571038e4671790f" + integrity sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA== + +"@rollup/rollup-win32-ia32-msvc@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz#a2fbf8246ed0bb014f078ca34ae6b377a90cb411" + integrity sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ== + +"@rollup/rollup-win32-x64-msvc@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503" + integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w== + "@rx-state/core@0.1.4": version "0.1.4" resolved "https://registry.npmjs.org/@rx-state/core/-/core-0.1.4.tgz" @@ -740,7 +930,7 @@ "@types/d3-transition" "*" "@types/d3-zoom" "*" -"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@1.0.5": +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": version "1.0.5" resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -774,11 +964,6 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz" integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== -"@types/pako@^2.0.3": - version "2.0.3" - resolved "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz" - integrity sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q== - "@types/prop-types@*": version "15.7.11" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz" @@ -805,7 +990,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@>=16.8", "@types/react@18.2.0": +"@types/react@*", "@types/react@18.2.0": version "18.2.0" resolved "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz" integrity sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA== @@ -846,7 +1031,7 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.21.0": +"@typescript-eslint/parser@^6.21.0": version "6.21.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz" integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== @@ -947,7 +1132,7 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.4.0, acorn@^8.9.0: +acorn@^8.4.0, acorn@^8.9.0: version "8.11.3" resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -1147,7 +1332,7 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4.21.0": +browserslist@^4.22.2, browserslist@^4.23.0: version "4.23.0" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz" integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== @@ -1205,7 +1390,7 @@ child_process@^1.0.2: resolved "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz" integrity sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g== -chokidar@^3.3.0, chokidar@^3.5.3, "chokidar@>=3.0.0 <4.0.0": +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -1258,16 +1443,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + commander@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" @@ -1325,7 +1510,7 @@ culori@^3: resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz" integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== -d3-drag@^3.0.0, "d3-drag@2 - 3": +"d3-drag@2 - 3", d3-drag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz" integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== @@ -1345,7 +1530,7 @@ d3-drag@^3.0.0, "d3-drag@2 - 3": dependencies: d3-color "1 - 3" -d3-selection@^3.0.0, "d3-selection@2 - 3", d3-selection@3: +"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz" integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== @@ -1663,7 +1848,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-prettier@*, eslint-config-prettier@^9.1.0: +eslint-config-prettier@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz" integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== @@ -1723,7 +1908,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -"eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.57.0, eslint@>=7, eslint@>=7.0.0, eslint@>=8.0.0: +eslint@^8.57.0: version "8.57.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -1795,12 +1980,7 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-walker@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== - -estree-walker@^2.0.2: +estree-walker@^2.0.1, estree-walker@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== @@ -2010,18 +2190,6 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" @@ -2034,6 +2202,18 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" @@ -2141,14 +2321,7 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" -hasown@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hasown@^2.0.2: +hasown@^2.0.1, hasown@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -2420,7 +2593,7 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" -jiti@^1.19.1, jiti@>=1.21.0: +jiti@^1.19.1: version "1.21.0" resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== @@ -2518,7 +2691,6 @@ lines-and-columns@^1.1.6: "live_select@file:../deps/live_select": version "1.4.2" - resolved "file:../deps/live_select" locate-path@^6.0.0: version "6.0.0" @@ -2612,13 +2784,6 @@ mini-svg-data-uri@^1.2.3: resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz" integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - minimatch@9.0.3: version "9.0.3" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" @@ -2626,6 +2791,13 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -2770,11 +2942,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -pako@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" - integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -2812,17 +2979,14 @@ path-type@^5.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz" integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== +"phoenix@file:../deps/phoenix": + version "1.7.14" + "phoenix_html@file:../deps/phoenix_html": version "4.1.0" - resolved "file:../deps/phoenix_html" "phoenix_live_view@file:../deps/phoenix_live_view": version "0.20.17" - resolved "file:../deps/phoenix_live_view" - -"phoenix@file:../deps/phoenix": - version "1.7.14" - resolved "file:../deps/phoenix" picocolors@^1, picocolors@^1.0.0: version "1.0.0" @@ -2923,14 +3087,6 @@ postcss-reporter@^7.0.0: picocolors "^1.0.0" thenby "^1.3.4" -postcss-selector-parser@^6.0.11: - version "6.0.13" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" - integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - postcss-selector-parser@6.0.10: version "6.0.10" resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz" @@ -2939,12 +3095,20 @@ postcss-selector-parser@6.0.10: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.11: + version "6.0.13" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.38, postcss@>=8.0.9: +postcss@^8.4.23, postcss@^8.4.38: version "8.4.38" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz" integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== @@ -2965,7 +3129,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.2.5, prettier@>=3.0.0: +prettier@^3.2.5: version "3.2.5" resolved "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz" integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== @@ -2993,7 +3157,7 @@ primereact@^10.6.5: "@types/react-transition-group" "^4.4.1" react-transition-group "^4.4.1" -prop-types@^15.6.2, prop-types@^15.8.1, prop-types@15.x: +prop-types@15.x, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -3012,7 +3176,7 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -"react-dom@^17.0.0 || ^18.0.0", "react-dom@>= 16.3.0", react-dom@>=16.6.0, react-dom@>=17, react-dom@>=18, "react-dom@16 || 17 || 18", react-dom@18.2.0: +react-dom@18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -3099,7 +3263,7 @@ react-usestateref@^1.0.9: resolved "https://registry.npmjs.org/react-usestateref/-/react-usestateref-1.0.9.tgz" integrity sha512-t8KLsI7oje0HzfzGhxFXzuwbf1z9vhBM1ptHLUIHhYqZDKFuI5tzdhEVxSNzUkYxwF8XdpOErzHlKxvP7sTERw== -"react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.2.0, "react@>= 16.3", "react@>= 16.3.0", react@>=16.13.1, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=17, react@>=18, react@>16.0.0, "react@16 || 17 || 18", react@18.2.0: +react@18.2.0: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== @@ -3215,7 +3379,7 @@ rollup-plugin-external-globals@^0.10.0: is-reference "^3.0.2" magic-string "^0.30.5" -rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0, "rollup@^2.25.0 || ^3.3.0 || ^4.1.4", rollup@^4.13.0: +rollup@^4.13.0: version "4.17.2" resolved "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz" integrity sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ== @@ -3247,7 +3411,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.8.1, rxjs@>=6, rxjs@>=7: +rxjs@^7.8.1: version "7.8.1" resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -3280,7 +3444,7 @@ sass-loader@^14.2.1: dependencies: neo-async "^2.6.2" -sass@*, sass@^1.3.0, sass@^1.77.2: +sass@^1.77.2: version "1.77.2" resolved "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz" integrity sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA== @@ -3362,7 +3526,7 @@ slash@^5.0.0, slash@^5.1.0: resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz" integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== -source-map-js@^1.2.0, "source-map-js@>=0.6.2 <2.0.0": +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== @@ -3479,7 +3643,7 @@ synckit@^0.8.6: "@pkgr/core" "^0.1.0" tslib "^2.6.2" -tailwindcss@^3.3.6, "tailwindcss@>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1", "tailwindcss@>=3.0.0 || >= 3.0.0-alpha.1", "tailwindcss@>=3.0.0 || insiders": +tailwindcss@^3.3.6: version "3.3.6" resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.6.tgz" integrity sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw== @@ -3619,7 +3783,7 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" -typescript@^5.2.2, typescript@>=4.2.0: +typescript@^5.2.2: version "5.4.5" resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz" integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== @@ -3664,7 +3828,7 @@ use-local-storage-state@^19.3.1: resolved "https://registry.npmjs.org/use-local-storage-state/-/use-local-storage-state-19.3.1.tgz" integrity sha512-y3Z1dODXvZXZB4qtLDNN8iuXbsYD6TAxz61K58GWB9/yKwrNG9ynI0GzCTHi/Je1rMiyOwMimz0oyFsZn+Kj7Q== -use-sync-external-store@^1.0.0, use-sync-external-store@1.2.0: +use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0: version "1.2.0" resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -3692,7 +3856,7 @@ vite-plugin-externals@^0.6.2: fs-extra "^10.0.0" magic-string "^0.25.7" -"vite@^4.2.0 || ^5.0.0", vite@^5.0.5, vite@>=2.0.0: +vite@^5.0.5: version "5.2.11" resolved "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz" integrity sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ== diff --git a/config/dev.exs b/config/dev.exs index 80cd0756..b96b821b 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -55,7 +55,6 @@ config :wanderer_app, WandererAppWeb.Endpoint, config :wanderer_app, WandererAppWeb.Endpoint, live_reload: [ interval: 1000, - web_console_logger: true, patterns: [ ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", ~r"priv/gettext/.*(po)$", diff --git a/lib/wanderer_app/api/calculations/calc_map_permissions.ex b/lib/wanderer_app/api/calculations/calc_map_permissions.ex index 10092438..9dbb371e 100644 --- a/lib/wanderer_app/api/calculations/calc_map_permissions.ex +++ b/lib/wanderer_app/api/calculations/calc_map_permissions.ex @@ -30,80 +30,89 @@ defmodule WandererApp.Api.Calculations.CalcMapPermissions do result = record.acls - |> Enum.filter(fn acl -> - acl.owner_id in character_ids or - acl.members |> Enum.any?(fn member -> member.eve_character_id in character_eve_ids end) or + |> Enum.reduce([0, 0], fn acl, acc -> + is_owner? = acl.owner_id in character_ids + + is_character_member? = + acl.members |> Enum.any?(fn member -> member.eve_character_id in character_eve_ids end) + + is_corporation_member? = acl.members - |> Enum.any?(fn member -> member.eve_corporation_id in character_corporation_ids end) or + |> Enum.any?(fn member -> member.eve_corporation_id in character_corporation_ids end) + + is_alliance_member? = acl.members |> Enum.any?(fn member -> member.eve_alliance_id in character_alliance_ids end) - end) - |> Enum.reduce([0, 0], fn acl, acc -> - case acc do - [_, -1] -> - [-1, -1] - [-1, char_acc] -> - char_acl_mask = - acl.members - |> Enum.filter(fn member -> - member.eve_character_id in character_eve_ids - end) - |> Enum.reduce(0, fn member, acc -> - case acc do + if is_owner? || is_character_member? || is_corporation_member? || is_alliance_member? do + case acc do + [_, -1] -> + [-1, -1] + + [-1, char_acc] -> + char_acl_mask = + acl.members + |> Enum.filter(fn member -> + member.eve_character_id in character_eve_ids + end) + |> Enum.reduce(0, fn member, acc -> + case acc do + -1 -> -1 + _ -> WandererApp.Permissions.calc_role_mask(member.role, acc) + end + end) + + char_acc = + case char_acl_mask do -1 -> -1 - _ -> WandererApp.Permissions.calc_role_mask(member.role, acc) + _ -> char_acc ||| char_acl_mask end - end) - char_acc = - case char_acl_mask do - -1 -> -1 - _ -> char_acc ||| char_acl_mask - end + [-1, char_acc] - [-1, char_acc] + [any_acc, char_acc] -> + any_acl_mask = + acl.members + |> Enum.filter(fn member -> + member.eve_character_id in character_eve_ids || + member.eve_corporation_id in character_corporation_ids || + member.eve_alliance_id in character_alliance_ids + end) + |> Enum.reduce(0, fn member, acc -> + case acc do + -1 -> -1 + _ -> WandererApp.Permissions.calc_role_mask(member.role, acc) + end + end) - [any_acc, char_acc] -> - any_acl_mask = - acl.members - |> Enum.filter(fn member -> - member.eve_character_id in character_eve_ids or - member.eve_corporation_id in character_corporation_ids or - member.eve_alliance_id in character_alliance_ids - end) - |> Enum.reduce(0, fn member, acc -> - case acc do + char_acl_mask = + acl.members + |> Enum.filter(fn member -> + member.eve_character_id in character_eve_ids + end) + |> Enum.reduce(0, fn member, acc -> + case acc do + -1 -> -1 + _ -> WandererApp.Permissions.calc_role_mask(member.role, acc) + end + end) + + any_acc = + case any_acl_mask do -1 -> -1 - _ -> WandererApp.Permissions.calc_role_mask(member.role, acc) + _ -> any_acc ||| any_acl_mask end - end) - char_acl_mask = - acl.members - |> Enum.filter(fn member -> - member.eve_character_id in character_eve_ids - end) - |> Enum.reduce(0, fn member, acc -> - case acc do + char_acc = + case char_acl_mask do -1 -> -1 - _ -> WandererApp.Permissions.calc_role_mask(member.role, acc) + _ -> char_acc ||| char_acl_mask end - end) - any_acc = - case any_acl_mask do - -1 -> -1 - _ -> any_acc ||| any_acl_mask - end - - char_acc = - case char_acl_mask do - -1 -> -1 - _ -> char_acc ||| char_acl_mask - end - - [any_acc, char_acc] + [any_acc, char_acc] + end + else + acc end end) diff --git a/lib/wanderer_app/api/map_connection.ex b/lib/wanderer_app/api/map_connection.ex index 1c0d096c..a562f2a5 100644 --- a/lib/wanderer_app/api/map_connection.ex +++ b/lib/wanderer_app/api/map_connection.ex @@ -48,7 +48,13 @@ defmodule WandererApp.Api.MapConnection do argument(:map_id, :string, allow_nil?: false) argument(:solar_system_source, :integer, allow_nil?: false) argument(:solar_system_target, :integer, allow_nil?: false) - filter(expr(map_id == ^arg(:map_id) and solar_system_source == ^arg(:solar_system_source) and solar_system_target == ^arg(:solar_system_target))) + + filter( + expr( + map_id == ^arg(:map_id) and solar_system_source == ^arg(:solar_system_source) and + solar_system_target == ^arg(:solar_system_target) + ) + ) end read :get_link_pairs_advanced do diff --git a/lib/wanderer_app/application.ex b/lib/wanderer_app/application.ex index 9d44a147..3e4ed015 100644 --- a/lib/wanderer_app/application.ex +++ b/lib/wanderer_app/application.ex @@ -38,8 +38,6 @@ defmodule WandererApp.Application do WandererApp.Character.TrackerManager, WandererApp.Map.Manager, WandererApp.Map.ZkbDataFetcher, - WandererApp.Character.ActivityTracker, - WandererApp.User.ActivityTracker, WandererAppWeb.Presence, WandererAppWeb.Endpoint ] ++ maybe_start_corp_wallet_tracker(WandererApp.Env.map_subscriptions_enabled?()) diff --git a/lib/wanderer_app/character.ex b/lib/wanderer_app/character.ex index 55547542..3165cea0 100644 --- a/lib/wanderer_app/character.ex +++ b/lib/wanderer_app/character.ex @@ -71,11 +71,24 @@ defmodule WandererApp.Character do end end + def get_character_state!(character_id) do + case get_character_state(character_id) do + {:ok, character_state} -> + character_state + + _ -> + Logger.error("Failed to get character_state #{character_id}") + throw("Failed to get character_state #{character_id}") + end + end + def update_character_state(character_id, character_state_update) do Cachex.get_and_update(:character_state_cache, character_id, fn character_state -> case character_state do nil -> new_state = WandererApp.Character.Tracker.init(character_id: character_id) + :telemetry.execute([:wanderer_app, :character, :tracker, :started], %{count: 1}) + {:commit, Map.merge(new_state, character_state_update)} _ -> diff --git a/lib/wanderer_app/character/activity_tracker.ex b/lib/wanderer_app/character/activity_tracker.ex deleted file mode 100644 index b642c641..00000000 --- a/lib/wanderer_app/character/activity_tracker.ex +++ /dev/null @@ -1,60 +0,0 @@ -defmodule WandererApp.Character.ActivityTracker do - @moduledoc false - use GenServer - - require Logger - - @name __MODULE__ - - def start_link(args) do - GenServer.start(__MODULE__, args, name: @name) - end - - @impl true - def init(_args) do - Logger.info("#{__MODULE__} started") - - {:ok, %{}, {:continue, :start}} - end - - @impl true - def handle_continue(:start, state) do - :telemetry.attach_many( - "map_character_activity_handler", - [ - [:wanderer_app, :map, :character, :jump] - ], - &WandererApp.Character.ActivityTracker.handle_event/4, - nil - ) - - {:noreply, state} - end - - @impl true - def terminate(_reason, _state) do - :ok - end - - def handle_event( - [:wanderer_app, :map, :character, :jump], - _event_data, - %{ - character: character, - map_id: map_id, - solar_system_source_id: solar_system_source_id, - solar_system_target_id: solar_system_target_id - } = _metadata, - _config - ) do - {:ok, _} = - WandererApp.Api.MapChainPassages.new(%{ - map_id: map_id, - character_id: character.id, - ship_type_id: character.ship, - ship_name: character.ship_name, - solar_system_source_id: solar_system_source_id, - solar_system_target_id: solar_system_target_id - }) - end -end diff --git a/lib/wanderer_app/character/tracker.ex b/lib/wanderer_app/character/tracker.ex index 0c35e226..5959028a 100644 --- a/lib/wanderer_app/character/tracker.ex +++ b/lib/wanderer_app/character/tracker.ex @@ -35,6 +35,7 @@ defmodule WandererApp.Character.Tracker do @online_error_timeout :timer.minutes(2) @forbidden_ttl :timer.minutes(1) + @pubsub_client Application.compile_env(:wanderer_app, :pubsub_client) def new(), do: __struct__() def new(args), do: __struct__(args) @@ -53,69 +54,55 @@ defmodule WandererApp.Character.Tracker do {:ok, character_state - |> _maybe_update_active_maps(track_settings) - |> _maybe_stop_tracking(track_settings) - |> _maybe_start_online_tracking(track_settings) - |> _maybe_start_location_tracking(track_settings) - |> _maybe_start_ship_tracking(track_settings)} + |> maybe_update_active_maps(track_settings) + |> maybe_stop_tracking(track_settings) + |> maybe_start_online_tracking(track_settings) + |> maybe_start_location_tracking(track_settings) + |> maybe_start_ship_tracking(track_settings)} end def update_info(character_id) do - {:ok, character_state} = WandererApp.Character.get_character_state(character_id) - _update_info(character_state) - end + WandererApp.Cache.has_key?("character:#{character_id}:info_forbidden") + |> case do + true -> + {:error, :skipped} - def update_ship(character_id) do - {:ok, character_state} = WandererApp.Character.get_character_state(character_id) - _update_ship(character_state) - end + false -> + {:ok, %{eve_id: eve_id}} = WandererApp.Character.get_character(character_id) - def update_location(character_id) do - {:ok, character_state} = WandererApp.Character.get_character_state(character_id) - _update_location(character_state) - end + case WandererApp.Esi.get_character_info(eve_id) do + {:ok, info} -> + {:ok, character_state} = WandererApp.Character.get_character_state(character_id) + update = maybe_update_corporation(character_state, info) + WandererApp.Character.update_character_state(character_id, update) - def update_online(character_id) do - {:ok, character_state} = WandererApp.Character.get_character_state(character_id) - _update_online(character_state) - end + :ok - def check_online_errors(character_id) do - case(WandererApp.Cache.lookup!("character:#{character_id}:online_error_time")) do - nil -> - :skip + {:error, :forbidden} -> + Logger.warning("#{__MODULE__} failed to get_character_info: forbidden") - error_time -> - duration = DateTime.diff(DateTime.utc_now(), error_time, :second) + WandererApp.Cache.put( + "character:#{character_id}:info_forbidden", + true, + ttl: @forbidden_ttl + ) - if duration >= @online_error_timeout do - {:ok, character_state} = WandererApp.Character.get_character_state(character_id) - WandererApp.Cache.delete("character:#{character_id}:online_forbidden") - WandererApp.Cache.delete("character:#{character_id}:online_error_time") - WandererApp.Character.update_character(character_id, %{online: false}) - WandererApp.Cache.delete("character:#{character_id}:location_started") - WandererApp.Cache.delete("character:#{character_id}:start_solar_system_id") + {:error, :forbidden} - WandererApp.Character.update_character_state(character_id, %{ - character_state - | is_online: false, - track_ship: false, - track_location: false - }) - - :ok - else - :skip + {:error, error} -> + Logger.error("#{__MODULE__} failed to get_character_info: #{inspect(error)}") + {:error, error} end end end - def update_wallet(character_id) do - {:ok, character_state} = WandererApp.Character.get_character_state(character_id) - _update_wallet(character_state) + def update_ship(character_id) when is_binary(character_id) do + character_id + |> WandererApp.Character.get_character_state!() + |> update_ship() end - defp _update_ship(%{character_id: character_id, track_ship: true} = character_state) do + def update_ship(%{character_id: character_id, track_ship: true} = character_state) do case WandererApp.Character.get_character(character_id) do {:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) -> WandererApp.Cache.has_key?("character:#{character_id}:ship_forbidden") @@ -123,14 +110,14 @@ defmodule WandererApp.Character.Tracker do true -> {:error, :skipped} - false -> + _ -> case WandererApp.Esi.get_character_ship(eve_id, access_token: access_token, character_id: character_id, refresh_token?: true ) do {:ok, ship} -> - character_state |> _maybe_update_ship(ship) + character_state |> maybe_update_ship(ship) :ok @@ -156,9 +143,68 @@ defmodule WandererApp.Character.Tracker do end end - defp _update_ship(_), do: {:error, :skipped} + def update_ship(_), do: {:error, :skipped} - defp _update_online(%{track_online: true, character_id: character_id} = character_state) do + def update_location(character_id) when is_binary(character_id) do + character_id + |> WandererApp.Character.get_character_state!() + |> update_location() + end + + def update_location(%{track_location: true, character_id: character_id} = character_state) do + case WandererApp.Character.get_character(character_id) do + {:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) -> + WandererApp.Cache.has_key?("character:#{character_id}:location_forbidden") + |> case do + true -> + {:error, :skipped} + + _ -> + case WandererApp.Esi.get_character_location(eve_id, + access_token: access_token, + character_id: character_id, + refresh_token?: true + ) do + {:ok, location} -> + character_state + |> maybe_update_location(location) + + :ok + + {:error, :forbidden} -> + Logger.warning("#{__MODULE__} failed to update_location: forbidden") + + WandererApp.Cache.put( + "character:#{character_id}:location_forbidden", + true, + ttl: @forbidden_ttl + ) + + {:error, :forbidden} + + {:error, error} -> + Logger.error("#{__MODULE__} failed to update_location: #{inspect(error)}") + {:error, error} + end + + _ -> + {:error, :skipped} + end + + _ -> + {:error, :skipped} + end + end + + def update_location(_), do: {:error, :skipped} + + def update_online(character_id) when is_binary(character_id) do + character_id + |> WandererApp.Character.get_character_state!() + |> update_online() + end + + def update_online(%{track_online: true, character_id: character_id} = character_state) do case WandererApp.Character.get_character(character_id) do {:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) -> @@ -167,14 +213,14 @@ defmodule WandererApp.Character.Tracker do true -> {:error, :skipped} - false -> + _ -> case WandererApp.Esi.get_character_online(eve_id, access_token: access_token, character_id: character_id, refresh_token?: true ) do {:ok, online} -> - online = _get_online(online) + online = get_online(online) WandererApp.Cache.delete("character:#{character_id}:online_forbidden") WandererApp.Cache.delete("character:#{character_id}:online_error_time") @@ -240,57 +286,43 @@ defmodule WandererApp.Character.Tracker do end end - defp _update_online(_), do: {:error, :skipped} + def update_online(_), do: {:error, :skipped} - defp _update_location(%{track_location: true, character_id: character_id} = character_state) do - case WandererApp.Character.get_character(character_id) do - {:ok, %{eve_id: eve_id, access_token: access_token}} when not is_nil(access_token) -> - WandererApp.Cache.has_key?("character:#{character_id}:location_forbidden") - |> case do - true -> - {:error, :skipped} + def check_online_errors(character_id) do + WandererApp.Cache.lookup!("character:#{character_id}:online_error_time") + |> case do + nil -> + :skip - false -> - case WandererApp.Esi.get_character_location(eve_id, - access_token: access_token, - character_id: character_id, - refresh_token?: true - ) do - {:ok, location} -> - character_state - |> _maybe_update_location(location) + error_time -> + duration = DateTime.diff(DateTime.utc_now(), error_time, :second) - :ok + if duration >= @online_error_timeout do + {:ok, character_state} = WandererApp.Character.get_character_state(character_id) + WandererApp.Cache.delete("character:#{character_id}:online_forbidden") + WandererApp.Cache.delete("character:#{character_id}:online_error_time") + WandererApp.Character.update_character(character_id, %{online: false}) + WandererApp.Cache.delete("character:#{character_id}:location_started") + WandererApp.Cache.delete("character:#{character_id}:start_solar_system_id") - {:error, :forbidden} -> - Logger.warning("#{__MODULE__} failed to update_location: forbidden") + WandererApp.Character.update_character_state(character_id, %{ + character_state + | is_online: false, + track_ship: false, + track_location: false + }) - WandererApp.Cache.put( - "character:#{character_id}:location_forbidden", - true, - ttl: @forbidden_ttl - ) - - {:error, :forbidden} - - {:error, error} -> - Logger.error("#{__MODULE__} failed to update_location: #{inspect(error)}") - {:error, error} - end - - _ -> - {:error, :skipped} + :ok + else + :skip end - - _ -> - {:error, :skipped} end end - defp _update_location(_), do: {:error, :skipped} - - defp _update_wallet(%{character_id: character_id} = state) do - case WandererApp.Character.get_character(character_id) do + def update_wallet(character_id) do + character_id + |> WandererApp.Character.get_character() + |> case do {:ok, %{eve_id: eve_id, access_token: access_token} = character} when not is_nil(access_token) -> character @@ -302,7 +334,7 @@ defmodule WandererApp.Character.Tracker do true -> {:error, :skipped} - false -> + _ -> case WandererApp.Esi.get_character_wallet(eve_id, params: %{datasource: "tranquility"}, access_token: access_token, @@ -310,7 +342,8 @@ defmodule WandererApp.Character.Tracker do refresh_token?: true ) do {:ok, result} -> - state |> _maybe_update_wallet(result) + {:ok, state} = WandererApp.Character.get_character_state(character_id) + maybe_update_wallet(state, result) :ok @@ -340,42 +373,10 @@ defmodule WandererApp.Character.Tracker do end end - defp _update_info(%{character_id: character_id} = character_state) do - {:ok, %{eve_id: eve_id}} = WandererApp.Character.get_character(character_id) - - WandererApp.Cache.has_key?("character:#{character_id}:info_forbidden") + defp update_alliance(%{character_id: character_id} = state, alliance_id) do + alliance_id + |> WandererApp.Esi.get_alliance_info() |> case do - true -> - {:error, :skipped} - - false -> - case WandererApp.Esi.get_character_info(eve_id) do - {:ok, info} -> - update = character_state |> _maybe_update_corporation(info) - WandererApp.Character.update_character_state(character_id, update) - - :ok - - {:error, :forbidden} -> - Logger.warning("#{__MODULE__} failed to get_character_info: forbidden") - - WandererApp.Cache.put( - "character:#{character_id}:info_forbidden", - true, - ttl: @forbidden_ttl - ) - - {:error, :forbidden} - - {:error, error} -> - Logger.error("#{__MODULE__} failed to get_character_info: #{inspect(error)}") - {:error, error} - end - end - end - - defp _update_alliance(%{character_id: character_id} = state, alliance_id) do - case WandererApp.Esi.get_alliance_info(alliance_id) do {:ok, %{"name" => alliance_name, "ticker" => alliance_ticker}} -> {:ok, character} = WandererApp.Character.get_character(character_id) @@ -390,7 +391,7 @@ defmodule WandererApp.Character.Tracker do WandererApp.Character.update_character(character_id, character_update) - Phoenix.PubSub.broadcast( + @pubsub_client.broadcast( WandererApp.PubSub, "character:#{character_id}:alliance", {:character_alliance, {character_id, character_update}} @@ -404,8 +405,10 @@ defmodule WandererApp.Character.Tracker do end end - defp _update_corporation(%{character_id: character_id} = state, corporation_id) do - case WandererApp.Esi.get_corporation_info(corporation_id) do + defp update_corporation(%{character_id: character_id} = state, corporation_id) do + corporation_id + |> WandererApp.Esi.get_corporation_info() + |> case do {:ok, %{"name" => corporation_name, "ticker" => corporation_ticker} = corporation_info} -> alliance_id = Map.get(corporation_info, "alliance_id") @@ -424,7 +427,7 @@ defmodule WandererApp.Character.Tracker do WandererApp.Character.update_character(character_id, character_update) - Phoenix.PubSub.broadcast( + @pubsub_client.broadcast( WandererApp.PubSub, "character:#{character_id}:corporation", {:character_corporation, @@ -438,7 +441,7 @@ defmodule WandererApp.Character.Tracker do state |> Map.merge(%{alliance_id: alliance_id, corporation_id: corporation_id}) - |> _maybe_update_alliance() + |> maybe_update_alliance() _error -> Logger.warning("Failed to get corporation info for #{corporation_id}") @@ -446,7 +449,7 @@ defmodule WandererApp.Character.Tracker do end end - defp _maybe_update_ship( + defp maybe_update_ship( %{ character_id: character_id } = @@ -459,38 +462,33 @@ defmodule WandererApp.Character.Tracker do {:ok, %{ship: old_ship_type_id, ship_name: old_ship_name} = character} = WandererApp.Character.get_character(character_id) - case old_ship_type_id != ship_type_id or old_ship_name != ship_name do - true -> - character_update = %{ - ship: ship_type_id, - ship_name: ship_name - } + ship_updated = old_ship_type_id != ship_type_id || old_ship_name != ship_name - {:ok, _character} = - WandererApp.Api.Character.update_ship(character, character_update) + if ship_updated do + character_update = %{ + ship: ship_type_id, + ship_name: ship_name + } - WandererApp.Character.update_character(character_id, character_update) + {:ok, _character} = + WandererApp.Api.Character.update_ship(character, character_update) - state - - _ -> - state + WandererApp.Character.update_character(character_id, character_update) end + + state end - defp _maybe_update_location( + defp maybe_update_location( %{ character_id: character_id } = state, location ) do - location = _get_location(location) + location = get_location(location) - if not WandererApp.Cache.lookup!( - "character:#{character_id}:location_started", - false - ) do + if not is_location_started?(character_id) do WandererApp.Cache.lookup!("character:#{character_id}:start_solar_system_id", nil) |> case do nil -> @@ -512,58 +510,51 @@ defmodule WandererApp.Character.Tracker do {:ok, %{solar_system_id: solar_system_id, structure_id: structure_id} = character} = WandererApp.Character.get_character(character_id) - WandererApp.Cache.lookup!( - "character:#{character_id}:location_started", - false - ) + (not is_location_started?(character_id) || + is_location_updated?(location, solar_system_id, structure_id)) |> case do true -> - case solar_system_id != location.solar_system_id or - structure_id != location.structure_id do - true -> - {:ok, _character} = WandererApp.Api.Character.update_location(character, location) - - WandererApp.Character.update_character(character_id, location) - - :ok - - _ -> - :ok - end - - false -> {:ok, _character} = WandererApp.Api.Character.update_location(character, location) WandererApp.Character.update_character(character_id, location) :ok + + _ -> + :ok end state end - defp _maybe_update_corporation( + defp is_location_started?(character_id), + do: + WandererApp.Cache.lookup!( + "character:#{character_id}:location_started", + false + ) + + defp is_location_updated?(location, solar_system_id, structure_id), + do: + solar_system_id != location.solar_system_id || + structure_id != location.structure_id + + defp maybe_update_corporation( state, %{ "corporation_id" => corporation_id } = _info - ) do - case corporation_id do - nil -> - state + ) + when not is_nil(corporation_id), + do: update_corporation(state, corporation_id) - _ -> - _update_corporation(state, corporation_id) - end - end - - defp _maybe_update_corporation( + defp maybe_update_corporation( state, _info ), do: state - defp _maybe_update_alliance( + defp maybe_update_alliance( %{character_id: character_id, alliance_id: alliance_id} = state ) do @@ -582,7 +573,7 @@ defmodule WandererApp.Character.Tracker do WandererApp.Character.update_character(character_id, character_update) - Phoenix.PubSub.broadcast( + @pubsub_client.broadcast( WandererApp.PubSub, "character:#{character_id}:alliance", {:character_alliance, {character_id, character_update}} @@ -591,11 +582,11 @@ defmodule WandererApp.Character.Tracker do state _ -> - _update_alliance(state, alliance_id) + update_alliance(state, alliance_id) end end - defp _maybe_update_wallet( + defp maybe_update_wallet( %{character_id: character_id} = state, wallet_balance @@ -611,7 +602,7 @@ defmodule WandererApp.Character.Tracker do eve_wallet_balance: wallet_balance }) - Phoenix.PubSub.broadcast( + @pubsub_client.broadcast( WandererApp.PubSub, "character:#{character_id}", {:character_wallet_balance} @@ -620,7 +611,7 @@ defmodule WandererApp.Character.Tracker do state end - defp _maybe_start_online_tracking( + defp maybe_start_online_tracking( state, %{track_online: true} = _track_settings ), @@ -631,38 +622,37 @@ defmodule WandererApp.Character.Tracker do track_ship: true } - defp _maybe_start_online_tracking( + defp maybe_start_online_tracking( state, _track_settings ), do: state - defp _maybe_start_location_tracking( + defp maybe_start_location_tracking( state, %{track_location: true} = _track_settings - ) do - %{state | track_location: true} - end + ), + do: %{state | track_location: true} - defp _maybe_start_location_tracking( + defp maybe_start_location_tracking( state, _track_settings ), do: state - defp _maybe_start_ship_tracking( + defp maybe_start_ship_tracking( state, %{track_ship: true} = _track_settings ), do: %{state | track_ship: true} - defp _maybe_start_ship_tracking( + defp maybe_start_ship_tracking( state, _track_settings ), do: state - defp _maybe_update_active_maps( + defp maybe_update_active_maps( %{character_id: character_id, active_maps: active_maps} = state, %{map_id: map_id, track: true} = _track_settings @@ -677,11 +667,12 @@ defmodule WandererApp.Character.Tracker do %{state | active_maps: [map_id | active_maps] |> Enum.uniq()} end - defp _maybe_update_active_maps( + defp maybe_update_active_maps( %{character_id: character_id, active_maps: active_maps} = state, %{map_id: map_id, track: false} = _track_settings ) do - case WandererApp.Cache.take("character:#{character_id}:map:#{map_id}:tracking_start_time") do + WandererApp.Cache.take("character:#{character_id}:map:#{map_id}:tracking_start_time") + |> case do start_time when not is_nil(start_time) -> duration = DateTime.diff(DateTime.utc_now(), start_time, :second) :telemetry.execute([:wanderer_app, :character, :tracker], %{duration: duration}) @@ -695,13 +686,13 @@ defmodule WandererApp.Character.Tracker do %{state | active_maps: Enum.filter(active_maps, &(&1 != map_id))} end - defp _maybe_update_active_maps( + defp maybe_update_active_maps( state, _track_settings ), do: state - defp _maybe_stop_tracking( + defp maybe_stop_tracking( %{active_maps: [], character_id: character_id, opts: opts} = state, _track_settings ) do @@ -722,25 +713,21 @@ defmodule WandererApp.Character.Tracker do } end - defp _maybe_stop_tracking( + defp maybe_stop_tracking( state, _track_settings ), do: state - defp _get_location(%{"solar_system_id" => solar_system_id, "structure_id" => structure_id}) do - %{solar_system_id: solar_system_id, structure_id: structure_id} - end + defp get_location(%{"solar_system_id" => solar_system_id, "structure_id" => structure_id}), + do: %{solar_system_id: solar_system_id, structure_id: structure_id} - defp _get_location(%{"solar_system_id" => solar_system_id}) do - %{solar_system_id: solar_system_id, structure_id: nil} - end + defp get_location(%{"solar_system_id" => solar_system_id}), + do: %{solar_system_id: solar_system_id, structure_id: nil} - defp _get_location(_), do: %{solar_system_id: nil, structure_id: nil} + defp get_location(_), do: %{solar_system_id: nil, structure_id: nil} - defp _get_online(%{"online" => online}) do - %{online: online} - end + defp get_online(%{"online" => online}), do: %{online: online} - defp _get_online(_), do: %{} + defp get_online(_), do: %{} end diff --git a/lib/wanderer_app/character/tracker_manager.ex b/lib/wanderer_app/character/tracker_manager.ex index fb887cb4..fa1489e8 100644 --- a/lib/wanderer_app/character/tracker_manager.ex +++ b/lib/wanderer_app/character/tracker_manager.ex @@ -46,9 +46,7 @@ defmodule WandererApp.Character.TrackerManager do def handle_call(:error, _, state), do: {:stop, :error, :ok, state} @impl true - def handle_call(:stop, _, state) do - {:stop, :normal, :ok, state} - end + def handle_call(:stop, _, state), do: {:stop, :normal, :ok, state} @impl true def handle_call( diff --git a/lib/wanderer_app/character/tracker_manager_impl.ex b/lib/wanderer_app/character/tracker_manager_impl.ex index 108abed1..94de40f3 100644 --- a/lib/wanderer_app/character/tracker_manager_impl.ex +++ b/lib/wanderer_app/character/tracker_manager_impl.ex @@ -70,12 +70,10 @@ defmodule WandererApp.Character.TrackerManager.Impl do false -> Logger.debug(fn -> "Start character tracker: #{inspect(character_id)}" end) - Task.start_link(fn -> - WandererApp.Character.update_character_state(character_id, %{opts: opts}) - :telemetry.execute([:wanderer_app, :character, :tracker, :started], %{count: 1}) - - :ok - end) + WandererApp.TaskWrapper.start_link(WandererApp.Character, :update_character_state, [ + character_id, + %{opts: opts} + ]) tracked_characters = [character_id | state.characters] |> Enum.uniq() WandererApp.Cache.insert("tracked_characters", tracked_characters) @@ -180,9 +178,9 @@ defmodule WandererApp.Character.TrackerManager.Impl do characters |> Enum.map(fn character_id -> - Task.start_link(fn -> - WandererApp.Character.Tracker.update_online(character_id) - end) + WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_online, [ + character_id + ]) end) state @@ -207,10 +205,19 @@ defmodule WandererApp.Character.TrackerManager.Impl do Process.send_after(self(), :check_online_errors, @check_online_errors_interval) characters - |> Enum.map(fn character_id -> - Task.start_link(fn -> - WandererApp.Character.Tracker.check_online_errors(character_id) - end) + |> Task.async_stream( + fn character_id -> + WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :check_online_errors, [ + character_id + ]) + end, + timeout: :timer.seconds(15), + max_concurrency: System.schedulers_online(), + on_timeout: :kill_task + ) + |> Enum.each(fn + {:ok, _result} -> :ok + {:error, reason} -> @logger.error("Error in check_online_errors: #{inspect(reason)}") end) state @@ -228,9 +235,9 @@ defmodule WandererApp.Character.TrackerManager.Impl do characters |> Enum.map(fn character_id -> - Task.start_link(fn -> - WandererApp.Character.Tracker.update_location(character_id) - end) + WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_location, [ + character_id + ]) end) state @@ -257,9 +264,9 @@ defmodule WandererApp.Character.TrackerManager.Impl do characters |> Enum.map(fn character_id -> - Task.start_link(fn -> - WandererApp.Character.Tracker.update_ship(character_id) - end) + WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_ship, [ + character_id + ]) end) state @@ -285,10 +292,19 @@ defmodule WandererApp.Character.TrackerManager.Impl do Process.send_after(self(), :update_info, @update_info_interval) characters - |> Enum.map(fn character_id -> - Task.start_link(fn -> - WandererApp.Character.Tracker.update_info(character_id) - end) + |> Task.async_stream( + fn character_id -> + WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_info, [ + character_id + ]) + end, + timeout: :timer.seconds(15), + max_concurrency: System.schedulers_online(), + on_timeout: :kill_task + ) + |> Enum.each(fn + {:ok, _result} -> :ok + {:error, reason} -> @logger.error("Error in update_info: #{inspect(reason)}") end) state @@ -314,10 +330,19 @@ defmodule WandererApp.Character.TrackerManager.Impl do Process.send_after(self(), :update_wallet, @update_wallet_interval) characters - |> Enum.map(fn character_id -> - Task.start_link(fn -> - WandererApp.Character.Tracker.update_wallet(character_id) - end) + |> Task.async_stream( + fn character_id -> + WandererApp.TaskWrapper.start_link(WandererApp.Character.Tracker, :update_wallet, [ + character_id + ]) + end, + timeout: :timer.seconds(15), + max_concurrency: System.schedulers_online(), + on_timeout: :kill_task + ) + |> Enum.each(fn + {:ok, _result} -> :ok + {:error, reason} -> @logger.error("Error in update_wallet: #{inspect(reason)}") end) state @@ -358,7 +383,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do end end end, - max_concurrency: 20, + max_concurrency: System.schedulers_online(), on_timeout: :kill_task, timeout: :timer.seconds(15) ) @@ -394,7 +419,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do WandererApp.Character.update_character_state(character_id, character_state) end, - max_concurrency: 20, + max_concurrency: System.schedulers_online(), on_timeout: :kill_task, timeout: :timer.seconds(30) ) @@ -404,7 +429,7 @@ defmodule WandererApp.Character.TrackerManager.Impl do end def handle_info({:stop_track, character_id}, state) do - Logger.debug(fn -> "Stopping character tracker: #{inspect(character_id)}" end) + @logger.debug(fn -> "Stopping character tracker: #{inspect(character_id)}" end) stop_tracking(state, character_id) end diff --git a/lib/wanderer_app/ddrt.ex b/lib/wanderer_app/ddrt.ex deleted file mode 100755 index 64166553..00000000 --- a/lib/wanderer_app/ddrt.ex +++ /dev/null @@ -1,42 +0,0 @@ -defmodule DDRT do - use DDRT.DynamicRtree - alias DDRT.DynamicRtree - - @moduledoc """ - This is the top-level `DDRT` module. Use this to create a distributed r-tree. If you're only interested in using this package for the r-tree implementation, you should instead use `DDRT.DynamicRtree` - - Please refer to `DDRT.DynamicRtree` module documentation for complete function specs and examples for general usage of the core API methods. - """ - - # DDRT party begins. - @spec start_link(DynamicRtree.tree_config()) :: {:ok, pid} - @doc "See `DDRT.DynamicRtree.start_link/1` for documentation and configuration parameters" - def start_link(opts) do - name = Keyword.get(opts, :name, DynamicRtree) - - children = [ - {DeltaCrdt, - [ - crdt: DeltaCrdt.AWLWWMap, - name: Module.concat([name, Crdt]), - on_diffs: &on_diffs(&1, DynamicRtree, name) - ]}, - {DynamicRtree, - [ - conf: Keyword.put_new(opts, :mode, :distributed), - crdt: Module.concat([name, Crdt]), - name: name - ]} - ] - - Supervisor.start_link(children, - strategy: :one_for_one, - name: Module.concat([name, Supervisor]) - ) - end - - @doc false - def on_diffs(diffs, mod, name) do - mod.merge_diffs(diffs, name) - end -end diff --git a/lib/wanderer_app/ddrt/dynamic_rtree.ex b/lib/wanderer_app/ddrt/dynamic_rtree.ex deleted file mode 100755 index ecc72908..00000000 --- a/lib/wanderer_app/ddrt/dynamic_rtree.ex +++ /dev/null @@ -1,725 +0,0 @@ -defmodule DDRT.DynamicRtree do - use GenServer, restart: :transient - use DDRT.DynamicRtreeImpl - - @type tree_init :: [ - name: GenServer.name(), - crdt: module(), - conf: tree_config() - ] - - @type tree_config :: [ - name: GenServer.name(), - width: integer(), - type: module(), - verbose: boolean(), - seed: integer(), - mode: ddrt_mode() - ] - - @type ddrt_mode :: :standalone | :distributed - @type coord_range :: {number(), number()} - @type bounding_box :: list(coord_range()) - @type id :: number() | String.t() - @type leaf :: {id(), bounding_box()} - @type member :: GenServer.name() | {GenServer.name(), node()} - - @callback delete(ids :: id() | [id()], name :: GenServer.name()) :: - {:ok, map()} | {:badtree, map()} - @callback insert(leaves :: leaf() | [leaf()], name :: GenServer.name()) :: - {:ok, map()} | {:badtree, map()} - @callback metadata(name :: GenServer.name()) :: map() - @callback pquery(box :: bounding_box(), depth :: integer(), name :: GenServer.name()) :: - {:ok, [id()]} | {:badtree, map()} - @callback query(box :: bounding_box(), name :: GenServer.name()) :: - {:ok, [id()]} | {:badtree, map()} - @callback update( - ids :: id(), - box :: bounding_box() | {bounding_box(), bounding_box()}, - name :: GenServer.name() - ) :: {:ok, map()} | {:badtree, map()} - @callback bulk_update(leaves :: [leaf()], name :: GenServer.name()) :: - {:ok, map()} | {:badtree, map()} - @callback new(opts :: Keyword.t(), name :: GenServer.name()) :: {:ok, map()} - @callback tree(name :: GenServer.name()) :: map() - @callback set_members(name :: GenServer.name(), [member()]) :: :ok - - @doc false - defmacro doc_referral({name, arity}) do - "See `DDRT.DynamicRtree.#{name}/#{arity}` for documentation and usage examples." - end - - defmacro __using__(_) do - quote do - alias DDRT.DynamicRtree - @behaviour DynamicRtree - - @doc unquote(doc_referral({:delete, 2})) - defdelegate delete(ids, name), to: DynamicRtree - - @doc unquote(doc_referral({:insert, 2})) - defdelegate insert(leaves, name), to: DynamicRtree - - @doc unquote(doc_referral({:metadata, 1})) - defdelegate metadata(name), to: DynamicRtree - - @doc unquote(doc_referral({:pquery, 3})) - defdelegate pquery(box, depth, name), to: DynamicRtree - - @doc unquote(doc_referral({:query, 2})) - defdelegate query(box, name), to: DynamicRtree - - @doc unquote(doc_referral({:update, 3})) - defdelegate update(ids, box, name), to: DynamicRtree - - @doc unquote(doc_referral({:bulk_update, 2})) - defdelegate bulk_update(leaves, name), to: DynamicRtree - - @doc unquote(doc_referral({:new, 2})) - defdelegate new(opts, name), to: DynamicRtree - - @doc unquote(doc_referral({:tree, 1})) - defdelegate tree(name), to: DynamicRtree - - @doc unquote(doc_referral({:set_members, 2})) - defdelegate set_members(name, members), to: DynamicRtree - end - end - - defstruct metadata: nil, - tree: nil, - listeners: [], - crdt: nil, - name: nil - - @moduledoc """ - Use this module if you're interested in creating an R-Tree optimized to run on a single machine. If you'd instead like to run a distributed R-Tree on a cluster of Elixir nodes, use the `DDRT` module. - """ - - @doc """ - These are all of the possible configuration parameters for `opts` and their default values: - - - **name**: The name of the DDRT process. Defaults to `DDRT` - - **width**: The max number of children a node may have. Defaults to `6` - - **verbose**: allows `Logger` to report console logs. (Also decreases performance). Defaults to `false`. - - **seed**: Sets the seed value for the pseudo-random number generator which generates the unique IDs for each node in the tree. This is a deterministic process; so the same seed value will guarantee the same pseudo-random unique IDs being generated for your tree in the same order each time. Defaults to `0` - """ - @spec start_link(opts :: tree_init()) :: {:ok, pid()} | {:error, term()} - def start_link(opts) do - name = Keyword.get(opts, :name, DDRT) - GenServer.start_link(__MODULE__, opts, name: name) - end - - @impl true - def init(opts) do - conf = filter_conf(opts[:conf]) - {t, meta} = tree_new(conf) - listeners = Node.list() - - t = - if %{metadata: meta} |> is_distributed? do - DeltaCrdt.set_neighbours(opts[:crdt], Enum.map(Node.list(), fn x -> {opts[:crdt], x} end)) - - crdt_value = DeltaCrdt.to_map(opts[:crdt]) - :net_kernel.monitor_nodes(true, node_type: :visible) - if crdt_value != %{}, do: reconstruct_from_crdt(crdt_value, t), else: t - else - t - end - - {:ok, - %__MODULE__{ - name: opts[:name], - metadata: meta, - tree: t, - listeners: listeners, - crdt: opts[:crdt] - }} - end - - @opt_values %{ - type: [Map, MerkleMap], - mode: [:standalone, :distributed] - } - - @defopts [ - width: 6, - type: Map, - mode: :standalone, - verbose: false, - seed: 0 - ] - - @spec new(opts :: Keyword.t(), name :: GenServer.name()) :: {:ok, map()} - def new(opts \\ @defopts, name \\ DDRT) when is_list(opts) do - GenServer.call(name, {:new, opts}) - end - - @spec insert(leaves :: leaf() | [leaf()], name :: GenServer.name()) :: - {:ok, map()} | {:badtree, map()} - def insert(_a, name \\ DDRT) - - @doc """ - Insert `leaves` into the r-tree with process with name `name` - - Returns `{:ok,map()}` - - ## Parameters - - - `leaves`: the data to insert. - - `name`: the r-tree name where you want to insert. - - ## Examples - - Individual insertion: - - ``` - iex> DynamicRtree.insert({"Griffin", [{4,5},{6,7}]}, :my_rtree) - iex> DynamicRtree.insert({"Parker", [{14,15},{16,17}]}, :my_rtree) - - {:ok, - %{ - 43143342109176739 => {["Parker", "Griffin"], nil, [{4, 15}, {6, 17}]}, - :root => 43143342109176739, - :ticket => [19125803434255161 | 82545666616502197], - "Griffin" => {:leaf, 43143342109176739, [{4, 5}, {6, 7}]}, - "Parker" => {:leaf, 43143342109176739, [{14, 15}, {16, 17}]} - }} - ``` - - Bulk Insertion: - - ``` - iex> DynamicRtree.insert([{"Griffin", [{4,5},{6,7}]}, {"Parker", [{14,15},{16,17}]}], :my_rtree) - - {:ok, - %{ - 43143342109176739 => {["Parker", "Griffin"], nil, [{4, 15}, {6, 17}]}, - :root => 43143342109176739, - :ticket => [19125803434255161 | 82545666616502197], - "Griffin" => {:leaf, 43143342109176739, [{4, 5}, {6, 7}]}, - "Parker" => {:leaf, 43143342109176739, [{14, 15}, {16, 17}]} - }} - ``` - """ - - def insert(leaves, name) when is_list(leaves) do - GenServer.call(name, {:bulk_insert, leaves}, :infinity) - end - - def insert(leaf, name) do - GenServer.call(name, {:insert, leaf}, :infinity) - end - - @doc """ - Query to get every leaf id overlapped by `box`. - - Returns `[id's]`. - - ## Examples - - iex> DynamicRtree.query([{0,7},{4,8}],:my_rtree) - {:ok, ["Griffin"]} - - """ - - @spec query(box :: bounding_box(), name :: GenServer.name()) :: - {:ok, [id()]} | {:badtree, map()} - def query(box, name \\ DDRT) do - GenServer.call(name, {:query, box}) - end - - @doc """ - Query to get every node id overlapped by `box` at the defined `depth`. - - Returns `[id's]`. - """ - - @spec pquery(box :: bounding_box(), depth :: integer(), name :: GenServer.name()) :: - {:ok, [id()]} | {:badtree, map()} - def pquery(box, depth, name \\ DDRT) do - GenServer.call(name, {:query_depth, {box, depth}}) - end - - @spec delete(ids :: id() | [id()], name :: GenServer.name()) :: - {:ok, map()} | {:badtree, map()} - def delete(_a, name \\ DDRT) - - @doc """ - Delete the leaves with the given `ids`. - - Returns `{:ok,map()}` - - ## Parameters - - - `ids`: Id or list of Id that you want to delete. - - `name`: the name of the rtree process. - - ## Examples - Individual deletion: - - ``` - iex> DynamicRtree.delete("Griffin",:my_rtree) - iex> DynamicRtree.delete("Parker",:my_rtree) - ``` - - Bulk Deletion: - - ``` - iex> DynamicRtree.delete(["Griffin","Parker"],:my_rtree) - ``` - """ - - def delete(ids, name) when is_list(ids) do - GenServer.call(name, {:bulk_delete, ids}, :infinity) - end - - def delete(id, name) do - GenServer.call(name, {:delete, id}) - end - - @doc """ - Update a bunch of r-tree leaves to the new bounding boxes defined. - - Returns `{:ok,map()}` - - ## Examples - - ``` - iex> DynamicRtree.bulk_update([{"Griffin",[{0,1},{0,1}]},{"Parker",[{10,11},{10,11}]}],:my_rtree) - - {:ok, - %{ - 43143342109176739 => {["Parker", "Griffin"], nil, [{0, 11}, {0, 11}]}, - :root => 43143342109176739, - :ticket => [19125803434255161 | 82545666616502197], - "Griffin" => {:leaf, 43143342109176739, [{0, 1}, {0, 1}]}, - "Parker" => {:leaf, 43143342109176739, [{10, 11}, {10, 11}]} - }} - ``` - """ - @spec bulk_update(leaves :: [leaf()], name :: GenServer.name()) :: - {:ok, map()} | {:badtree, map()} - def bulk_update(updates, name \\ DDRT) when is_list(updates) do - GenServer.call(name, {:bulk_update, updates}, :infinity) - end - - @doc """ - Update a single leaf bounding box - - Returns `{:ok,map()}` - - ## Examples - ``` - iex> DynamicRtree.update({"Griffin",[{0,1},{0,1}]},:my_rtree) - - {:ok, - %{ - 43143342109176739 => {["Parker", "Griffin"], nil, [{0, 11}, {0, 11}]}, - :root => 43143342109176739, - :ticket => [19125803434255161 | 82545666616502197], - "Griffin" => {:leaf, 43143342109176739, [{0, 1}, {0, 1}]}, - "Parker" => {:leaf, 43143342109176739, [{10, 11}, {16, 17}]} - }} - ``` - """ - - @spec update( - ids :: id(), - box :: bounding_box() | {bounding_box(), bounding_box()}, - name :: GenServer.name() - ) :: {:ok, map()} | {:badtree, map()} - def update(id, update, name \\ DDRT) do - GenServer.call(name, {:update, {id, update}}) - end - - @doc """ - Get the r-tree metadata - - Returns `map()` - - ## Examples - - iex> DynamicRtree.metadata(:my_rtree) - - %{ - params: %{mode: :standalone, seed: 0, type: Map, verbose: false, width: 6}, - seeding: %{ - bits: 58, - jump: #Function<3.53802439/1 in :rand.mk_alg/1>, - next: #Function<0.53802439/1 in :rand.mk_alg/1>, - type: :exrop, - uniform: #Function<1.53802439/1 in :rand.mk_alg/1>, - uniform_n: #Function<2.53802439/2 in :rand.mk_alg/1>, - weak_low_bits: 1 - } - } - - - """ - @spec metadata(name :: GenServer.name()) :: map() - def metadata(name \\ DDRT) - - def metadata(name) do - GenServer.call(name, :metadata) - end - - @doc """ - Get the r-tree representation - - Returns `map()` - - ## Examples - - ``` - iex> DynamicRtree.metadata(:my_rtree) - - %{ - 43143342109176739 => {["Parker", "Griffin"], nil, [{0, 11}, {0, 11}]}, - :root => 43143342109176739, - :ticket => [19125803434255161 | 82545666616502197], - "Griffin" => {:leaf, 43143342109176739, [{0, 1}, {0, 1}]}, - "Parker" => {:leaf, 43143342109176739, [{10, 11}, {10, 11}]} - } - ``` - - """ - @spec tree(name :: GenServer.name()) :: map() - def tree(name \\ DDRT) - - def tree(name) do - GenServer.call(name, :tree) - end - - @doc """ - Set the members of the `DDRT` cluster. - - `members` should be in the format `{GenServer.name(), node()}`. - - ## Examples - - ``` - DDRT.set_members(DDRT, [{DDRT.A, :yournode@foreignhost}, {DDRT.B, :yournode@foreignhost}]) - ``` - - """ - @spec set_members(name :: GenServer.name(), [member()]) :: :ok - def set_members(name, members) do - :ok = GenServer.call(name, {:set_members, members}) - :ok - end - - def merge_diffs(_a, name \\ DDRT) - @doc false - def merge_diffs(diffs, name) do - send(name, {:merge_diff, diffs}) - end - - ## PRIVATE METHODS - - defp fully_qualified_name({_name, _node} = fq_pair), do: fq_pair - - defp fully_qualified_name(name) do - {name, Node.self()} - end - - defp is_distributed?(state) do - state.metadata[:params][:mode] == :distributed - end - - defp constraints() do - %{ - width: fn v -> v > 0 end, - type: fn v -> v in (@opt_values |> Map.get(:type)) end, - mode: fn v -> v in (@opt_values |> Map.get(:mode)) end, - verbose: fn v -> is_boolean(v) end, - seed: fn v -> is_integer(v) end - } - end - - defp filter_conf(opts) do - # set default :mode to :standalone - opts = Keyword.put_new(opts, :mode, :standalone) - - new_opts = - case opts[:mode] do - :distributed -> Keyword.put(opts, :type, MerkleMap) - _ -> opts - end - - good_keys = - new_opts - |> Keyword.keys() - |> Enum.filter(fn k -> - constraints() |> Map.has_key?(k) and constraints()[k].(new_opts[k]) - end) - - good_keys - |> Enum.reduce(@defopts, fn k, acc -> - acc |> Keyword.put(k, new_opts[k]) - end) - end - - defp get_rbundle(state) do - meta = state.metadata - params = meta.params - - %{ - tree: state.tree, - width: params[:width], - verbose: params[:verbose], - type: params[:type], - seeding: meta[:seeding] - } - end - - @impl true - def handle_call({:set_members, members}, _from, state) do - self_crdt = - Module.concat([state.name, Crdt]) - |> fully_qualified_name() - - member_crdts = - members - |> Enum.map(&fully_qualified_name(&1)) - |> Enum.map(fn {pname, node} -> - {Module.concat([pname, Crdt]), node} - end) - - result = DeltaCrdt.set_neighbours(self_crdt, member_crdts) - {:reply, result, state} - end - - @impl true - def handle_call({:new, config}, _from, state) do - conf = config |> filter_conf - {t, meta} = tree_new(conf) - {:reply, {:ok, t}, %__MODULE__{state | metadata: meta, tree: t}} - end - - @impl true - def handle_call({:insert, leaf}, _from, state) do - r = - {_atom, t} = - case state.tree do - nil -> {:badtree, state.tree} - _ -> {:ok, get_rbundle(state) |> tree_insert(leaf)} - end - - if is_distributed?(state) do - diffs = tree_diffs(state.tree, t) - sync_crdt(diffs, state.crdt) - end - - {:reply, r, %__MODULE__{state | tree: t}} - end - - @impl true - def handle_call({:bulk_insert, leaves}, _from, state) do - r = - {_atom, t} = - case state.tree do - nil -> - {:badtree, state.tree} - - _ -> - final_rbundle = - leaves - |> Enum.reduce(get_rbundle(state), fn l, acc -> - %{acc | tree: acc |> tree_insert(l)} - end) - - {:ok, final_rbundle.tree} - end - - if is_distributed?(state) do - diffs = tree_diffs(state.tree, t) - sync_crdt(diffs, state.crdt) - end - - {:reply, r, %__MODULE__{state | tree: t}} - end - - @impl true - def handle_call({:query, box}, _from, state) do - r = - {_atom, _t} = - case state.tree do - nil -> {:badtree, state.tree} - _ -> {:ok, get_rbundle(state) |> tree_query(box)} - end - - {:reply, r, state} - end - - @impl true - def handle_call({:query_depth, {box, depth}}, _from, state) do - r = - {_atom, _t} = - case state.tree do - nil -> {:badtree, state.tree} - _ -> {:ok, get_rbundle(state) |> tree_query(box, depth)} - end - - {:reply, r, state} - end - - @impl true - def handle_call({:delete, id}, _from, state) do - r = - {_atom, t} = - case state.tree do - nil -> {:badtree, state.tree} - _ -> {:ok, get_rbundle(state) |> tree_delete(id)} - end - - if is_distributed?(state) do - diffs = tree_diffs(state.tree, t) - sync_crdt(diffs, state.crdt) - end - - {:reply, r, %__MODULE__{state | tree: t}} - end - - @impl true - def handle_call({:bulk_delete, ids}, _from, state) do - r = - {_atom, t} = - case state.tree do - nil -> - {:badtree, state.tree} - - _ -> - final_rbundle = - ids - |> Enum.reduce(get_rbundle(state), fn id, acc -> - %{acc | tree: acc |> tree_delete(id)} - end) - - {:ok, final_rbundle.tree} - end - - if is_distributed?(state) do - diffs = tree_diffs(state.tree, t) - sync_crdt(diffs, state.crdt) - end - - {:reply, r, %__MODULE__{state | tree: t}} - end - - @impl true - def handle_call({:update, {id, update}}, _from, state) do - r = - {_atom, t} = - case state.tree do - nil -> {:badtree, state.tree} - _ -> {:ok, get_rbundle(state) |> tree_update_leaf(id, update)} - end - - if is_distributed?(state) do - diffs = tree_diffs(state.tree, t) - sync_crdt(diffs, state.crdt) - end - - {:reply, r, %__MODULE__{state | tree: t}} - end - - @impl true - def handle_call({:bulk_update, updates}, _from, state) do - r = - {_atom, t} = - case state.tree do - nil -> - {:badtree, state.tree} - - _ -> - final_rbundle = - updates - |> Enum.reduce(get_rbundle(state), fn {id, update} = _u, acc -> - %{acc | tree: acc |> tree_update_leaf(id, update)} - end) - - {:ok, final_rbundle.tree} - end - - if is_distributed?(state) do - diffs = tree_diffs(state.tree, t) - sync_crdt(diffs, state.crdt) - end - - {:reply, r, %__MODULE__{state | tree: t}} - end - - @impl true - def handle_call(:metadata, _from, state) do - {:reply, state.metadata, state} - end - - @impl true - def handle_call(:tree, _from, state) do - {:reply, state.tree, state} - end - - # Distributed things - - @impl true - def handle_info({:merge_diff, diff}, state) do - new_tree = - diff - |> Enum.reduce(state.tree, fn x, acc -> - case x do - {:add, k, v} -> acc |> MerkleMap.put(k, v) - {:remove, k} -> acc |> MerkleMap.delete(k) - end - end) - - {:noreply, %__MODULE__{state | tree: new_tree}} - end - - def handle_info({:nodeup, _node, _opts}, state) do - DeltaCrdt.set_neighbours(state.crdt, Enum.map(Node.list(), fn x -> {state.crdt, x} end)) - {:noreply, %__MODULE__{state | listeners: Node.list()}} - end - - def handle_info({:nodedown, _node, _opts}, state) do - DeltaCrdt.set_neighbours(state.crdt, Enum.map(Node.list(), fn x -> {state.crdt, x} end)) - {:noreply, %__MODULE__{state | listeners: Node.list()}} - end - - @doc false - def sync_crdt(diffs, crdt) when length(diffs) > 0 do - diffs - |> Enum.each(fn {k, v} -> - if v do - DeltaCrdt.put(crdt, k, v) - else - DeltaCrdt.delete(crdt, k) - end - end) - end - - @doc false - def sync_crdt(_diffs, _crdt) do - end - - @doc false - def reconstruct_from_crdt(map, t) do - map - |> Enum.reduce(t, fn {x, y}, acc -> - acc |> MerkleMap.put(x, y) - end) - end - - @doc false - def tree_diffs(old_tree, new_tree) when not is_nil(old_tree) and not is_nil(new_tree) do - case MerkleMap.diff_keys( - old_tree |> MerkleMap.update_hashes(), - new_tree |> MerkleMap.update_hashes() - ) do - {:ok, keys} -> keys |> Enum.map(fn x -> {x, new_tree |> MerkleMap.get(x)} end) - _ -> [] - end - end - - def tree_diffs(_old_tree, _new_tree), do: [] -end diff --git a/lib/wanderer_app/ddrt/dynamic_rtree_impl.ex b/lib/wanderer_app/ddrt/dynamic_rtree_impl.ex deleted file mode 100755 index d6dc44d7..00000000 --- a/lib/wanderer_app/ddrt/dynamic_rtree_impl.ex +++ /dev/null @@ -1,687 +0,0 @@ -defmodule DDRT.DynamicRtreeImpl do - alias DDRT.DynamicRtreeImpl.{Node, Utils} - - require Logger - import IO.ANSI - - # Between 1 y 64800. Bigger value => ^ updates speed, ~v query speed. - @max_area 20000 - - defmacro __using__(_) do - quote do - alias DDRT.DynamicRtreeImpl - - @doc false - defdelegate tree_new(opts), to: DynamicRtreeImpl - - @doc false - defdelegate tree_insert(tree, leaf), to: DynamicRtreeImpl - - @doc false - defdelegate tree_query(tree, box), to: DynamicRtreeImpl - - @doc false - defdelegate tree_query(tree, box, depth), to: DynamicRtreeImpl - - @doc false - defdelegate tree_delete(tree, id), to: DynamicRtreeImpl - - @doc false - defdelegate tree_update_leaf(tree, id, update), to: DynamicRtreeImpl - end - end - - # PUBLIC METHODS - - def tree_new(opts) do - {f, s} = :rand.seed(:exrop, opts[:seed]) - {node, new_ticket} = Node.new(f, s) - - tree_init = - case opts[:type] do - Map -> %{} - MerkleMap -> %MerkleMap{} - end - - tree = - tree_init - |> opts[:type].put(:ticket, new_ticket) - |> opts[:type].put(:root, node) - |> opts[:type].put(node, {[], nil, [{0, 0}, {0, 0}]}) - - {tree, %{params: opts, seeding: f}} - end - - def tree_insert(rbundle, {id, _box} = leaf) do - if rbundle.tree |> rbundle[:type].get(id) do - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - green() <> - "Insertion" <> - cyan() <> - "] failed:" <> - yellow() <> - " [#{id}] " <> - cyan() <> - "already exists at tree." <> - yellow() <> " [Tip]" <> cyan() <> " use " <> yellow() <> "update_leaf/3" - ) - - rbundle.tree - else - path = best_subtree(rbundle, leaf) - t1 = :os.system_time(:microsecond) - - r = - insertion(rbundle, path, leaf) - |> recursive_update(tl(path), leaf, :insertion) - - t2 = :os.system_time(:microsecond) - - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - green() <> - "Insertion" <> - cyan() <> - "] success: " <> - yellow() <> - "[#{id}]" <> cyan() <> " was inserted at" <> yellow() <> " ['#{hd(path)}']" - ) - - if rbundle.verbose, - do: - Logger.info( - cyan() <> - "[" <> green() <> "Insertion" <> cyan() <> "] took" <> yellow() <> " #{t2 - t1} µs" - ) - - r - end - end - - def tree_query(rbundle, box) do - t1 = :os.system_time(:microsecond) - r = find_match_leaves(rbundle, box, [get_root(rbundle)], [], []) - t2 = :os.system_time(:microsecond) - - if rbundle.verbose, - do: - Logger.info( - cyan() <> - "[" <> - color(201) <> - "Query" <> - cyan() <> - "] box " <> - yellow() <> - "#{box |> Kernel.inspect()} " <> cyan() <> "took " <> yellow() <> "#{t2 - t1} µs" - ) - - r - end - - def tree_query(rbundle, box, depth) do - find_match_depth(rbundle, box, [{get_root(rbundle), 0}], [], depth) - end - - def tree_delete(rbundle, id) do - t1 = :os.system_time(:microsecond) - - r = - if rbundle.tree |> rbundle[:type].get(id) do - remove(rbundle, id) - else - rbundle.tree - end - - t2 = :os.system_time(:microsecond) - - if rbundle.verbose, - do: - Logger.info( - cyan() <> - "[" <> - color(124) <> - "Delete" <> - cyan() <> - "] leaf " <> - yellow() <> "[#{id}]" <> cyan() <> " took " <> yellow() <> "#{t2 - t1} µs" - ) - - r - end - - def tree_update_leaf(rbundle, id, {old_box, new_box} = boxes) do - if rbundle.tree |> rbundle[:type].get(id) do - t1 = :os.system_time(:microsecond) - r = update(rbundle, id, boxes) - t2 = :os.system_time(:microsecond) - - if rbundle.verbose, - do: - Logger.info( - cyan() <> - "[" <> - color(195) <> - "Update" <> - cyan() <> - "] " <> - yellow() <> - "[#{id}]" <> - cyan() <> - " from " <> - yellow() <> - "#{old_box |> Kernel.inspect()}" <> - cyan() <> - " to " <> - yellow() <> - "#{new_box |> Kernel.inspect()}" <> - cyan() <> " took " <> yellow() <> "#{t2 - t1} µs" - ) - - r - else - if rbundle.verbose, - do: - Logger.warning( - cyan() <> - "[" <> - color(195) <> - "Update" <> cyan() <> "] " <> yellow() <> "[#{id}] doesn't exists" <> cyan() - ) - - rbundle.tree - end - end - - # You dont need to know old_box but is a BIT slower - def tree_update_leaf(rbundle, id, new_box) do - tree_update_leaf( - rbundle, - id, - {rbundle.tree |> rbundle[:type].get(id) |> Utils.tuple_value(:bbox), new_box} - ) - end - - ### PRIVATE METHODS - - # Helpers - defp get_root(rbundle) do - rbundle.tree |> rbundle[:type].get(:root) - end - - defp is_root?(rbundle, node) do - get_root(rbundle) == node - end - - ## Internal actions - ## Insert - - # triple - S (Structure Swifty Shift) - defp triple_s(rbundle, old_node, new_node, {id, box}) do - tuple_entry = - {old_node_childs_update, _daddy, _bbox} = - rbundle.tree |> rbundle[:type].get(old_node) |> (fn {n, d, b} -> {n -- [id], d, b} end).() - - tree_update = - rbundle.tree - |> rbundle[:type].update!(new_node, fn {ch, d, b} -> {[id] ++ ch, d, b} end) - |> rbundle[:type].update!(id, fn {ch, _d, b} -> {ch, new_node, b} end) - - if length(old_node_childs_update) > 0 do - %{rbundle | tree: tree_update |> rbundle[:type].put(old_node, tuple_entry)} - |> recursive_update(old_node, box, :deletion) - else - %{rbundle | tree: tree_update} |> remove(old_node) - end - end - - defp insertion(rbundle, branch, {_id, _box} = leaf) do - tree_update = add_entry(rbundle, hd(branch), leaf) - - childs = tree_update |> rbundle[:type].get(hd(branch)) |> Utils.tuple_value(:childs) - - final_tree = - if length(childs) > rbundle.width do - handle_overflow(%{rbundle | tree: tree_update}, branch) - else - tree_update - end - - %{rbundle | tree: final_tree} - end - - defp add_entry(rbundle, node, {id, box} = _leaf) do - rbundle.tree - |> rbundle[:type].update!(node, fn {ch, daddy, b} -> - {[id] ++ ch, daddy, Utils.combine_multiple([box, b])} - end) - |> rbundle[:type].put(id, {:leaf, node, box}) - end - - defp handle_overflow(rbundle, branch) do - n = hd(branch) - {node_n, new_node} = split(rbundle, n) - treeck = rbundle.tree |> rbundle[:type].put(:ticket, new_node.next_ticket) - - if is_root?(rbundle, n) do - {new_root, ticket} = Node.new(rbundle.seeding, treeck |> rbundle[:type].get(:ticket)) - treeck = treeck |> rbundle[:type].put(:ticket, ticket) - root_bbox = Utils.combine_multiple([node_n.bbox, new_node.bbox]) - - treeck = - treeck - |> rbundle[:type].put(new_node.id, {new_node.childs, new_root, new_node.bbox}) - |> rbundle[:type].replace!(node_n.id, {node_n.childs, new_root, node_n.bbox}) - |> rbundle[:type].replace!(:root, new_root) - |> rbundle[:type].put(new_root, {[node_n.id, new_node.id], nil, root_bbox}) - - new_node.childs - |> Enum.reduce(treeck, fn c, acc -> - acc |> rbundle[:type].update!(c, fn {ch, _d, b} -> {ch, new_node.id, b} end) - end) - else - parent = hd(tl(branch)) - - treeck = - treeck - |> rbundle[:type].put(new_node.id, {new_node.childs, parent, new_node.bbox}) - |> rbundle[:type].replace!(node_n.id, {node_n.childs, parent, node_n.bbox}) - |> rbundle[:type].update!(parent, fn {ch, d, b} -> - {[new_node.id] ++ ch, d, Utils.combine_multiple([b, new_node.bbox])} - end) - - updated_tree = - new_node.childs - |> Enum.reduce(treeck, fn c, acc -> - acc |> rbundle[:type].update!(c, fn {ch, _d, b} -> {ch, new_node.id, b} end) - end) - - if length(updated_tree |> rbundle[:type].get(parent) |> elem(0)) > rbundle.width, - do: handle_overflow(%{rbundle | tree: updated_tree}, tl(branch)), - else: updated_tree - end - end - - defp split(rbundle, node) do - sorted_nodes = - rbundle.tree - |> rbundle[:type].get(node) - |> Utils.tuple_value(:childs) - |> Enum.map(fn n -> - box = rbundle.tree |> rbundle[:type].get(n) |> Utils.tuple_value(:bbox) - {box |> Utils.middle_value(), n, box} - end) - |> Enum.sort() - |> Enum.map(fn {_x, y, z} -> {y, z} end) - - {n_id, n_bbox} = - sorted_nodes |> Enum.slice(0..((rbundle.width / 2 - 1) |> Kernel.trunc())) |> Enum.unzip() - - {dn_id, dn_bbox} = - sorted_nodes - |> Enum.slice(((rbundle.width / 2) |> Kernel.trunc())..(length(sorted_nodes) - 1)) - |> Enum.unzip() - - {new_node, next_ticket} = - Node.new(rbundle.seeding, rbundle.tree |> rbundle[:type].get(:ticket)) - - n_bounds = n_bbox |> Utils.combine_multiple() - dn_bounds = dn_bbox |> Utils.combine_multiple() - - {%{id: node, childs: n_id, bbox: n_bounds}, - %{id: new_node, childs: dn_id, bbox: dn_bounds, next_ticket: next_ticket}} - end - - defp best_subtree(rbundle, leaf) do - find_best_subtree(rbundle, get_root(rbundle), leaf, []) - end - - defp find_best_subtree(rbundle, root, {_id, box} = leaf, track) do - childs = rbundle.tree |> rbundle[:type].get(root) |> Utils.tuple_value(:childs) - - if is_list(childs) and length(childs) > 0 do - winner = get_best_candidate(rbundle, childs, box) - new_track = [root] ++ track - find_best_subtree(rbundle, winner, leaf, new_track) - else - if is_atom(childs), do: track, else: [root] ++ track - end - end - - defp get_best_candidate(rbundle, candidates, box) do - win_entry = - candidates - |> Enum.reduce_while(%{id: :not_id, cost: :infinity}, fn c, acc -> - cbox = rbundle.tree |> rbundle[:type].get(c) |> Utils.tuple_value(:bbox) - - if Utils.contained?(cbox, box) do - {:halt, %{id: c, cost: 0}} - else - enlargement = Utils.enlargement_area(cbox, box) - - if enlargement < acc |> Map.get(:cost) do - {:cont, %{id: c, cost: enlargement}} - else - {:cont, acc} - end - end - end) - - win_entry[:id] - end - - ## Query - - defp find_match_leaves(rbundle, box, dig, leaves, flood) do - f = hd(dig) - tail = if length(dig) > 1, do: tl(dig), else: [] - {content, _dad, fbox} = rbundle.tree |> rbundle[:type].get(f) - - {new_dig, new_leaves, new_flood} = - if Utils.overlap?(fbox, box) do - if is_atom(content) do - {tail, [f] ++ leaves, flood} - else - if Utils.contained?(box, fbox), - do: {tail, leaves, [f] ++ flood}, - else: {content ++ tail, leaves, flood} - end - else - {tail, leaves, flood} - end - - if length(new_dig) > 0 do - find_match_leaves(rbundle, box, new_dig, new_leaves, new_flood) - else - new_leaves ++ explore_flood(rbundle, new_flood) - end - end - - defp explore_flood(rbundle, flood) do - next_floor = - flood - |> Enum.flat_map(fn x -> - case rbundle.tree |> rbundle[:type].get(x) |> Utils.tuple_value(:childs) do - :leaf -> [] - any -> any - end - end) - - if length(next_floor) > 0, do: explore_flood(rbundle, next_floor), else: flood - end - - defp find_match_depth(rbundle, box, dig, leaves, depth) do - {f, cdepth} = hd(dig) - tail = if length(dig) > 1, do: tl(dig), else: [] - {content, _dad, fbox} = rbundle.tree |> rbundle[:type].get(f) - - {new_dig, new_leaves} = - if Utils.overlap?(fbox, box) do - if cdepth < depth and is_list(content) do - childs = content |> Enum.map(fn c -> {c, cdepth + 1} end) - {childs ++ tail, leaves} - else - {tail, [f] ++ leaves} - end - else - {tail, leaves} - end - - if length(new_dig) > 0, - do: find_match_depth(rbundle, box, new_dig, new_leaves, depth), - else: new_leaves - end - - ## Delete - - defp remove(rbundle, id) do - {_ch, parent, removed_bbox} = rbundle.tree |> rbundle[:type].get(id) - - if parent do - tree_updated = - rbundle.tree - |> rbundle[:type].delete(id) - |> rbundle[:type].update!(parent, fn {ch, daddy, b} -> {ch -- [id], daddy, b} end) - - parent_childs = tree_updated |> rbundle[:type].get(parent) |> elem(0) - - if length(parent_childs) > 0 do - %{rbundle | tree: tree_updated} |> recursive_update(parent, removed_bbox, :deletion) - else - remove(%{rbundle | tree: tree_updated}, parent) - end - else - rbundle.tree - |> rbundle[:type].update!(id, fn {ch, daddy, _b} -> {ch, daddy, [{0, 0}, {0, 0}]} end) - end - end - - ## Hard update - - defp update(rbundle, id, {old_box, new_box}) do - parent = rbundle.tree |> rbundle[:type].get(id) |> Utils.tuple_value(:dad) - parent_box = rbundle.tree |> rbundle[:type].get(parent) |> Utils.tuple_value(:bbox) - - updated_tree = - rbundle.tree |> rbundle[:type].update!(id, fn {ch, d, _b} -> {ch, d, new_box} end) - - local_rbundle = %{rbundle | tree: updated_tree} - - if Utils.contained?(parent_box, new_box) do - if Utils.in_border?(parent_box, old_box) do - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - color(195) <> - "Update" <> - cyan() <> - "] Good case: new box " <> - yellow() <> - "(#{new_box |> Kernel.inspect()})" <> - cyan() <> - " of " <> - yellow() <> - "[#{id}]" <> - cyan() <> - " reduce the parent " <> yellow() <> "(['#{parent}'])" <> cyan() <> " box" - ) - - local_rbundle |> recursive_update(parent, old_box, :deletion) - else - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - color(195) <> - "Update" <> - cyan() <> - "] Best case: new box " <> - yellow() <> - "(#{new_box |> Kernel.inspect()})" <> - cyan() <> - " of " <> - yellow() <> - "[#{id}]" <> - cyan() <> " was contained by his parent " <> yellow() <> "(['#{parent}'])" - ) - - local_rbundle.tree - end - else - case local_rbundle - |> node_brothers(parent) - |> (fn b -> good_slot?(local_rbundle, b, new_box) end).() do - {new_parent, _new_brothers, _new_parent_box} -> - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - color(195) <> - "Update" <> - cyan() <> - "] Neutral case: new box " <> - yellow() <> - "(#{new_box |> Kernel.inspect()})" <> - cyan() <> - " of " <> - yellow() <> - "[#{id}]" <> - cyan() <> - " increases the parent box but there is an available slot at one uncle " <> - yellow() <> "(['#{new_parent}'])" - ) - - triple_s(local_rbundle, parent, new_parent, {id, old_box}) - - nil -> - if Utils.area(parent_box) >= @max_area do - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - color(195) <> - "Update" <> - cyan() <> - "] Worst case: new box " <> - yellow() <> - "(#{new_box |> Kernel.inspect()})" <> - cyan() <> - " of " <> - yellow() <> - "[#{id}]" <> - cyan() <> - " increases the parent box which was so big " <> - yellow() <> - "#{((Utils.area(parent_box) |> Kernel.trunc()) / @max_area * 100) |> Kernel.trunc()} %. " <> - cyan() <> - "So we proceed to delete " <> - yellow() <> "[#{id}]" <> cyan() <> " and reinsert at tree" - ) - - local_rbundle |> top_down({id, new_box}) - else - if rbundle.verbose, - do: - Logger.debug( - cyan() <> - "[" <> - color(195) <> - "Update" <> - cyan() <> - "] Bad case: new box " <> - yellow() <> - "(#{new_box |> Kernel.inspect()})" <> - cyan() <> - " of " <> - yellow() <> - "[#{id}]" <> - cyan() <> - " increases the parent box which isn't that big yet " <> - yellow() <> - "#{((Utils.area(parent_box) |> Kernel.trunc()) / @max_area * 100) |> Kernel.trunc()} %. " <> - cyan() <> - "So we proceed to increase parent " <> - yellow() <> "(['#{parent}'])" <> cyan() <> " box" - ) - - local_rbundle |> recursive_update(parent, new_box, :insertion) - end - end - end - end - - ## Common updates - - defp top_down(rbundle, {id, box}) do - %{rbundle | tree: rbundle |> remove(id)} |> tree_insert({id, box}) - end - - # Recursive bbox updates when you have node path from root (at insertion) - defp recursive_update(rbundle, path, {_id, box} = leaf, :insertion) when length(path) > 0 do - {modified, t} = update_node_bbox(rbundle, hd(path), box, :insertion) - - if modified and length(path) > 1, - do: recursive_update(%{rbundle | tree: t}, tl(path), leaf, :insertion), - else: rbundle.tree - end - - # Recursive bbox updates when u dont have node path from root, so you have to query parents map... (at delete) - defp recursive_update(rbundle, node, box, mode) when is_list(node) |> Kernel.not() do - {modified, t} = update_node_bbox(rbundle, node, box, mode) - next = rbundle.tree |> rbundle[:type].get(node) |> Utils.tuple_value(:dad) - if modified and next, do: recursive_update(%{rbundle | tree: t}, next, box, mode), else: t - end - - # Typical dumbass safe method - defp recursive_update(rbundle, _path, _leaf, :insertion) do - rbundle.tree - end - - defp update_node_bbox(rbundle, node, the_box, action) do - node_box = rbundle.tree |> rbundle[:type].get(node) |> Utils.tuple_value(:bbox) - - new_bbox = - case action do - :insertion -> - Utils.combine(node_box, the_box) - - :deletion -> - if Utils.in_border?(node_box, the_box) do - rbundle.tree - |> rbundle[:type].get(node) - |> Utils.tuple_value(:childs) - |> Enum.map(fn c -> - rbundle.tree |> rbundle[:type].get(c) |> Utils.tuple_value(:bbox) - end) - |> Utils.combine_multiple() - else - node_box - end - end - - bbox_mutation(rbundle, node, new_bbox, node_box) - end - - defp bbox_mutation(rbundle, node, new_bbox, node_box) do - if new_bbox == node_box do - {false, rbundle.tree} - else - t = rbundle.tree |> rbundle[:type].update!(node, fn {ch, d, _b} -> {ch, d, new_bbox} end) - {true, t} - end - end - - # Return the brothers of the node [{brother_id, brother_childs, brother_box},...] - defp node_brothers(rbundle, node) do - parent = rbundle.tree |> rbundle[:type].get(node) |> Utils.tuple_value(:dad) - - rbundle.tree - |> rbundle[:type].get(parent) - |> Utils.tuple_value(:childs) - |> (fn c -> if c, do: c -- [node], else: [] end).() - |> Enum.map(fn b -> - tuple = rbundle.tree |> rbundle[:type].get(b) - {b, tuple |> Utils.tuple_value(:childs), tuple |> Utils.tuple_value(:bbox)} - end) - end - - # Find a good slot (at bros/brothers list) for the box, it means that the brother hasnt the max childs and the box is at the limits of his own - defp good_slot?(rbundle, bros, box) do - bros - |> Enum.find(fn {_bid, bchilds, bbox} -> - length(bchilds) < rbundle.width and Utils.contained?(bbox, box) - end) - end -end diff --git a/lib/wanderer_app/ddrt/dynamic_rtree_impl/bounding_box_generator.ex b/lib/wanderer_app/ddrt/dynamic_rtree_impl/bounding_box_generator.ex deleted file mode 100755 index 03f24443..00000000 --- a/lib/wanderer_app/ddrt/dynamic_rtree_impl/bounding_box_generator.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule DDRT.DynamicRtreeImpl.BoundingBoxGenerator do - @moduledoc false - - def generate(n, size, result) do - s = size / 2 - x = Enum.random(-180..180) - y = Enum.random(-90..90) - - if n > 0, - do: generate(n - 1, size, [[{x - s, x + s}, {y - s, y + s}]] ++ result), - else: result - end -end diff --git a/lib/wanderer_app/ddrt/dynamic_rtree_impl/node.ex b/lib/wanderer_app/ddrt/dynamic_rtree_impl/node.ex deleted file mode 100755 index 414daf09..00000000 --- a/lib/wanderer_app/ddrt/dynamic_rtree_impl/node.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule DDRT.DynamicRtreeImpl.Node do - @moduledoc false - - def new(gen, seed) do - gen[:next].(seed) - end -end diff --git a/lib/wanderer_app/ddrt/dynamic_rtree_impl/utils.ex b/lib/wanderer_app/ddrt/dynamic_rtree_impl/utils.ex deleted file mode 100755 index 1601f6b3..00000000 --- a/lib/wanderer_app/ddrt/dynamic_rtree_impl/utils.ex +++ /dev/null @@ -1,118 +0,0 @@ -defmodule DDRT.DynamicRtreeImpl.Utils do - @moduledoc false - - def format_bbox([{min_x, max_x} = x, {min_y, max_y} = y]) do - %{ - x: x, - y: y, - xm: min_x, - xM: max_x, - ym: min_y, - yM: max_y - } - end - - def tuple_value(raw, _atom) when raw == nil do - nil - end - - def tuple_value(raw, atom) do - case atom do - :childs -> raw |> elem(0) - :dad -> raw |> elem(1) - :bbox -> raw |> elem(2) - end - end - - # Combine two bounding boxes into one - def combine(box1, box2) do - a = box1 |> format_bbox - b = box2 |> format_bbox - xm = Kernel.min(a.xm, b.xm) - xM = Kernel.max(a.xM, b.xM) - ym = Kernel.min(a.ym, b.ym) - yM = Kernel.max(a.yM, b.yM) - result = [{xm, xM}, {ym, yM}] - result = if area(box1) === 0, do: box2, else: result - if area(box2) === 0, do: box1, else: result - end - - # Combine multiple bbox - def combine_multiple(list) when length(list) > 1 do - real_list = list |> Enum.filter(fn x -> area(x) > 0 end) - - tl(real_list) - |> Enum.reduce(hd(real_list), fn [{a, b}, {c, d}] = _e, [{x, y}, {z, w}] = _acc -> - [{Kernel.min(a, x), Kernel.max(b, y)}, {Kernel.min(c, z), Kernel.max(d, w)}] - end) - end - - def combine_multiple(list) do - hd(list) - end - - # Returns de percent of the overlap area (of the box1) between box1 and box2 - def overlap_area(box1, box2) do - a = box1 |> format_bbox - b = box2 |> format_bbox - x_overlap = Kernel.max(0, Kernel.min(a.xM, b.xM) - Kernel.max(a.xm, b.xm)) - y_overlap = Kernel.max(0, Kernel.min(a.yM, b.yM) - Kernel.max(a.ym, b.ym)) - x_overlap * y_overlap / area(box1) * 100 - end - - # Return if those 2 boxes are overlapping - def overlap?(box1, box2) do - if overlap_area(box1, box2) > 0, do: true, else: false - end - - # Return if box 1 contains box 2 - def contained?(box1, box2) do - a = box1 |> format_bbox - b = box2 |> format_bbox - - a.xm <= b.xm and a.xM >= b.xM and a.ym <= b.ym and a.yM >= b.yM - end - - # Enlargement area after adding new box - def enlargement_area(box, new_box) do - a1 = area(box) - a2 = combine_multiple([box, new_box]) |> area - a2 - a1 - end - - # Checks if box is at some border of parent_box - def in_border?(parent_box, box) do - p = parent_box |> format_bbox - b = box |> format_bbox - - p.xm == b.xm or p.xM == b.xM or p.ym == b.ym or p.yM == b.yM - end - - # Return the area of a bounding box - def area([{a, b}, {c, d}]) do - ab = b - a - cd = d - c - - cond do - ab == 0 and cd != 0 -> cd - ab != 0 and cd == 0 -> ab - ab != 0 and cd != 0 -> ab * cd - ab == 0 and cd == 0 -> -1 - end - end - - # Return the middle bounding box value - def middle_value([{a, b}, {c, d}]) do - (a + b + c + d) / 2 - end - - def get_posxy([{a, b}, {c, d}]) do - %{x: (b + a) / 2, y: (c + d) / 2} - end - - def box_move([{a, b}, {c, d}], move) do - x = move[:x] - y = move[:y] - [{a + x, b + x}, {c + y, d + y}] - end -end diff --git a/lib/wanderer_app/esi/api_client.ex b/lib/wanderer_app/esi/api_client.ex index 33d2e404..98ae6d87 100644 --- a/lib/wanderer_app/esi/api_client.ex +++ b/lib/wanderer_app/esi/api_client.ex @@ -274,7 +274,7 @@ defmodule WandererApp.Esi.ApiClient do ) def get_alliance_info(eve_id, opts \\ []) do case _get_alliance_info(eve_id, "", opts) do - {:ok, result} -> {:ok, result |> Map.merge(%{"eve_id" => eve_id})} + {:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)} {:error, error} -> {:error, error} end end @@ -286,7 +286,7 @@ defmodule WandererApp.Esi.ApiClient do ) def get_corporation_info(eve_id, opts \\ []) do case _get_corporation_info(eve_id, "", opts) do - {:ok, result} -> {:ok, result |> Map.merge(%{"eve_id" => eve_id})} + {:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)} {:error, error} -> {:error, error} end end @@ -301,7 +301,7 @@ defmodule WandererApp.Esi.ApiClient do "/characters/#{eve_id}/", opts |> _with_cache_opts() ) do - {:ok, result} -> {:ok, result |> Map.merge(%{"eve_id" => eve_id})} + {:ok, result} -> {:ok, result |> Map.put("eve_id", eve_id)} {:error, error} -> {:error, error} end end diff --git a/lib/wanderer_app/eve_data_service.ex b/lib/wanderer_app/eve_data_service.ex index dc2ca65f..c82c4663 100644 --- a/lib/wanderer_app/eve_data_service.ex +++ b/lib/wanderer_app/eve_data_service.ex @@ -67,8 +67,8 @@ defmodule WandererApp.EveDataService do end def load_wormhole_types() do - JSONUtil.read_json("#{:code.priv_dir(:wanderer_app)}/repo/data/wormholes.json") - |> JSONUtil.map_json(fn row -> + JSONUtil.read_json!("#{:code.priv_dir(:wanderer_app)}/repo/data/wormholes.json") + |> Enum.map(fn row -> %{ id: row["typeID"], name: row["name"], @@ -85,8 +85,8 @@ defmodule WandererApp.EveDataService do end def load_wormhole_classes() do - JSONUtil.read_json("#{:code.priv_dir(:wanderer_app)}/repo/data/wormholeClasses.json") - |> JSONUtil.map_json(fn row -> + JSONUtil.read_json!("#{:code.priv_dir(:wanderer_app)}/repo/data/wormholeClasses.json") + |> Enum.map(fn row -> %{ id: row["id"], short_name: row["shortName"], @@ -98,8 +98,8 @@ defmodule WandererApp.EveDataService do end def load_wormhole_systems() do - JSONUtil.read_json("#{:code.priv_dir(:wanderer_app)}/repo/data/wormholeSystems.json") - |> JSONUtil.map_json(fn row -> + JSONUtil.read_json!("#{:code.priv_dir(:wanderer_app)}/repo/data/wormholeSystems.json") + |> Enum.map(fn row -> %{ solar_system_id: row["solarSystemID"], wanderers: row["wanderers"], @@ -111,8 +111,8 @@ defmodule WandererApp.EveDataService do end def load_effects() do - JSONUtil.read_json("#{:code.priv_dir(:wanderer_app)}/repo/data/effects.json") - |> JSONUtil.map_json(fn row -> + JSONUtil.read_json!("#{:code.priv_dir(:wanderer_app)}/repo/data/effects.json") + |> Enum.map(fn row -> %{ id: row["name"] |> Slug.slugify(), name: row["name"], @@ -130,8 +130,8 @@ defmodule WandererApp.EveDataService do end def load_triglavian_systems() do - JSONUtil.read_json("#{:code.priv_dir(:wanderer_app)}/repo/data/triglavianSystems.json") - |> JSONUtil.map_json(fn row -> + JSONUtil.read_json!("#{:code.priv_dir(:wanderer_app)}/repo/data/triglavianSystems.json") + |> Enum.map(fn row -> %{ solar_system_id: row["solarSystemID"], solar_system_name: row["solarSystemName"], @@ -377,9 +377,21 @@ defmodule WandererApp.EveDataService do end end - defp get_true_security(security) when is_float(security) and security > 0.0 and security < 0.05, do: security |> Float.ceil(1) + defp truncate_to_two_digits(value) when is_float(value), do: Float.floor(value * 100) / 100 - defp get_true_security(security) when is_float(security), do: security |> Float.floor(1) + defp get_true_security(security) when is_float(security) and security > 0.0 and security < 0.05, + do: security |> Float.ceil(1) + + defp get_true_security(security) when is_float(security) do + truncated_value = security |> truncate_to_two_digits() + floor_value = truncated_value |> Float.floor(1) + + if Float.round(truncated_value - floor_value, 2) < 0.05 do + floor_value + else + Float.ceil(truncated_value, 1) + end + end defp get_class_title(wormhole_classes_info, wormhole_class_id, security, wormhole_class) do case wormhole_class_id in [ diff --git a/lib/wanderer_app/map/map_server_impl.ex b/lib/wanderer_app/map/map_server_impl.ex index 30dc2313..9dd27f39 100644 --- a/lib/wanderer_app/map/map_server_impl.ex +++ b/lib/wanderer_app/map/map_server_impl.ex @@ -376,17 +376,21 @@ defmodule WandererApp.Map.Server.Impl do case not is_nil(user_id) do true -> - :telemetry.execute( - [:wanderer_app, :map, :systems, :remove], - %{count: removed_ids |> Enum.count()}, - %{ + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:systems_removed, %{ character_id: character_id, user_id: user_id, map_id: map_id, solar_system_ids: removed_ids - } + }) + + :telemetry.execute( + [:wanderer_app, :map, :systems, :remove], + %{count: removed_ids |> Enum.count()} ) + :ok + _ -> :ok end @@ -1169,12 +1173,15 @@ defmodule WandererApp.Map.Server.Impl do broadcast!(map_id, :add_system, system) - :telemetry.execute([:wanderer_app, :map, :system, :add], %{count: 1}, %{ - character_id: character_id, - user_id: user_id, - map_id: map_id, - solar_system_id: solar_system_id - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:system_added, %{ + character_id: character_id, + user_id: user_id, + map_id: map_id, + solar_system_id: solar_system_id + }) + + :telemetry.execute([:wanderer_app, :map, :system, :add], %{count: 1}) state end @@ -1592,12 +1599,17 @@ defmodule WandererApp.Map.Server.Impl do :ok _ -> - :telemetry.execute([:wanderer_app, :map, :character, :jump], %{count: 1}, %{ - map_id: map_id, - character: character, - solar_system_source_id: old_location.solar_system_id, - solar_system_target_id: location.solar_system_id - }) + :telemetry.execute([:wanderer_app, :map, :character, :jump], %{count: 1}, %{}) + + {:ok, _} = + WandererApp.Api.MapChainPassages.new(%{ + map_id: map_id, + character_id: character.id, + ship_type_id: character.ship, + ship_name: character.ship_name, + solar_system_source_id: old_location.solar_system_id, + solar_system_target_id: location.solar_system_id + }) end case WandererApp.Map.check_connection(map_id, location, old_location) do diff --git a/lib/wanderer_app/map/map_zkb_data_fetcher.ex b/lib/wanderer_app/map/map_zkb_data_fetcher.ex index d667ff62..caf2cc71 100644 --- a/lib/wanderer_app/map/map_zkb_data_fetcher.ex +++ b/lib/wanderer_app/map/map_zkb_data_fetcher.ex @@ -55,13 +55,7 @@ defmodule WandererApp.Map.ZkbDataFetcher do def handle_info({ref, result}, state) do Process.demonitor(ref, [:flush]) - case result do - :ok -> - {:noreply, state} - - _ -> - {:noreply, state} - end + {:noreply, state} end defp _update_map_kills(map_id) do @@ -70,10 +64,9 @@ defmodule WandererApp.Map.ZkbDataFetcher do map_id |> WandererApp.Map.get_map!() |> Map.get(:systems, Map.new()) - |> Map.keys() - |> Enum.reduce(Map.new(), fn solar_system_id, acc -> + |> Enum.reduce(Map.new(), fn {solar_system_id, _system}, acc -> kills_count = WandererApp.Cache.get("zkb_kills_#{solar_system_id}") - acc |> Map.put_new(solar_system_id, kills_count || 0) + acc |> Map.put(solar_system_id, kills_count || 0) end) |> _maybe_broadcast_map_kills(map_id) @@ -87,28 +80,24 @@ defmodule WandererApp.Map.ZkbDataFetcher do updated_kills_system_ids = new_kills_map - |> Map.keys() - |> Enum.filter(fn solar_system_id -> - kills_count = new_kills_map |> Map.get(solar_system_id, 0) - old_kills_count = old_kills_map |> Map.get(solar_system_id, 0) - - kills_count != old_kills_count and - kills_count > 0 - end) - - removed_kills_system_ids = - old_kills_map - |> Map.keys() - |> Enum.filter(fn solar_system_id -> - new_kills_count = new_kills_map |> Map.get(solar_system_id, 0) + |> Map.filter(fn {solar_system_id, new_kills_count} -> old_kills_count = old_kills_map |> Map.get(solar_system_id, 0) new_kills_count != old_kills_count and - old_kills_count > 0 and new_kills_count == 0 + new_kills_count > 0 end) + |> Map.keys() - [updated_kills_system_ids | removed_kills_system_ids] - |> List.flatten() + removed_kills_system_ids = + old_kills_map + |> Map.filter(fn {solar_system_id, old_kills_count} -> + new_kills_count = new_kills_map |> Map.get(solar_system_id, 0) + + old_kills_count > 0 and new_kills_count == 0 + end) + |> Map.keys() + + (updated_kills_system_ids ++ removed_kills_system_ids) |> case do [] -> :ok diff --git a/lib/wanderer_app/repositories/map_connection_repo.ex b/lib/wanderer_app/repositories/map_connection_repo.ex index 5365ba86..2c5fb69d 100644 --- a/lib/wanderer_app/repositories/map_connection_repo.ex +++ b/lib/wanderer_app/repositories/map_connection_repo.ex @@ -9,7 +9,11 @@ defmodule WandererApp.MapConnectionRepo do do: WandererApp.Api.MapConnection.read_by_map(%{map_id: map_id}) def get_by_locations(map_id, solar_system_source, solar_system_target) do - WandererApp.Api.MapConnection.by_locations(%{map_id: map_id, solar_system_source: solar_system_source, solar_system_target: solar_system_target}) + WandererApp.Api.MapConnection.by_locations(%{ + map_id: map_id, + solar_system_source: solar_system_source, + solar_system_target: solar_system_target + }) |> case do {:ok, connections} -> {:ok, connections} @@ -26,8 +30,11 @@ defmodule WandererApp.MapConnectionRepo do def create!(connection), do: connection |> WandererApp.Api.MapConnection.create!() def destroy(map_id, connection) do - {:ok, from_connections} = get_by_locations(map_id, connection.solar_system_source, connection.solar_system_target) - {:ok, to_connections} = get_by_locations(map_id, connection.solar_system_target, connection.solar_system_source) + {:ok, from_connections} = + get_by_locations(map_id, connection.solar_system_source, connection.solar_system_target) + + {:ok, to_connections} = + get_by_locations(map_id, connection.solar_system_target, connection.solar_system_source) [from_connections ++ to_connections] |> List.flatten() @@ -42,8 +49,7 @@ defmodule WandererApp.MapConnectionRepo do end end - def destroy!(connection), do: - connection |> WandererApp.Api.MapConnection.destroy!() + def destroy!(connection), do: connection |> WandererApp.Api.MapConnection.destroy!() def bulk_destroy!(connections) do connections @@ -51,6 +57,7 @@ defmodule WandererApp.MapConnectionRepo do |> case do %Ash.BulkResult{status: :success} -> :ok + error -> error end diff --git a/lib/wanderer_app/task_wrapper.ex b/lib/wanderer_app/task_wrapper.ex new file mode 100644 index 00000000..8f4a0220 --- /dev/null +++ b/lib/wanderer_app/task_wrapper.ex @@ -0,0 +1,9 @@ +defmodule WandererApp.TaskWrapper do + def start_link(module, func, args) do + if Mix.env() == :test do + apply(module, func, args) + else + Task.start_link(module, func, args) + end + end +end diff --git a/lib/wanderer_app/user/user_activity_tracker.ex b/lib/wanderer_app/user/user_activity_tracker.ex index d3e9a2c3..4e855294 100644 --- a/lib/wanderer_app/user/user_activity_tracker.ex +++ b/lib/wanderer_app/user/user_activity_tracker.ex @@ -1,114 +1,16 @@ defmodule WandererApp.User.ActivityTracker do @moduledoc false - use GenServer - require Logger - @name __MODULE__ + def track_map_event( + event_type, + metadata + ), + do: WandererApp.Map.Audit.track_map_event(event_type, metadata) - def start_link(args) do - GenServer.start(__MODULE__, args, name: @name) - end - - @impl true - def init(_args) do - Logger.info("#{__MODULE__} started") - - {:ok, %{}, {:continue, :start}} - end - - @impl true - def handle_continue(:start, state) do - :telemetry.attach_many( - "map_user_activity", - [ - [:wanderer_app, :map, :hub, :add], - [:wanderer_app, :map, :hub, :remove], - [:wanderer_app, :map, :system, :add], - [:wanderer_app, :map, :system, :update], - [:wanderer_app, :map, :systems, :remove], - [:wanderer_app, :map, :connection, :add], - [:wanderer_app, :map, :connection, :update], - [:wanderer_app, :map, :connection, :remove], - [:wanderer_app, :map, :acl, :add], - [:wanderer_app, :map, :acl, :remove], - [:wanderer_app, :acl, :member, :add], - [:wanderer_app, :acl, :member, :remove], - [:wanderer_app, :acl, :member, :update] - ], - &handle_event/4, - nil - ) - - {:noreply, state} - end - - def handle_event([:wanderer_app, :map, :system, :add], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:system_added, metadata) - end - - def handle_event([:wanderer_app, :map, :hub, :add], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:hub_added, metadata) - end - - def handle_event([:wanderer_app, :map, :hub, :remove], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:hub_removed, metadata) - end - - def handle_event([:wanderer_app, :map, :system, :update], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:system_updated, metadata) - end - - def handle_event([:wanderer_app, :map, :systems, :remove], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:systems_removed, metadata) - end - - def handle_event([:wanderer_app, :map, :connection, :add], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:map_connection_added, metadata) - end - - def handle_event([:wanderer_app, :map, :connection, :update], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:map_connection_updated, metadata) - end - - def handle_event([:wanderer_app, :map, :connection, :remove], _event_data, metadata, _config) do - {:ok, _} = _track_map_event(:map_connection_removed, metadata) - end - - def handle_event([:wanderer_app, :acl, :member, :add], _event_data, metadata, _config) do - {:ok, _} = _track_acl_event(:map_acl_member_added, metadata) - end - - def handle_event([:wanderer_app, :acl, :member, :remove], _event_data, metadata, _config) do - {:ok, _} = _track_acl_event(:map_acl_member_removed, metadata) - end - - def handle_event([:wanderer_app, :acl, :member, :update], _event_data, metadata, _config) do - {:ok, _} = _track_acl_event(:map_acl_member_updated, metadata) - end - - def handle_event([:wanderer_app, :map, :acl, :add], _event_data, _metadata, _config) do - # {:ok, _} = _track_map_event(:map_acl_added, metadata) - end - - def handle_event([:wanderer_app, :map, :acl, :remove], _event_data, _metadata, _config) do - # {:ok, _} = _track_map_event(:map_acl_removed, metadata) - end - - defp _track_map_event( - event_type, - metadata - ), - do: WandererApp.Map.Audit.track_map_event(event_type, metadata) - - defp _track_acl_event( - event_type, - metadata - ), - do: WandererApp.Map.Audit.track_acl_event(event_type, metadata) - - @impl true - def terminate(_reason, _state) do - :ok - end + def track_acl_event( + event_type, + metadata + ), + do: WandererApp.Map.Audit.track_acl_event(event_type, metadata) end diff --git a/lib/wanderer_app/utils/JSONUtil.ex b/lib/wanderer_app/utils/JSONUtil.ex index dd83afbf..ccdefcb1 100644 --- a/lib/wanderer_app/utils/JSONUtil.ex +++ b/lib/wanderer_app/utils/JSONUtil.ex @@ -6,14 +6,9 @@ defmodule WandererApp.Utils.JSONUtil do Jason.decode(body) end - def map_json({:ok, json}, mapper) do - Enum.map(json, mapper) - end - - def compress(data) do - data - |> Jason.encode!() - |> :zlib.compress() - |> Base.encode64() - end + def read_json!(filename), + do: + filename + |> File.read!() + |> Jason.decode!() end diff --git a/lib/wanderer_app/zkb/zkb_kills_provider.ex b/lib/wanderer_app/zkb/zkb_kills_provider.ex index 697db9a1..2af342dc 100644 --- a/lib/wanderer_app/zkb/zkb_kills_provider.ex +++ b/lib/wanderer_app/zkb/zkb_kills_provider.ex @@ -63,7 +63,7 @@ defmodule WandererApp.Zkb.KillsProvider do end defp handle_websocket(message, state) do - case message |> _parse_message() do + case message |> parse_message() do nil -> {:ok, state} @@ -109,7 +109,7 @@ defmodule WandererApp.Zkb.KillsProvider do Logger.warning(fn -> "Terminating client process with reason : #{inspect(reason)}" end) end - defp _parse_message( + defp parse_message( %{ "solar_system_id" => solar_system_id, "killmail_time" => killmail_time @@ -123,5 +123,5 @@ defmodule WandererApp.Zkb.KillsProvider do } end - defp _parse_message(_message), do: nil + defp parse_message(_message), do: nil end diff --git a/lib/wanderer_app_web/components/core_components.ex b/lib/wanderer_app_web/components/core_components.ex index 8a08131c..45b2b82e 100644 --- a/lib/wanderer_app_web/components/core_components.ex +++ b/lib/wanderer_app_web/components/core_components.ex @@ -605,9 +605,7 @@ defmodule WandererAppWeb.CoreComponents do phx-click={@row_click && @row_click.(row)} class={"hover #{if @row_selected && @row_selected.(row), do: "!bg-slate-600", else: ""} #{if @row_click, do: "cursor-pointer", else: ""}"} > - + <%= render_slot(col, @row_item.(row)) %> diff --git a/lib/wanderer_app_web/controllers/basic_auth.ex b/lib/wanderer_app_web/controllers/basic_auth.ex index 09e0ed3e..5274a8fe 100755 --- a/lib/wanderer_app_web/controllers/basic_auth.ex +++ b/lib/wanderer_app_web/controllers/basic_auth.ex @@ -3,11 +3,15 @@ defmodule WandererAppWeb.BasicAuth do def admin_basic_auth(conn, _opts) do admin_password = WandererApp.Env.admin_password() + if is_nil(admin_password) do conn else conn - |> Plug.BasicAuth.basic_auth(username: WandererApp.Env.admin_username(), password: admin_password) + |> Plug.BasicAuth.basic_auth( + username: WandererApp.Env.admin_username(), + password: admin_password + ) end end end diff --git a/lib/wanderer_app_web/live/access_lists/access_lists_live.ex b/lib/wanderer_app_web/live/access_lists/access_lists_live.ex index 4b019390..289f60c0 100755 --- a/lib/wanderer_app_web/live/access_lists/access_lists_live.ex +++ b/lib/wanderer_app_web/live/access_lists/access_lists_live.ex @@ -99,8 +99,9 @@ defmodule WandererAppWeb.AccessListsLive do end defp apply_action(socket, :add_members, %{"id" => acl_id} = _params) do - with {:ok, %{owner: %{id: _character_id}} = access_list} <- socket.assigns.access_lists |> Enum.find(&(&1.id == acl_id)) |> Ash.load(:owner), - user_character_ids <- socket.assigns.current_user.characters |> Enum.map(& &1.id) do + with {:ok, %{owner: %{id: _character_id}} = access_list} <- + socket.assigns.access_lists |> Enum.find(&(&1.id == acl_id)) |> Ash.load(:owner), + user_character_ids <- socket.assigns.current_user.characters |> Enum.map(& &1.id) do user_character_ids |> Enum.each(fn user_character_id -> :ok = WandererApp.Character.TrackerManager.start_tracking(user_character_id) @@ -125,7 +126,7 @@ defmodule WandererAppWeb.AccessListsLive do ) else _ -> - socket + socket end end @@ -373,12 +374,16 @@ defmodule WandererAppWeb.AccessListsLive do member |> WandererApp.Api.AccessListMember.update_role!(%{role: role_atom}) - :telemetry.execute([:wanderer_app, :acl, :member, :update], %{count: 1}, %{ - user_id: socket.assigns.current_user.id, - acl_id: socket.assigns.selected_acl_id, - member: - member |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_acl_event(:map_acl_member_updated, %{ + user_id: socket.assigns.current_user.id, + acl_id: socket.assigns.selected_acl_id, + member: + member + |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) + }) + + :telemetry.execute([:wanderer_app, :acl, :member, :update], %{count: 1}) Phoenix.PubSub.broadcast( WandererApp.PubSub, @@ -488,12 +493,16 @@ defmodule WandererAppWeb.AccessListsLive do eve_corporation_id: nil }) do {:ok, member} -> - :telemetry.execute([:wanderer_app, :acl, :member, :add], %{count: 1}, %{ - user_id: socket.assigns.current_user.id, - acl_id: access_list_id, - member: - member |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_acl_event(:map_acl_member_added, %{ + user_id: socket.assigns.current_user.id, + acl_id: access_list_id, + member: + member + |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) + }) + + :telemetry.execute([:wanderer_app, :acl, :member, :add], %{count: 1}) {:ok, member} @@ -515,12 +524,16 @@ defmodule WandererAppWeb.AccessListsLive do eve_corporation_id: eve_id }) do {:ok, member} -> - :telemetry.execute([:wanderer_app, :acl, :member, :add], %{count: 1}, %{ - user_id: socket.assigns.current_user.id, - acl_id: access_list_id, - member: - member |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_acl_event(:map_acl_member_added, %{ + user_id: socket.assigns.current_user.id, + acl_id: access_list_id, + member: + member + |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) + }) + + :telemetry.execute([:wanderer_app, :acl, :member, :add], %{count: 1}) {:ok, member} @@ -543,12 +556,16 @@ defmodule WandererAppWeb.AccessListsLive do role: :viewer }) do {:ok, member} -> - :telemetry.execute([:wanderer_app, :acl, :member, :add], %{count: 1}, %{ - user_id: socket.assigns.current_user.id, - acl_id: access_list_id, - member: - member |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_acl_event(:map_acl_member_added, %{ + user_id: socket.assigns.current_user.id, + acl_id: access_list_id, + member: + member + |> Map.take([:eve_character_id, :eve_corporation_id, :eve_alliance_id, :role]) + }) + + :telemetry.execute([:wanderer_app, :acl, :member, :add], %{count: 1}) {:ok, member} @@ -647,6 +664,6 @@ defmodule WandererAppWeb.AccessListsLive do end defp map_ui_acl(acl, selected_id) do - acl |> Map.merge(%{selected: acl.id == selected_id}) + acl |> Map.put(:selected, acl.id == selected_id) end end diff --git a/lib/wanderer_app_web/live/characters/characters_tracking_live.ex b/lib/wanderer_app_web/live/characters/characters_tracking_live.ex index 01d57068..046572c6 100755 --- a/lib/wanderer_app_web/live/characters/characters_tracking_live.ex +++ b/lib/wanderer_app_web/live/characters/characters_tracking_live.ex @@ -44,6 +44,7 @@ defmodule WandererAppWeb.CharactersTrackingLive do case WandererApp.Api.MapCharacterSettings.read_by_map(%{map_id: selected_map.id}) do {:ok, settings} -> {:ok, settings} + _ -> {:ok, []} end diff --git a/lib/wanderer_app_web/live/maps/map_live.ex b/lib/wanderer_app_web/live/maps/map_live.ex index 951f4476..0f6e9832 100644 --- a/lib/wanderer_app_web/live/maps/map_live.ex +++ b/lib/wanderer_app_web/live/maps/map_live.ex @@ -44,7 +44,6 @@ defmodule WandererAppWeb.MapLive do id: map_id, deleted: false } = map} -> - Process.send_after(self(), {:init_map, map}, 10) socket @@ -130,28 +129,28 @@ defmodule WandererAppWeb.MapLive do def handle_info(%{event: :add_system, payload: system}, socket) do {:noreply, socket - |> _push_map_event("add_systems", [map_ui_system(system)])} + |> push_map_event("add_systems", [map_ui_system(system)])} end @impl true def handle_info(%{event: :update_system, payload: system}, socket) do {:noreply, socket - |> _push_map_event("update_systems", [map_ui_system(system)])} + |> push_map_event("update_systems", [map_ui_system(system)])} end @impl true def handle_info(%{event: :update_connection, payload: connection}, socket) do {:noreply, socket - |> _push_map_event("update_connection", map_ui_connection(connection))} + |> push_map_event("update_connection", map_ui_connection(connection))} end @impl true def handle_info(%{event: :systems_removed, payload: solar_system_ids}, socket) do {:noreply, socket - |> _push_map_event("remove_systems", solar_system_ids)} + |> push_map_event("remove_systems", solar_system_ids)} end @impl true @@ -160,7 +159,7 @@ defmodule WandererAppWeb.MapLive do {:noreply, socket - |> _push_map_event( + |> push_map_event( "remove_connections", connection_ids )} @@ -172,7 +171,7 @@ defmodule WandererAppWeb.MapLive do {:noreply, socket - |> _push_map_event( + |> push_map_event( "add_connections", connections )} @@ -182,7 +181,7 @@ defmodule WandererAppWeb.MapLive do def handle_info(%{event: :update_map, payload: map_diff}, socket) do {:noreply, socket - |> _push_map_event( + |> push_map_event( "map_updated", map_diff )} @@ -196,7 +195,7 @@ defmodule WandererAppWeb.MapLive do {:noreply, socket - |> _push_map_event( + |> push_map_event( "kills_updated", kills )} @@ -218,7 +217,7 @@ defmodule WandererAppWeb.MapLive do {:noreply, socket - |> _push_map_event( + |> push_map_event( "characters_updated", characters )} @@ -228,7 +227,7 @@ defmodule WandererAppWeb.MapLive do def handle_info(%{event: :character_added, payload: character}, socket) do {:noreply, socket - |> _push_map_event( + |> push_map_event( "character_added", character |> map_ui_character() )} @@ -238,7 +237,7 @@ defmodule WandererAppWeb.MapLive do def handle_info(%{event: :character_removed, payload: character}, socket) do {:noreply, socket - |> _push_map_event( + |> push_map_event( "character_removed", character |> map_ui_character() )} @@ -248,7 +247,7 @@ defmodule WandererAppWeb.MapLive do def handle_info(%{event: :character_updated, payload: character}, socket) do {:noreply, socket - |> _push_map_event( + |> push_map_event( "character_updated", character |> map_ui_character() )} @@ -262,7 +261,7 @@ defmodule WandererAppWeb.MapLive do do: {:noreply, socket - |> _push_map_event( + |> push_map_event( "present_characters", present_character_eve_ids )} @@ -336,7 +335,7 @@ defmodule WandererAppWeb.MapLive do socket |> assign(user_permissions: user_permissions) - |> _push_map_event( + |> push_map_event( "user_permissions", user_permissions ) @@ -376,17 +375,24 @@ defmodule WandererAppWeb.MapLive do tracked_character_ids = availaible_map_characters |> Enum.filter(& &1.tracked) |> Enum.map(& &1.id) - all_character_tracked? = (not (availaible_map_characters |> Enum.empty?())) and availaible_map_characters |> Enum.all?(& &1.tracked) + all_character_tracked? = + not (availaible_map_characters |> Enum.empty?()) and + availaible_map_characters |> Enum.all?(& &1.tracked) cond do (only_tracked_characters and can_track? and all_character_tracked?) or (not only_tracked_characters and can_view?) -> - Process.send_after(self(), {:map_init, %{ - map_id: map_id, - page_title: map_name, - user_permissions: user_permissions, - tracked_character_ids: tracked_character_ids - }}, 10) + Process.send_after( + self(), + {:map_init, + %{ + map_id: map_id, + page_title: map_name, + user_permissions: user_permissions, + tracked_character_ids: tracked_character_ids + }}, + 10 + ) only_tracked_characters and can_track? and not all_character_tracked? -> Process.send_after(self(), :not_all_characters_tracked, 10) @@ -406,17 +412,20 @@ defmodule WandererAppWeb.MapLive do Phoenix.PubSub.subscribe(WandererApp.PubSub, map_id) {:noreply, - socket - |> assign(initial_data)} + socket + |> assign(initial_data)} end - def handle_info({:map_start, - %{ - map_id: map_id, - user_characters: user_character_eve_ids, - initial_data: initial_data, - events: events - } = _started_data}, socket) do + def handle_info( + {:map_start, + %{ + map_id: map_id, + user_characters: user_character_eve_ids, + initial_data: initial_data, + events: events + } = _started_data}, + socket + ) do socket = events |> Enum.reduce(socket, fn event, socket -> @@ -452,66 +461,78 @@ defmodule WandererAppWeb.MapLive do end end) - Process.send_after(self(), {:map_loaded, - %{ - map_id: map_id, - user_characters: user_character_eve_ids, - initial_data: initial_data - }}, 10) + Process.send_after( + self(), + {:map_loaded, + %{ + map_id: map_id, + user_characters: user_character_eve_ids, + initial_data: initial_data + }}, + 10 + ) {:noreply, socket} end - def handle_info({:map_loaded, - %{ - map_id: map_id, - user_characters: user_character_eve_ids, - initial_data: initial_data - } = _loaded_data}, socket) do + def handle_info( + {:map_loaded, + %{ + map_id: map_id, + user_characters: user_character_eve_ids, + initial_data: initial_data + } = _loaded_data}, + socket + ) do map_characters = map_id |> WandererApp.Map.list_characters() {:noreply, - socket - |> assign( - map_loaded?: true, - user_characters: user_character_eve_ids, - has_tracked_characters?: _has_tracked_characters?(user_character_eve_ids) - ) - |> _push_map_event("init", initial_data |> Map.merge(%{ - characters: map_characters |> Enum.map(&map_ui_character/1) - })) - |> push_event("js-exec", %{ - to: "#map-loader", - attr: "data-loaded" - })} + socket + |> assign( + map_loaded?: true, + user_characters: user_character_eve_ids, + has_tracked_characters?: _has_tracked_characters?(user_character_eve_ids) + ) + |> push_map_event( + "init", + initial_data |> Map.put(:characters, map_characters |> Enum.map(&map_ui_character/1)) + ) + |> push_event("js-exec", %{ + to: "#map-loader", + attr: "data-loaded" + })} end - def handle_info(:no_access, socket), do: - {:noreply, - socket - |> put_flash(:error, "You don't have an access to this map.") - |> push_navigate(to: ~p"/maps")} + def handle_info(:no_access, socket), + do: + {:noreply, + socket + |> put_flash(:error, "You don't have an access to this map.") + |> push_navigate(to: ~p"/maps")} - def handle_info(:no_permissions, socket), do: - {:noreply, - socket - |> put_flash(:error, "You don't have permissions to use this map.") - |> push_navigate(to: ~p"/maps")} + def handle_info(:no_permissions, socket), + do: + {:noreply, + socket + |> put_flash(:error, "You don't have permissions to use this map.") + |> push_navigate(to: ~p"/maps")} - def handle_info(:not_all_characters_tracked, socket), do: - {:noreply, - socket - |> put_flash( - :error, - "You should enable tracking for all characters that have access to this map first!" - ) - |> push_navigate(to: ~p"/tracking/#{socket.assigns.map_slug}")} + def handle_info(:not_all_characters_tracked, socket), + do: + {:noreply, + socket + |> put_flash( + :error, + "You should enable tracking for all characters that have access to this map first!" + ) + |> push_navigate(to: ~p"/tracking/#{socket.assigns.map_slug}")} @impl true def handle_info( {ref, result}, socket - ) when is_reference(ref) do + ) + when is_reference(ref) do Process.demonitor(ref, [:flush]) case result do @@ -539,7 +560,7 @@ defmodule WandererAppWeb.MapLive do {:routes, {solar_system_id, %{routes: routes, systems_static_data: systems_static_data}}} -> {:noreply, socket - |> _push_map_event( + |> push_map_event( "routes", %{ solar_system_id: solar_system_id, @@ -571,12 +592,11 @@ defmodule WandererAppWeb.MapLive do end @impl true - def handle_event("reconnected", _body, socket) do - {:noreply, socket} - end - - @impl true - def handle_event("change_map", %{"map_slug" => map_slug} = _event, %{assigns: %{map_id: map_id}} = socket) do + def handle_event( + "change_map", + %{"map_slug" => map_slug} = _event, + %{assigns: %{map_id: map_id}} = socket + ) do Phoenix.PubSub.unsubscribe(WandererApp.PubSub, map_id) {:noreply, push_navigate(socket, to: ~p"/#{map_slug}")} end @@ -637,13 +657,16 @@ defmodule WandererAppWeb.MapLive do solar_system_target_id: solar_system_target_id |> String.to_integer() }) - :telemetry.execute([:wanderer_app, :map, :connection, :add], %{count: 1}, %{ - character_id: tracked_character_ids |> List.first(), - user_id: current_user.id, - map_id: map_id, - solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(), - solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer() - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:map_connection_added, %{ + character_id: tracked_character_ids |> List.first(), + user_id: current_user.id, + map_id: map_id, + solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(), + solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer() + }) + + :telemetry.execute([:wanderer_app, :map, :connection, :add], %{count: 1}) {:noreply, socket} @@ -682,18 +705,21 @@ defmodule WandererAppWeb.MapLive do socket |> map_id() - :telemetry.execute([:wanderer_app, :map, :hub, :add], %{count: 1}, %{ - character_id: tracked_character_ids |> List.first(), - user_id: current_user.id, - map_id: map_id, - solar_system_id: solar_system_id - }) - map_id |> WandererApp.Map.Server.add_hub(%{ solar_system_id: solar_system_id }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:hub_added, %{ + character_id: tracked_character_ids |> List.first(), + user_id: current_user.id, + map_id: map_id, + solar_system_id: solar_system_id + }) + + :telemetry.execute([:wanderer_app, :map, :hub, :add], %{count: 1}) + {:noreply, socket} _ -> @@ -731,18 +757,21 @@ defmodule WandererAppWeb.MapLive do socket |> map_id() - :telemetry.execute([:wanderer_app, :map, :hub, :remove], %{count: 1}, %{ - character_id: tracked_character_ids |> List.first(), - user_id: current_user.id, - map_id: map_id, - solar_system_id: solar_system_id - }) - map_id |> WandererApp.Map.Server.remove_hub(%{ solar_system_id: solar_system_id }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:hub_removed, %{ + character_id: tracked_character_ids |> List.first(), + user_id: current_user.id, + map_id: map_id, + solar_system_id: solar_system_id + }) + + :telemetry.execute([:wanderer_app, :map, :hub, :remove], %{count: 1}) + {:noreply, socket} _ -> @@ -802,15 +831,6 @@ defmodule WandererAppWeb.MapLive do socket |> map_id() - :telemetry.execute([:wanderer_app, :map, :system, :update], %{count: 1}, %{ - character_id: tracked_character_ids |> List.first(), - user_id: current_user.id, - map_id: map_id, - solar_system_id: "#{solar_system_id}" |> String.to_integer(), - key: key_atom, - value: value - }) - apply(WandererApp.Map.Server, method_atom, [ map_id, %{ @@ -819,6 +839,18 @@ defmodule WandererAppWeb.MapLive do |> Map.put_new(key_atom, value) ]) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:system_updated, %{ + character_id: tracked_character_ids |> List.first(), + user_id: current_user.id, + map_id: map_id, + solar_system_id: "#{solar_system_id}" |> String.to_integer(), + key: key_atom, + value: value + }) + + :telemetry.execute([:wanderer_app, :map, :system, :update], %{count: 1}) + {:noreply, socket} _ -> @@ -878,15 +910,18 @@ defmodule WandererAppWeb.MapLive do socket |> map_id() - :telemetry.execute([:wanderer_app, :map, :connection, :update], %{count: 1}, %{ - character_id: tracked_character_ids |> List.first(), - user_id: current_user.id, - map_id: map_id, - solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(), - solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer(), - key: key_atom, - value: value - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:map_connection_updated, %{ + character_id: tracked_character_ids |> List.first(), + user_id: current_user.id, + map_id: map_id, + solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(), + solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer(), + key: key_atom, + value: value + }) + + :telemetry.execute([:wanderer_app, :map, :connection, :update], %{count: 1}) apply(WandererApp.Map.Server, method_atom, [ map_id, @@ -1230,13 +1265,16 @@ defmodule WandererAppWeb.MapLive do solar_system_target_id: solar_system_target_id |> String.to_integer() }) - :telemetry.execute([:wanderer_app, :map, :connection, :remove], %{count: 1}, %{ - character_id: tracked_character_ids |> List.first(), - user_id: current_user.id, - map_id: map_id, - solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(), - solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer() - }) + {:ok, _} = + WandererApp.User.ActivityTracker.track_map_event(:map_connection_removed, %{ + character_id: tracked_character_ids |> List.first(), + user_id: current_user.id, + map_id: map_id, + solar_system_source_id: "#{solar_system_source_id}" |> String.to_integer(), + solar_system_target_id: "#{solar_system_target_id}" |> String.to_integer() + }) + + :telemetry.execute([:wanderer_app, :map, :connection, :remove], %{count: 1}) {:noreply, socket} @@ -1424,7 +1462,7 @@ defmodule WandererAppWeb.MapLive do |> assign_async(:characters, fn -> {:ok, %{characters: characters}} end) - |> _push_map_event( + |> push_map_event( "init", %{ user_characters: user_character_eve_ids, @@ -1564,16 +1602,20 @@ defmodule WandererAppWeb.MapLive do ) |> Map.put(:reset, true) - Process.send_after(self(), {:map_start, %{ - map_id: map_id, - user_characters: user_character_eve_ids, - initial_data: initial_data, - events: events - }}, 10) + Process.send_after( + self(), + {:map_start, + %{ + map_id: map_id, + user_characters: user_character_eve_ids, + initial_data: initial_data, + events: events + }}, + 10 + ) _ -> Process.send_after(self(), :no_access, 10) - end end @@ -2079,14 +2121,13 @@ defmodule WandererAppWeb.MapLive do :ok = WandererApp.Character.TrackerManager.start_tracking(character_id) end - defp _push_map_event(socket, type, event_body) - do + defp push_map_event(socket, type, body), + do: socket |> push_event("map_event", %{ type: type, - body: event_body |> WandererApp.Utils.JSONUtil.compress() + body: body }) - end defp map_id(%{assigns: %{map_id: map_id}} = _socket), do: map_id end diff --git a/lib/wanderer_app_web/live/maps/map_live.html.heex b/lib/wanderer_app_web/live/maps/map_live.html.heex index 3c18c26d..46947976 100644 --- a/lib/wanderer_app_web/live/maps/map_live.html.heex +++ b/lib/wanderer_app_web/live/maps/map_live.html.heex @@ -13,8 +13,6 @@ - -
diff --git a/lib/wanderer_app_web/live/maps/maps_live.ex b/lib/wanderer_app_web/live/maps/maps_live.ex index 2e641b79..4ff504c9 100644 --- a/lib/wanderer_app_web/live/maps/maps_live.ex +++ b/lib/wanderer_app_web/live/maps/maps_live.ex @@ -608,20 +608,12 @@ defmodule WandererAppWeb.MapsLive do added_acls |> Enum.each(fn acl_id -> - :telemetry.execute([:wanderer_app, :map, :acl, :add], %{count: 1}, %{ - user_id: current_user.id, - map_id: map.id, - acl_id: acl_id - }) + :telemetry.execute([:wanderer_app, :map, :acl, :add], %{count: 1}) end) removed_acls |> Enum.each(fn acl_id -> - :telemetry.execute([:wanderer_app, :map, :acl, :remove], %{count: 1}, %{ - user_id: current_user.id, - map_id: map.id, - acl_id: acl_id - }) + :telemetry.execute([:wanderer_app, :map, :acl, :remove], %{count: 1}) end) Phoenix.PubSub.broadcast( @@ -934,6 +926,6 @@ defmodule WandererAppWeb.MapsLive do defp map_map(%{acls: acls} = map) do map - |> Map.merge(%{acls: acls |> Enum.map(&map_acl/1)}) + |> Map.put(:acls, acls |> Enum.map(&map_acl/1)) end end diff --git a/lib/wanderer_app_web/router.ex b/lib/wanderer_app_web/router.ex index 7c9bc53b..5f0270ab 100644 --- a/lib/wanderer_app_web/router.ex +++ b/lib/wanderer_app_web/router.ex @@ -200,8 +200,6 @@ defmodule WandererAppWeb.Router do end end - - # Enable LiveDashboard and Swoosh mailbox preview in development if Application.compile_env(:wanderer_app, :dev_routes) do # If you want to use the LiveDashboard in production, you should put diff --git a/mix.exs b/mix.exs index 96a5d47a..b28c636e 100644 --- a/mix.exs +++ b/mix.exs @@ -100,7 +100,7 @@ defmodule WandererApp.MixProject do {:makeup_elixir, ">= 0.0.0"}, {:makeup_erlang, ">= 0.0.0"}, {:better_number, "~> 1.0.0"}, - {:delta_crdt, "~> 0.6.5"}, + {:delta_crdt, "~> 0.6.5", override: true}, {:qex, "~> 0.5"}, {:site_encrypt, "~> 0.6.0"}, {:bandit, "~> 1.0"}, @@ -111,7 +111,8 @@ defmodule WandererApp.MixProject do {:mox, "~> 1.1", only: [:test, :integration]}, {:git_ops, "~> 2.6.1"}, {:version_tasks, "~> 0.12.0"}, - {:error_tracker, "~> 0.2"} + {:error_tracker, "~> 0.2"}, + {:ddrt, "~> 0.2.1"} ] end diff --git a/mix.lock b/mix.lock index 1c60b23c..4f1fd9a7 100644 --- a/mix.lock +++ b/mix.lock @@ -18,6 +18,7 @@ "crontab": {:hex, :crontab, "1.1.13", "3bad04f050b9f7f1c237809e42223999c150656a6b2afbbfef597d56df2144c5", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "d67441bec989640e3afb94e123f45a2bc42d76e02988c9613885dc3d01cf7085"}, "dart_sass": {:hex, :dart_sass, "0.5.1", "d45f20a8e324313689fb83287d4702352793ce8c9644bc254155d12656ade8b6", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "24f8a1c67e8b5267c51a33cbe6c0b5ebf12c2c83ace88b5ac04947d676b4ec81"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, + "ddrt": {:hex, :ddrt, "0.2.1", "c4e4bddcef36add5de6599ec72ec822699932413ece0ad310e4be4ab2b3ab6d3", [:mix], [{:delta_crdt, "~> 0.5.0", [hex: :delta_crdt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:merkle_map, "~> 0.2.0", [hex: :merkle_map, repo: "hexpm", optional: false]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "1efcd60cf4ca4a4352e752d7f41ed9d696560e5860ee07d5bf31c16950100365"}, "debounce_and_throttle": {:hex, :debounce_and_throttle, "0.9.0", "fa86c982963e00365cc9808afa496e82ca2b48f8905c6c79f8edd304800d0892", [:mix], [], "hexpm", "573a7cff4032754023d8e6874f3eff5354864c90b39b692f1fc4a44b3eb7517b"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"},