This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

fix(toast-load): infinite load of adding a card on a collection page

Adding a card from a collection page shows 'Adding card...' forever. The collection page's `<Suspense>` boundary re-suspends on query invalidation, unmounting the component before React Query can fire the mutate-level `onSuccess`/`onError` callbacks that update the toast.
Moved toast handling from mutate-level callbacks into definition-level callbacks on `useMutation`, which always fire regardless of component mount state.

+107 -266
+7
DEVELOPERS.md
··· 14 14 - `fly logs --app annos` 15 15 - `fly secrets set SUPER_SECRET_KEY=password1234` 16 16 - make sure that new environment vars are added as secrets with the above command 17 + 18 + ## local development with mocked backend 19 + 20 + - `npm install` 21 + - in one terminal: `npm run dev:mock` 22 + - another terminal: `npm run webapp:dev` 23 + - ui is available on `localhost:4000`
+12 -150
package-lock.json
··· 7784 7784 "version": "2.5.1", 7785 7785 "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", 7786 7786 "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", 7787 - "dev": true, 7787 + "devOptional": true, 7788 7788 "hasInstallScript": true, 7789 7789 "dependencies": { 7790 7790 "detect-libc": "^1.0.3", ··· 7822 7822 "cpu": [ 7823 7823 "arm64" 7824 7824 ], 7825 - "dev": true, 7826 7825 "optional": true, 7827 7826 "os": [ 7828 7827 "android" 7829 7828 ], 7830 - "peer": true, 7831 7829 "engines": { 7832 7830 "node": ">= 10.0.0" 7833 7831 }, ··· 7843 7841 "cpu": [ 7844 7842 "arm64" 7845 7843 ], 7846 - "dev": true, 7847 7844 "optional": true, 7848 7845 "os": [ 7849 7846 "darwin" 7850 7847 ], 7851 - "peer": true, 7852 7848 "engines": { 7853 7849 "node": ">= 10.0.0" 7854 7850 }, ··· 7864 7860 "cpu": [ 7865 7861 "x64" 7866 7862 ], 7867 - "dev": true, 7868 7863 "optional": true, 7869 7864 "os": [ 7870 7865 "darwin" 7871 7866 ], 7872 - "peer": true, 7873 7867 "engines": { 7874 7868 "node": ">= 10.0.0" 7875 7869 }, ··· 7885 7879 "cpu": [ 7886 7880 "x64" 7887 7881 ], 7888 - "dev": true, 7889 7882 "optional": true, 7890 7883 "os": [ 7891 7884 "freebsd" 7892 7885 ], 7893 - "peer": true, 7894 7886 "engines": { 7895 7887 "node": ">= 10.0.0" 7896 7888 }, ··· 7906 7898 "cpu": [ 7907 7899 "arm" 7908 7900 ], 7909 - "dev": true, 7910 7901 "optional": true, 7911 7902 "os": [ 7912 7903 "linux" 7913 7904 ], 7914 - "peer": true, 7915 7905 "engines": { 7916 7906 "node": ">= 10.0.0" 7917 7907 }, ··· 7927 7917 "cpu": [ 7928 7918 "arm" 7929 7919 ], 7930 - "dev": true, 7931 7920 "optional": true, 7932 7921 "os": [ 7933 7922 "linux" 7934 7923 ], 7935 - "peer": true, 7936 7924 "engines": { 7937 7925 "node": ">= 10.0.0" 7938 7926 }, ··· 7948 7936 "cpu": [ 7949 7937 "arm64" 7950 7938 ], 7951 - "dev": true, 7952 7939 "optional": true, 7953 7940 "os": [ 7954 7941 "linux" 7955 7942 ], 7956 - "peer": true, 7957 7943 "engines": { 7958 7944 "node": ">= 10.0.0" 7959 7945 }, ··· 7969 7955 "cpu": [ 7970 7956 "arm64" 7971 7957 ], 7972 - "dev": true, 7973 7958 "optional": true, 7974 7959 "os": [ 7975 7960 "linux" 7976 7961 ], 7977 - "peer": true, 7978 7962 "engines": { 7979 7963 "node": ">= 10.0.0" 7980 7964 }, ··· 7990 7974 "cpu": [ 7991 7975 "x64" 7992 7976 ], 7993 - "dev": true, 7994 7977 "optional": true, 7995 7978 "os": [ 7996 7979 "linux" 7997 7980 ], 7998 - "peer": true, 7999 7981 "engines": { 8000 7982 "node": ">= 10.0.0" 8001 7983 }, ··· 8011 7993 "cpu": [ 8012 7994 "x64" 8013 7995 ], 8014 - "dev": true, 8015 7996 "optional": true, 8016 7997 "os": [ 8017 7998 "linux" 8018 7999 ], 8019 - "peer": true, 8020 8000 "engines": { 8021 8001 "node": ">= 10.0.0" 8022 8002 }, ··· 8032 8012 "cpu": [ 8033 8013 "arm64" 8034 8014 ], 8035 - "dev": true, 8036 8015 "optional": true, 8037 8016 "os": [ 8038 8017 "win32" 8039 8018 ], 8040 - "peer": true, 8041 8019 "engines": { 8042 8020 "node": ">= 10.0.0" 8043 8021 }, ··· 8053 8031 "cpu": [ 8054 8032 "ia32" 8055 8033 ], 8056 - "dev": true, 8057 8034 "optional": true, 8058 8035 "os": [ 8059 8036 "win32" 8060 8037 ], 8061 - "peer": true, 8062 8038 "engines": { 8063 8039 "node": ">= 10.0.0" 8064 8040 }, ··· 8074 8050 "cpu": [ 8075 8051 "x64" 8076 8052 ], 8077 - "dev": true, 8078 8053 "optional": true, 8079 8054 "os": [ 8080 8055 "win32" 8081 8056 ], 8082 - "peer": true, 8083 8057 "engines": { 8084 8058 "node": ">= 10.0.0" 8085 8059 }, ··· 8092 8066 "version": "1.0.3", 8093 8067 "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 8094 8068 "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", 8095 - "dev": true, 8069 + "devOptional": true, 8096 8070 "bin": { 8097 8071 "detect-libc": "bin/detect-libc.js" 8098 8072 }, ··· 11192 11166 "os": [ 11193 11167 "darwin" 11194 11168 ], 11195 - "peer": true, 11196 11169 "engines": { 11197 11170 "node": ">=10" 11198 11171 } ··· 11209 11182 "os": [ 11210 11183 "darwin" 11211 11184 ], 11212 - "peer": true, 11213 11185 "engines": { 11214 11186 "node": ">=10" 11215 11187 } ··· 11226 11198 "os": [ 11227 11199 "linux" 11228 11200 ], 11229 - "peer": true, 11230 11201 "engines": { 11231 11202 "node": ">=10" 11232 11203 } ··· 11243 11214 "os": [ 11244 11215 "linux" 11245 11216 ], 11246 - "peer": true, 11247 11217 "engines": { 11248 11218 "node": ">=10" 11249 11219 } ··· 11260 11230 "os": [ 11261 11231 "linux" 11262 11232 ], 11263 - "peer": true, 11264 11233 "engines": { 11265 11234 "node": ">=10" 11266 11235 } ··· 11277 11246 "os": [ 11278 11247 "linux" 11279 11248 ], 11280 - "peer": true, 11281 11249 "engines": { 11282 11250 "node": ">=10" 11283 11251 } ··· 11294 11262 "os": [ 11295 11263 "linux" 11296 11264 ], 11297 - "peer": true, 11298 11265 "engines": { 11299 11266 "node": ">=10" 11300 11267 } ··· 11311 11278 "os": [ 11312 11279 "win32" 11313 11280 ], 11314 - "peer": true, 11315 11281 "engines": { 11316 11282 "node": ">=10" 11317 11283 } ··· 11328 11294 "os": [ 11329 11295 "win32" 11330 11296 ], 11331 - "peer": true, 11332 11297 "engines": { 11333 11298 "node": ">=10" 11334 11299 } ··· 11345 11310 "os": [ 11346 11311 "win32" 11347 11312 ], 11348 - "peer": true, 11349 11313 "engines": { 11350 11314 "node": ">=10" 11351 11315 } ··· 14099 14063 "version": "3.0.3", 14100 14064 "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 14101 14065 "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 14102 - "dev": true, 14066 + "devOptional": true, 14103 14067 "dependencies": { 14104 14068 "fill-range": "^7.1.1" 14105 14069 }, ··· 15532 15496 "node": ">=4" 15533 15497 } 15534 15498 }, 15535 - "node_modules/csso": { 15536 - "version": "5.0.5", 15537 - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", 15538 - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", 15539 - "dev": true, 15540 - "license": "MIT", 15541 - "optional": true, 15542 - "peer": true, 15543 - "dependencies": { 15544 - "css-tree": "~2.2.0" 15545 - }, 15546 - "engines": { 15547 - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", 15548 - "npm": ">=7.0.0" 15549 - } 15550 - }, 15551 - "node_modules/csso/node_modules/css-tree": { 15552 - "version": "2.2.1", 15553 - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", 15554 - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", 15555 - "dev": true, 15556 - "license": "MIT", 15557 - "optional": true, 15558 - "peer": true, 15559 - "dependencies": { 15560 - "mdn-data": "2.0.28", 15561 - "source-map-js": "^1.0.1" 15562 - }, 15563 - "engines": { 15564 - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", 15565 - "npm": ">=7.0.0" 15566 - } 15567 - }, 15568 - "node_modules/csso/node_modules/mdn-data": { 15569 - "version": "2.0.28", 15570 - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", 15571 - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", 15572 - "dev": true, 15573 - "license": "CC0-1.0", 15574 - "optional": true, 15575 - "peer": true 15576 - }, 15577 15499 "node_modules/cssstyle": { 15578 15500 "version": "4.6.0", 15579 15501 "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", ··· 16704 16626 "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", 16705 16627 "dev": true, 16706 16628 "optional": true, 16707 - "peer": true, 16708 16629 "dependencies": { 16709 16630 "prr": "~1.0.1" 16710 16631 }, ··· 18124 18045 "version": "7.1.1", 18125 18046 "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 18126 18047 "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 18127 - "dev": true, 18048 + "devOptional": true, 18128 18049 "dependencies": { 18129 18050 "to-regex-range": "^5.0.1" 18130 18051 }, ··· 19203 19124 "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", 19204 19125 "dev": true, 19205 19126 "optional": true, 19206 - "peer": true, 19207 19127 "bin": { 19208 19128 "image-size": "bin/image-size.js" 19209 19129 }, ··· 19629 19549 "version": "2.1.1", 19630 19550 "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 19631 19551 "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 19632 - "dev": true, 19552 + "devOptional": true, 19633 19553 "engines": { 19634 19554 "node": ">=0.10.0" 19635 19555 } ··· 19696 19616 "version": "4.0.3", 19697 19617 "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 19698 19618 "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 19699 - "dev": true, 19619 + "devOptional": true, 19700 19620 "dependencies": { 19701 19621 "is-extglob": "^2.1.1" 19702 19622 }, ··· 19747 19667 "version": "7.0.0", 19748 19668 "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 19749 19669 "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 19750 - "dev": true, 19670 + "devOptional": true, 19751 19671 "engines": { 19752 19672 "node": ">=0.12.0" 19753 19673 } ··· 21183 21103 "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", 21184 21104 "dev": true, 21185 21105 "optional": true, 21186 - "peer": true, 21187 21106 "dependencies": { 21188 21107 "pify": "^4.0.1", 21189 21108 "semver": "^5.6.0" ··· 21198 21117 "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 21199 21118 "dev": true, 21200 21119 "optional": true, 21201 - "peer": true, 21202 21120 "bin": { 21203 21121 "mime": "cli.js" 21204 21122 }, ··· 21212 21130 "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", 21213 21131 "dev": true, 21214 21132 "optional": true, 21215 - "peer": true, 21216 21133 "bin": { 21217 21134 "semver": "bin/semver" 21218 21135 } ··· 21223 21140 "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 21224 21141 "dev": true, 21225 21142 "optional": true, 21226 - "peer": true, 21227 21143 "engines": { 21228 21144 "node": ">=0.10.0" 21229 21145 } ··· 21291 21207 "os": [ 21292 21208 "android" 21293 21209 ], 21294 - "peer": true, 21295 21210 "engines": { 21296 21211 "node": ">= 12.0.0" 21297 21212 }, ··· 21312 21227 "os": [ 21313 21228 "darwin" 21314 21229 ], 21315 - "peer": true, 21316 21230 "engines": { 21317 21231 "node": ">= 12.0.0" 21318 21232 }, ··· 21333 21247 "os": [ 21334 21248 "darwin" 21335 21249 ], 21336 - "peer": true, 21337 21250 "engines": { 21338 21251 "node": ">= 12.0.0" 21339 21252 }, ··· 21354 21267 "os": [ 21355 21268 "freebsd" 21356 21269 ], 21357 - "peer": true, 21358 21270 "engines": { 21359 21271 "node": ">= 12.0.0" 21360 21272 }, ··· 21375 21287 "os": [ 21376 21288 "linux" 21377 21289 ], 21378 - "peer": true, 21379 21290 "engines": { 21380 21291 "node": ">= 12.0.0" 21381 21292 }, ··· 21396 21307 "os": [ 21397 21308 "linux" 21398 21309 ], 21399 - "peer": true, 21400 21310 "engines": { 21401 21311 "node": ">= 12.0.0" 21402 21312 }, ··· 21417 21327 "os": [ 21418 21328 "linux" 21419 21329 ], 21420 - "peer": true, 21421 21330 "engines": { 21422 21331 "node": ">= 12.0.0" 21423 21332 }, ··· 21438 21347 "os": [ 21439 21348 "linux" 21440 21349 ], 21441 - "peer": true, 21442 21350 "engines": { 21443 21351 "node": ">= 12.0.0" 21444 21352 }, ··· 21459 21367 "os": [ 21460 21368 "linux" 21461 21369 ], 21462 - "peer": true, 21463 21370 "engines": { 21464 21371 "node": ">= 12.0.0" 21465 21372 }, ··· 21480 21387 "os": [ 21481 21388 "win32" 21482 21389 ], 21483 - "peer": true, 21484 21390 "engines": { 21485 21391 "node": ">= 12.0.0" 21486 21392 }, ··· 21501 21407 "os": [ 21502 21408 "win32" 21503 21409 ], 21504 - "peer": true, 21505 21410 "engines": { 21506 21411 "node": ">= 12.0.0" 21507 21412 }, ··· 22674 22579 "version": "4.0.8", 22675 22580 "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", 22676 22581 "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", 22677 - "dev": true, 22582 + "devOptional": true, 22678 22583 "dependencies": { 22679 22584 "braces": "^3.0.3", 22680 22585 "picomatch": "^2.3.1" ··· 22687 22592 "version": "2.3.2", 22688 22593 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", 22689 22594 "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", 22690 - "dev": true, 22595 + "devOptional": true, 22691 22596 "license": "MIT", 22692 22597 "engines": { 22693 22598 "node": ">=8.6" ··· 22970 22875 "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", 22971 22876 "dev": true, 22972 22877 "optional": true, 22973 - "peer": true, 22974 22878 "dependencies": { 22975 22879 "iconv-lite": "^0.6.3", 22976 22880 "sax": "^1.2.4" ··· 23318 23222 "version": "7.1.1", 23319 23223 "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", 23320 23224 "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", 23321 - "dev": true 23225 + "devOptional": true 23322 23226 }, 23323 23227 "node_modules/node-fetch": { 23324 23228 "version": "2.7.0", ··· 24318 24222 "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", 24319 24223 "dev": true, 24320 24224 "optional": true, 24321 - "peer": true, 24322 24225 "engines": { 24323 24226 "node": ">=6" 24324 24227 } ··· 25600 25503 "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", 25601 25504 "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", 25602 25505 "dev": true, 25603 - "optional": true, 25604 - "peer": true 25506 + "optional": true 25605 25507 }, 25606 25508 "node_modules/pstree.remy": { 25607 25509 "version": "1.1.8", ··· 28007 27909 "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", 28008 27910 "dev": true 28009 27911 }, 28010 - "node_modules/svgo": { 28011 - "version": "3.3.3", 28012 - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", 28013 - "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", 28014 - "dev": true, 28015 - "license": "MIT", 28016 - "optional": true, 28017 - "peer": true, 28018 - "dependencies": { 28019 - "commander": "^7.2.0", 28020 - "css-select": "^5.1.0", 28021 - "css-tree": "^2.3.1", 28022 - "css-what": "^6.1.0", 28023 - "csso": "^5.0.5", 28024 - "picocolors": "^1.0.0", 28025 - "sax": "^1.5.0" 28026 - }, 28027 - "bin": { 28028 - "svgo": "bin/svgo" 28029 - }, 28030 - "engines": { 28031 - "node": ">=14.0.0" 28032 - }, 28033 - "funding": { 28034 - "type": "opencollective", 28035 - "url": "https://opencollective.com/svgo" 28036 - } 28037 - }, 28038 - "node_modules/svgo/node_modules/commander": { 28039 - "version": "7.2.0", 28040 - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", 28041 - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", 28042 - "dev": true, 28043 - "license": "MIT", 28044 - "optional": true, 28045 - "peer": true, 28046 - "engines": { 28047 - "node": ">= 10" 28048 - } 28049 - }, 28050 27912 "node_modules/symbol-tree": { 28051 27913 "version": "3.2.4", 28052 27914 "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", ··· 28453 28315 "version": "5.0.1", 28454 28316 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 28455 28317 "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 28456 - "dev": true, 28318 + "devOptional": true, 28457 28319 "dependencies": { 28458 28320 "is-number": "^7.0.0" 28459 28321 },
+15 -3
src/modules/cards/infrastructure/IFramelyMetadataService.ts
··· 37 37 38 38 export class IFramelyMetadataService implements IMetadataService { 39 39 private readonly baseUrl = 'https://iframe.ly/api/iframely'; 40 - private readonly apiKey: string; 40 + private readonly apiKey: string | null; 41 41 42 42 constructor(apiKey: string) { 43 43 if (!apiKey || apiKey.trim().length === 0) { 44 - throw new Error('Iframely API key is required'); 44 + console.warn( 45 + 'IFramelyMetadataService: No API key provided. Metadata fetching will be unavailable.', 46 + ); 47 + this.apiKey = null; 48 + } else { 49 + this.apiKey = apiKey; 45 50 } 46 - this.apiKey = apiKey; 47 51 } 48 52 49 53 async fetchMetadata(url: URL): Promise<Result<UrlMetadata>> { 54 + if (!this.apiKey) { 55 + return err(new Error('Iframely API key not configured')); 56 + } 57 + 50 58 try { 51 59 const encodedUrl = encodeURIComponent(url.value); 52 60 const fullUrl = `${this.baseUrl}?url=${encodedUrl}&api_key=${this.apiKey}`; ··· 108 116 } 109 117 110 118 async isAvailable(): Promise<boolean> { 119 + if (!this.apiKey) { 120 + return false; 121 + } 122 + 111 123 try { 112 124 // Test with a simple URL to check if the service is available 113 125 const testUrl = `${this.baseUrl}?url=${encodeURIComponent('https://example.com')}&api_key=${this.apiKey}`;
+1 -28
src/webapp/features/cards/components/addCardDrawer/AddCardForm.tsx
··· 33 33 import { FaSeedling } from 'react-icons/fa6'; 34 34 import { CardSaveSource } from '@/features/analytics/types'; 35 35 import { usePathname } from 'next/navigation'; 36 - import { BsCheck, BsExclamation } from 'react-icons/bs'; 37 36 38 37 interface Props { 39 38 onClose: () => void; ··· 112 111 setSelectedCollections(initialCollections); 113 112 form.reset(); 114 113 115 - addCard.mutate(cardData, { 116 - onSuccess: () => { 117 - notifications.update({ 118 - id: notificationId, 119 - color: 'green', 120 - title: 'Success!', 121 - message: 'Card added', 122 - position: 'top-center', 123 - loading: false, 124 - autoClose: 2000, 125 - icon: <BsCheck />, 126 - }); 127 - }, 128 - onError: () => { 129 - notifications.update({ 130 - id: notificationId, 131 - color: 'red', 132 - title: 'Error', 133 - message: 'Could not add card', 134 - position: 'top-center', 135 - loading: false, 136 - autoClose: 5000, 137 - withCloseButton: true, 138 - icon: <BsExclamation />, 139 - }); 140 - }, 141 - }); 114 + addCard.mutate({ ...cardData, notificationId }); 142 115 }; 143 116 144 117 return (
+2 -55
src/webapp/features/cards/components/addCardToModal/AddCardToModal.tsx
··· 10 10 import useUpdateCardAssociations from '@/features/cards/lib/mutations/useUpdateCardAssociations'; 11 11 import { notifications } from '@mantine/notifications'; 12 12 import { track } from '@vercel/analytics'; 13 - import { BsCheck, BsExclamation } from 'react-icons/bs'; 14 13 15 14 interface Props { 16 15 isOpen: boolean; ··· 78 77 79 78 props.onClose(); 80 79 81 - addCard.mutate(data.cardData, { 82 - onSuccess: () => { 83 - notifications.update({ 84 - id: notificationId, 85 - color: 'green', 86 - title: 'Success!', 87 - message: 'Card added', 88 - position: 'top-center', 89 - loading: false, 90 - autoClose: 2000, 91 - icon: <BsCheck />, 92 - }); 93 - }, 94 - onError: () => { 95 - notifications.update({ 96 - id: notificationId, 97 - color: 'red', 98 - title: 'Error', 99 - message: 'Could not add card', 100 - loading: false, 101 - autoClose: 5000, 102 - withCloseButton: true, 103 - position: 'top-center', 104 - icon: <BsExclamation />, 105 - }); 106 - }, 107 - }); 80 + addCard.mutate({ ...data.cardData, notificationId }); 108 81 } else if (!data.isAddingNewCard && data.updateData) { 109 82 const notificationId = `update-card-${Date.now()}`; 110 83 notifications.show({ ··· 119 92 120 93 props.onClose(); 121 94 122 - updateCardAssociations.mutate(data.updateData, { 123 - onSuccess: () => { 124 - notifications.update({ 125 - id: notificationId, 126 - color: 'green', 127 - title: 'Success!', 128 - message: 'Card updated', 129 - position: 'top-center', 130 - loading: false, 131 - autoClose: 2000, 132 - icon: <BsCheck />, 133 - }); 134 - }, 135 - onError: () => { 136 - notifications.update({ 137 - id: notificationId, 138 - color: 'red', 139 - title: 'Error', 140 - message: 'Could not update card', 141 - position: 'top-center', 142 - loading: false, 143 - autoClose: false, 144 - withCloseButton: true, 145 - icon: <BsExclamation />, 146 - }); 147 - }, 148 - }); 95 + updateCardAssociations.mutate({ ...data.updateData, notificationId }); 149 96 } 150 97 }; 151 98
+36 -2
src/webapp/features/cards/lib/mutations/useAddCard.tsx
··· 11 11 CardSaveEventProperties, 12 12 } from '@/features/analytics/types'; 13 13 import { shouldCaptureAnalytics } from '@/features/analytics/utils'; 14 + import { notifications } from '@mantine/notifications'; 15 + import { BsCheck, BsExclamation } from 'react-icons/bs'; 14 16 15 17 export default function useAddCard( 16 18 analyticsContext?: CardSaveAnalyticsContext, ··· 23 25 note?: string; 24 26 collectionIds?: string[]; 25 27 viaCardId?: string; 28 + notificationId?: string; 26 29 }) => { 27 30 return addUrlToLibrary(newCard.url, { 28 31 note: newCard.note, ··· 31 34 }); 32 35 }, 33 36 34 - // Do things that are absolutely necessary and logic related (like query invalidation) in the useMutation callbacks 35 - // Do UI related things like redirects or showing toast notifications in mutate callbacks. If the user navigated away from the current screen before the mutation finished, those will purposefully not fire 37 + // Generally, do UI things (redirects, toasts) in mutate-level callbacks so they 38 + // don't fire if the user navigated away. But loading toasts that need .update() 39 + // must be handled here — Suspense re-renders can unmount the caller and drop 40 + // mutate-level callbacks, leaving the loading toast stuck forever. 36 41 // https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire 37 42 onSuccess: (_data, variables) => { 43 + if (variables.notificationId) { 44 + notifications.update({ 45 + id: variables.notificationId, 46 + color: 'green', 47 + title: 'Success!', 48 + message: 'Card added', 49 + position: 'top-center', 50 + loading: false, 51 + autoClose: 2000, 52 + icon: <BsCheck />, 53 + }); 54 + } 55 + 38 56 queryClient.invalidateQueries({ queryKey: cardKeys.all() }); 39 57 queryClient.invalidateQueries({ queryKey: noteKeys.all() }); 40 58 queryClient.invalidateQueries({ queryKey: feedKeys.all() }); ··· 84 102 // Clear super properties after capture 85 103 posthog.unregister('original_save_source'); 86 104 posthog.unregister('original_active_filters'); 105 + } 106 + }, 107 + 108 + onError: (_error, variables) => { 109 + if (variables.notificationId) { 110 + notifications.update({ 111 + id: variables.notificationId, 112 + color: 'red', 113 + title: 'Error', 114 + message: 'Could not add card', 115 + position: 'top-center', 116 + loading: false, 117 + autoClose: 5000, 118 + withCloseButton: true, 119 + icon: <BsExclamation />, 120 + }); 87 121 } 88 122 }, 89 123 });
+32
src/webapp/features/cards/lib/mutations/useUpdateCardAssociations.tsx
··· 11 11 CardSaveEventProperties, 12 12 } from '@/features/analytics/types'; 13 13 import { shouldCaptureAnalytics } from '@/features/analytics/utils'; 14 + import { notifications } from '@mantine/notifications'; 15 + import { BsCheck, BsExclamation } from 'react-icons/bs'; 14 16 15 17 export default function useUpdateCardAssociations( 16 18 analyticsContext?: CardSaveAnalyticsContext, ··· 27 29 removeFromCollectionIds?: string[]; 28 30 viaCardId?: string; 29 31 addToLibrary?: boolean; 32 + notificationId?: string; 30 33 }) => { 31 34 return client.updateUrlCardAssociations({ 32 35 cardId: updatedCard.cardId, ··· 38 41 }, 39 42 40 43 onSuccess: (_data, variables) => { 44 + if (variables.notificationId) { 45 + notifications.update({ 46 + id: variables.notificationId, 47 + color: 'green', 48 + title: 'Success!', 49 + message: 'Card updated', 50 + position: 'top-center', 51 + loading: false, 52 + autoClose: 2000, 53 + icon: <BsCheck />, 54 + }); 55 + } 56 + 41 57 queryClient.invalidateQueries({ queryKey: cardKeys.all() }); 42 58 queryClient.invalidateQueries({ queryKey: noteKeys.all() }); 43 59 queryClient.invalidateQueries({ queryKey: feedKeys.all() }); ··· 99 115 // Clear super properties after capture 100 116 posthog.unregister('original_save_source'); 101 117 posthog.unregister('original_active_filters'); 118 + } 119 + }, 120 + 121 + onError: (_error, variables) => { 122 + if (variables.notificationId) { 123 + notifications.update({ 124 + id: variables.notificationId, 125 + color: 'red', 126 + title: 'Error', 127 + message: 'Could not update card', 128 + position: 'top-center', 129 + loading: false, 130 + autoClose: false, 131 + withCloseButton: true, 132 + icon: <BsExclamation />, 133 + }); 102 134 } 103 135 }, 104 136 });
+2 -28
src/webapp/features/composer/components/Composer.tsx
··· 38 38 import { FaRegNoteSticky } from 'react-icons/fa6'; 39 39 import { CardSaveSource } from '@/features/analytics/types'; 40 40 import { usePathname } from 'next/navigation'; 41 - import { BsCheck, BsExclamation } from 'react-icons/bs'; 41 + import { BsExclamation } from 'react-icons/bs'; 42 42 import AddConnectionForm from '@/features/connections/components/addConnectionDrawer/AddConnectionForm'; 43 43 import { TbPlugConnected } from 'react-icons/tb'; 44 44 ··· 156 156 window.history.replaceState({}, '', window.location.pathname); 157 157 cardForm.reset(); 158 158 159 - addCard.mutate(cardData, { 160 - onSuccess: () => { 161 - notifications.update({ 162 - id: notificationId, 163 - color: 'green', 164 - title: 'Success!', 165 - message: 'Card added', 166 - position: 'top-center', 167 - loading: false, 168 - autoClose: 2000, 169 - icon: <BsCheck />, 170 - }); 171 - }, 172 - onError: () => { 173 - notifications.update({ 174 - id: notificationId, 175 - color: 'red', 176 - title: 'Error', 177 - message: 'Could not add card', 178 - position: 'top-center', 179 - loading: false, 180 - autoClose: 5000, 181 - withCloseButton: true, 182 - icon: <BsExclamation />, 183 - }); 184 - }, 185 - }); 159 + addCard.mutate({ ...cardData, notificationId }); 186 160 }; 187 161 188 162 const handleCreateCollection = (e: React.FormEvent) => {