diff --git a/package.json b/package.json index 576559a8c..2c3a995dc 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "format": "prettier --ignore-path .prettierignore -w ." }, "dependencies": { - "@cashu/cashu-ts": "2.0.0-rc1", + "@cashu/cashu-ts": "^2.1.0", "@chakra-ui/anatomy": "^2.3.4", "@chakra-ui/breakpoint-utils": "^2.0.8", "@chakra-ui/icons": "^2.2.4", @@ -93,7 +93,7 @@ "react-error-boundary": "^4.1.2", "react-force-graph-2d": "^1.26.1", "react-force-graph-3d": "^1.25.1", - "react-hook-form": "^7.54.0", + "react-hook-form": "^7.54.1", "react-markdown": "^9.0.1", "react-mosaic-component": "^6.1.0", "react-photo-album": "^2.4.1", @@ -109,7 +109,7 @@ "rx-nostr": "^3.4.1", "rxjs": "^7.8.1", "three": "^0.170.0", - "three-spritetext": "^1.9.2", + "three-spritetext": "^1.9.3", "three-stdlib": "^2.35.2", "tiny-lru": "^11.2.11", "unified": "^11.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07a9395f7..6a656cacc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,8 +13,8 @@ importers: .: dependencies: '@cashu/cashu-ts': - specifier: 2.0.0-rc1 - version: 2.0.0-rc1 + specifier: ^2.1.0 + version: 2.1.0 '@chakra-ui/anatomy': specifier: ^2.3.4 version: 2.3.4 @@ -92,28 +92,28 @@ importers: version: 4.9.2(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) applesauce-channel: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-content: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-core: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-factory: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-lists: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-net: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-react: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) applesauce-signer: specifier: next - version: 0.0.0-next-20241212192616(typescript@5.7.2) + version: 0.0.0-next-20241213171848(typescript@5.7.2) bech32: specifier: ^2.0.0 version: 2.0.0 @@ -244,8 +244,8 @@ importers: specifier: ^1.25.1 version: 1.25.1(react@18.3.1) react-hook-form: - specifier: ^7.54.0 - version: 7.54.0(react@18.3.1) + specifier: ^7.54.1 + version: 7.54.1(react@18.3.1) react-markdown: specifier: ^9.0.1 version: 9.0.1(@types/react@18.3.16)(react@18.3.1) @@ -292,8 +292,8 @@ importers: specifier: ^0.170.0 version: 0.170.0 three-spritetext: - specifier: ^1.9.2 - version: 1.9.2(three@0.170.0) + specifier: ^1.9.3 + version: 1.9.3(three@0.170.0) three-stdlib: specifier: ^2.35.2 version: 2.35.2(three@0.170.0) @@ -403,8 +403,8 @@ importers: packages: - 3d-force-graph@1.74.2: - resolution: {integrity: sha512-XBC1KB0vDvG616qfy11jE6pwp4SDQ4tBVof0GSHztjV1fVp7rhP5IGhwnbSjyp6FKAw+PjsWL/0tZuAcGFbXsA==} + 3d-force-graph@1.74.3: + resolution: {integrity: sha512-srzOasC6RMXVXCivorDhDSqJCFKz3t5+AUIggVYP2pp3vPJ8YlRH/BLiIybtmvEYLQLv+hKeEuWFDHRaCeJrnQ==} engines: {node: '>=12'} '@ampproject/remapping@2.3.0': @@ -917,9 +917,15 @@ packages: '@cashu/cashu-ts@2.0.0-rc1': resolution: {integrity: sha512-39459l7x/fUMEgOsCdGLLl6rMekO4nbv+wEuavmyElh8hgN8t66wcb29AJvdFTb6K3lPACKF2rs/jAlPYrN7Ng==} + '@cashu/cashu-ts@2.1.0': + resolution: {integrity: sha512-qFfFz1dx9keJxumjk5FyTvI1j0Yp/P5LXDy0cGO4Xlp3WYKOI1nykNOTPd+bTY9vSkvIM+xuXRer9BtQxqHtwA==} + '@cashu/crypto@0.2.7': resolution: {integrity: sha512-1aaDfUjiHNXoJqg8nW+341TLWV9W28DsVNXJUKcHL0yAmwLs5+56SSnb8LLDJzPamLVoYL0U0bda91klAzptig==} + '@cashu/crypto@0.3.4': + resolution: {integrity: sha512-mfv1Pj4iL1PXzUj9NKIJbmncCLMqYfnEDqh/OPxAX0nNBt6BOnVJJLjLWFlQeYxlnEfWABSNkrqPje1t5zcyhA==} + '@chakra-ui/anatomy@2.2.2': resolution: {integrity: sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==} @@ -1905,29 +1911,29 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - applesauce-channel@0.0.0-next-20241212192616: - resolution: {integrity: sha512-N7S0XIw+aYymBF5pllEMwKJkDJft9qZAnGMJzdTA400KR391eFbnmE3sVOFXvIvU3XzKP89RrysOWjaPRAdASA==} + applesauce-channel@0.0.0-next-20241213171848: + resolution: {integrity: sha512-4D+FApKZ/YDd/E2Jr26IRzl47wr5um9N7+Sgd6FjeGLYjCKnSKkrO/TZ2nX6/bAWS6pwOgo6e492EEn2xkKAyA==} - applesauce-content@0.0.0-next-20241212192616: - resolution: {integrity: sha512-WUC5WJVDR9sMCJVp5c/3FKBDL6pCqGaBNS5UXeLY+sBXYs9pqLzxXrrILQKWZM+ghPfjCdfc6fDpjVxjyR4vcA==} + applesauce-content@0.0.0-next-20241213171848: + resolution: {integrity: sha512-BUtsmWGMUiw1RmY/xTsDUfLSS2u/rhODT2cDwnfc4ln7hy5CF3vibvn6amgO+7xWe/uK04TJGUmESbUIlKeWjg==} - applesauce-core@0.0.0-next-20241212192616: - resolution: {integrity: sha512-QQjsU2CU0OtQZfnb8VYbz8q/mKC2tLDsxSqHKsa1wIj5m5i7zRgPRBUj2FZrFJmIwlrG73G7uj1Ip53F93SIEw==} + applesauce-core@0.0.0-next-20241213171848: + resolution: {integrity: sha512-2VTdwkfPCUJcykbP+4np9CUoZI4aKUOTX1vjN/DRSaq6Rjd3jC5FP2MNMdRi9u8d1FlAoJyqOs2CCMjnsfkLOg==} - applesauce-factory@0.0.0-next-20241212192616: - resolution: {integrity: sha512-YhCaVGcVbqAzg7T2Cu6MG/iYRpfyC538GNZfG38wm6BD/5YCyygZ0LOmnNrIdhkD5mhxYwLxjlcQyY8n0meK9w==} + applesauce-factory@0.0.0-next-20241213171848: + resolution: {integrity: sha512-SBr57K1ZHyLFhvpn4xXDFZsyWG0X0k1FeTIf6vTcWwZ1FZ1e50eslt+zVLYfFhw55Tk8wT0chB4sToce25VbIw==} - applesauce-lists@0.0.0-next-20241212192616: - resolution: {integrity: sha512-Yd4clwLOKegWs0nJRHcjlJvJtxXYXNSWMQiqVHos510r/0sXpkCL9dmY9Y2QMLbG65Rcwvlu4OfT1w9+iXB7gA==} + applesauce-lists@0.0.0-next-20241213171848: + resolution: {integrity: sha512-GmziucyndF2+xhV2JBqQE1ai683wd6iV0mtatdLeM3h4DfAyElLT4CzFEONH7TzGYwDNnNaB11o7LmE78ww2fg==} - applesauce-net@0.0.0-next-20241212192616: - resolution: {integrity: sha512-LjGH+SCKTpGOYbdiybS0zJzdNpQMX0n1Zq3/S+JeQyfK0vPyeDPznQE0jvuIVT7SHyyCVf9cqSLIPm20qaxDXA==} + applesauce-net@0.0.0-next-20241213171848: + resolution: {integrity: sha512-xtu/QdSN1hv2oRobJ6NX3oewLKWcC/LlqwFjE/RwL+s0+n7U91vIfqIM6Cy2GnSuvEyL8kbdPdOTfdzbqu4/Pg==} - applesauce-react@0.0.0-next-20241212192616: - resolution: {integrity: sha512-tDtXc5e23Y5i11icZge0fTfpYIUXFtj0MQfzBgUfF4FT6zwUw69Bsw4roAVhVezWfWh4fD8dcIFjhNzGyi79AA==} + applesauce-react@0.0.0-next-20241213171848: + resolution: {integrity: sha512-EVUEC4x5/mtXPs1V66FFb6Ke2Pw7gqF8gyBnzcoH9Dvt8DIysJqWbLgtiha+wscgfit9W+eO/YxXxpNbpMjArA==} - applesauce-signer@0.0.0-next-20241212192616: - resolution: {integrity: sha512-kzOCY+TrlGWJnDHpsIzyB/lxFTJfMhQIr6u1+tGro/bqfVBrdX1fJF7cxv7Ro0Tnf/1RGosE4nxjz1Z+9ifqMg==} + applesauce-signer@0.0.0-next-20241213171848: + resolution: {integrity: sha512-op99Gq8z7NvbNVX8lO8GjNi9JIINDbHmwygM6qO2VmavAE0a75EFpIQWEaarr9XACD3V/54GnHiBlqFNTl0skA==} argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -2029,8 +2035,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.2: - resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + browserslist@4.24.3: + resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -2372,8 +2378,8 @@ packages: dnd-core@16.0.1: resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} - dnd-multi-backend@8.1.0: - resolution: {integrity: sha512-Favl89icddeqKI9MzMOW5AH5WHlJhdTmfnHm3QrbviJSp87N4oE7JPcqAzbu2QSis40senFQSoehuLX3H/xadA==} + dnd-multi-backend@8.1.1: + resolution: {integrity: sha512-+uksZeT2J+yD3pwRDixJQgZZ6XJN9uc1PR7ENobUnow6Vg6rawFi524aAOVacNJJvat2trtUo6RRs358U5lEdg==} peerDependencies: dnd-core: ^16.0.1 @@ -2571,8 +2577,8 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - force-graph@1.47.0: - resolution: {integrity: sha512-2R6q3cLPuUzltGA+uYlmV/1cU4Fc/+jwU+0RiwLlUAh/geBmxfV3S1bpZxJ47UMMgD5Op+g0MlxNiJ62LAjQCQ==} + force-graph@1.47.1: + resolution: {integrity: sha512-NF0prpR8tNGq7oCE/aFImT/6/3wSk585bcp39UAj6SNSPjvKbX6ktCH6cZnjfsnPNx/DYj1rn+cvvjH814HCFA==} engines: {node: '>=12'} framer-motion@10.18.0: @@ -2699,8 +2705,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-to-html@9.0.3: - resolution: {integrity: sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==} + hast-util-to-html@9.0.4: + resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==} hast-util-to-jsx-runtime@2.3.2: resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==} @@ -2781,8 +2787,8 @@ packages: inline-style-prefixer@7.0.1: resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} internmap@2.0.3: @@ -2819,8 +2825,8 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-boolean-object@1.2.0: - resolution: {integrity: sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==} + is-boolean-object@1.2.1: + resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} engines: {node: '>= 0.4'} is-callable@1.2.7: @@ -2835,8 +2841,8 @@ packages: resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} is-decimal@1.0.4: @@ -2922,8 +2928,8 @@ packages: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} - is-symbol@1.1.0: - resolution: {integrity: sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==} + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} is-typed-array@1.1.13: @@ -2934,8 +2940,9 @@ packages: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.1.0: + resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + engines: {node: '>= 0.4'} is-weakset@2.0.3: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} @@ -3440,8 +3447,8 @@ packages: parse-entities@2.0.0: resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - parse-entities@4.0.1: - resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} @@ -3553,8 +3560,8 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - rdndmb-html5-to-touch@8.1.0: - resolution: {integrity: sha512-Ep19LSFHwRhq+kuGGSqB8/+LW9uLyjd1xyQG/WkZ8nPoFgIH/3XtbPD7BxF9jMiwPCJepSK+YNUPej4I97F7XA==} + rdndmb-html5-to-touch@8.1.1: + resolution: {integrity: sha512-edyTFE+V9q8aHA2/gKM86F4tjVSdzfEVxFo0kvvTGqIUO2biZJC1TFpr6D+YNA7/oKXHejK5IllyhVrsB6HEsw==} react-chartjs-2@5.2.0: resolution: {integrity: sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==} @@ -3577,16 +3584,16 @@ packages: react-dnd-html5-backend@16.0.1: resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==} - react-dnd-multi-backend@8.1.0: - resolution: {integrity: sha512-SOVXIDAyTp63weGWp6/nI8Km0PwzP/SAqYCz2ZZHp31pNgHtsnDhGkasBSIMG9HDzMNkVp3/X1IPKuDF2OvEwg==} + react-dnd-multi-backend@8.1.1: + resolution: {integrity: sha512-ctRbiLCYGxtXdXV6GbNML9XrvNFoR72AtrgWE3TZlpqwE8ZcaN+PJkARFVW9+99uU9z3BC+dx9FIfIgS9ParEw==} peerDependencies: dnd-core: ^16.0.1 react: ^16.14.0 || ^17.0.2 || ^18.0.0 react-dnd: ^16.0.1 react-dom: ^16.14.0 || ^17.0.2 || ^18.0.0 - react-dnd-preview@8.1.0: - resolution: {integrity: sha512-bMSQb5KTCy58Mo2hDMf48ioNIiPyK87SajJigdd3WlGKXSV7EY/37qPNog1ox5an0iVFSunbQnv2huvZjy3CbA==} + react-dnd-preview@8.1.1: + resolution: {integrity: sha512-NXMxP+U2V5p8Fin3on7L6yfy1jCk2wekwcQw0Wtwhjo4K1lMYtID9tOBo7IKEgVmDdVCLXPQBC1wlGFwDa97Mg==} peerDependencies: react: ^16.14.0 || ^17.0.2 || ^18.0.0 react-dnd: ^16.0.1 @@ -3643,8 +3650,8 @@ packages: peerDependencies: react: '*' - react-hook-form@7.54.0: - resolution: {integrity: sha512-PS05+UQy/IdSbJNojBypxAo9wllhHgGmyr8/dyGQcPoiMf3e7Dfb9PWYVRco55bLbxH9S+1yDDJeTdlYCSxO3A==} + react-hook-form@7.54.1: + resolution: {integrity: sha512-PUNzFwQeQ5oHiiTUO7GO/EJXGEtuun2Y1A59rLnZBBj+vNEOWt/3ERTiG1/zt7dVeJEM+4vDX/7XQ/qanuvPMg==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -3652,8 +3659,8 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-kapsule@2.5.2: - resolution: {integrity: sha512-ptK3/0BBJwS958e0Akn5VkQjeVDA2tM8NrJvlctIPI3OA5zp6Q6AD/MWwvKiMCH/tTI04h8JfGsOztDwJdQHqw==} + react-kapsule@2.5.6: + resolution: {integrity: sha512-aE4Nq7dDG8R/LdNmvOL6Azjr97I2E7ycFDJRkoHJSp9OQgTJDT3MHTJtJDrOTwzCl6sllYSqrtcndaCzizyAjQ==} engines: {node: '>=12'} peerDependencies: react: '>=16.13.1' @@ -4114,20 +4121,20 @@ packages: textarea-caret@3.0.2: resolution: {integrity: sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg==} - three-forcegraph@1.42.5: - resolution: {integrity: sha512-39PVCtQ9Ayh0MxeE7/eiqKokCjRVE7seDKIRe47tTkImQDPMani6/Jzxawoy96JJyhdI8Kz3/gmlcfK6iB3EIQ==} + three-forcegraph@1.42.8: + resolution: {integrity: sha512-fhTQPC9lvYYUlsyVUUeJwijpUxHzbmurFSf5LEQI9W3Yp5kwI9oupS03g9HojpTUMd+bqiGvhVCnle5zmBMcAw==} engines: {node: '>=12'} peerDependencies: three: '>=0.118.3' - three-render-objects@1.32.0: - resolution: {integrity: sha512-bTpyr8hp8PowFAi3pGjm/t0cMY7G8tecxgBTlOi9H/JNXxlAJBmvT6LF7oXeh7i2Q4Q6m9DNy+/Y72s+Varzww==} + three-render-objects@1.32.1: + resolution: {integrity: sha512-HcbVhMFwPxtxrrQYe+pD8HFZmx22lYuYZeHXcZlDdxqWyr5wAZgjD+vX23oALrmP3i1LW8udheXxbntwYmA9sw==} engines: {node: '>=12'} peerDependencies: three: '>=0.168' - three-spritetext@1.9.2: - resolution: {integrity: sha512-muexs0ClLe1ppqtAbpwHIn6bcmGoGG4fRB5pnoWgKNmYRqURYfsPr6luMGSJ4MftE/rirZDuAgqUgwL/voGzyg==} + three-spritetext@1.9.3: + resolution: {integrity: sha512-p7iEQr7anABRUJNOH2logMf1aRuWb026IshUC4aJ8F+bbMTn5Z43/Jd7/W3VihE3ToCwrWMl6u6VwgFMTw0jvA==} engines: {node: '>=12'} peerDependencies: three: '>=0.86.0' @@ -4407,8 +4414,8 @@ packages: resolution: {integrity: sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==} engines: {node: '>= 0.4'} - which-builtin-type@1.2.0: - resolution: {integrity: sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==} + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} engines: {node: '>= 0.4'} which-collection@1.0.2: @@ -4536,13 +4543,13 @@ packages: snapshots: - 3d-force-graph@1.74.2: + 3d-force-graph@1.74.3: dependencies: accessor-fn: 1.5.1 kapsule: 1.16.0 three: 0.170.0 - three-forcegraph: 1.42.5(three@0.170.0) - three-render-objects: 1.32.0(three@0.170.0) + three-forcegraph: 1.42.8(three@0.170.0) + three-render-objects: 1.32.1(three@0.170.0) '@ampproject/remapping@2.3.0': dependencies: @@ -4600,7 +4607,7 @@ snapshots: dependencies: '@babel/compat-data': 7.26.3 '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.2 + browserslist: 4.24.3 lru-cache: 5.1.1 semver: 6.3.1 @@ -5213,6 +5220,14 @@ snapshots: '@scure/bip32': 1.6.0 buffer: 6.0.3 + '@cashu/cashu-ts@2.1.0': + dependencies: + '@cashu/crypto': 0.3.4 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/bip32': 1.6.0 + buffer: 6.0.3 + '@cashu/crypto@0.2.7': dependencies: '@noble/curves': 1.7.0 @@ -5221,6 +5236,14 @@ snapshots: '@scure/bip39': 1.5.0 buffer: 6.0.3 + '@cashu/crypto@0.3.4': + dependencies: + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + '@scure/bip32': 1.6.0 + '@scure/bip39': 1.5.0 + buffer: 6.0.3 + '@chakra-ui/anatomy@2.2.2': {} '@chakra-ui/anatomy@2.3.4': {} @@ -6077,7 +6100,7 @@ snapshots: '@shikijs/types': 1.24.2 '@shikijs/vscode-textmate': 9.3.1 '@types/hast': 3.0.4 - hast-util-to-html: 9.0.3 + hast-util-to-html: 9.0.4 '@shikijs/engine-javascript@1.24.2': dependencies: @@ -6368,23 +6391,23 @@ snapshots: dependencies: color-convert: 2.0.1 - applesauce-channel@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-channel@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) - applesauce-factory: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) + applesauce-factory: 0.0.0-next-20241213171848(typescript@5.7.2) nostr-tools: 2.10.4(typescript@5.7.2) rxjs: 7.8.1 transitivePeerDependencies: - supports-color - typescript - applesauce-content@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-content@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: '@cashu/cashu-ts': 2.0.0-rc1 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 '@types/unist': 3.0.3 - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) mdast-util-find-and-replace: 3.0.1 nostr-tools: 2.10.4(typescript@5.7.2) remark: 15.0.1 @@ -6395,7 +6418,7 @@ snapshots: - supports-color - typescript - applesauce-core@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-core@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: '@scure/base': 1.2.1 debug: 4.4.0 @@ -6409,23 +6432,23 @@ snapshots: - supports-color - typescript - applesauce-factory@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-factory@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: - applesauce-content: 0.0.0-next-20241212192616(typescript@5.7.2) - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-content: 0.0.0-next-20241213171848(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) nostr-tools: 2.10.4(typescript@5.7.2) transitivePeerDependencies: - supports-color - typescript - applesauce-lists@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-lists@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: '@noble/hashes': 1.6.1 '@noble/secp256k1': 1.7.1 '@scure/base': 1.2.1 '@types/dom-serial': 1.0.6 - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) - applesauce-factory: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) + applesauce-factory: 0.0.0-next-20241213171848(typescript@5.7.2) debug: 4.4.0 nostr-tools: 2.10.4(typescript@5.7.2) rxjs: 7.8.1 @@ -6433,9 +6456,9 @@ snapshots: - supports-color - typescript - applesauce-net@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-net@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) nanoid: 5.0.9 nostr-tools: 2.10.4(typescript@5.7.2) rxjs: 7.8.1 @@ -6443,11 +6466,11 @@ snapshots: - supports-color - typescript - applesauce-react@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-react@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: - applesauce-content: 0.0.0-next-20241212192616(typescript@5.7.2) - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) - applesauce-factory: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-content: 0.0.0-next-20241213171848(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) + applesauce-factory: 0.0.0-next-20241213171848(typescript@5.7.2) nostr-tools: 2.10.4(typescript@5.7.2) react: 18.3.1 rxjs: 7.8.1 @@ -6455,14 +6478,14 @@ snapshots: - supports-color - typescript - applesauce-signer@0.0.0-next-20241212192616(typescript@5.7.2): + applesauce-signer@0.0.0-next-20241213171848(typescript@5.7.2): dependencies: '@noble/hashes': 1.6.1 '@noble/secp256k1': 1.7.1 '@scure/base': 1.2.1 '@types/dom-serial': 1.0.6 - applesauce-core: 0.0.0-next-20241212192616(typescript@5.7.2) - applesauce-net: 0.0.0-next-20241212192616(typescript@5.7.2) + applesauce-core: 0.0.0-next-20241213171848(typescript@5.7.2) + applesauce-net: 0.0.0-next-20241213171848(typescript@5.7.2) debug: 4.4.0 nanoid: 5.0.9 nostr-tools: 2.10.4(typescript@5.7.2) @@ -6594,12 +6617,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.2: + browserslist@4.24.3: dependencies: caniuse-lite: 1.0.30001688 electron-to-chromium: 1.5.73 node-releases: 2.0.19 - update-browserslist-db: 1.1.1(browserslist@4.24.2) + update-browserslist-db: 1.1.1(browserslist@4.24.3) buffer-from@1.1.2: {} @@ -6771,7 +6794,7 @@ snapshots: core-js-compat@3.39.0: dependencies: - browserslist: 4.24.2 + browserslist: 4.24.3 cosmiconfig@7.1.0: dependencies: @@ -6975,7 +6998,7 @@ snapshots: '@react-dnd/invariant': 4.0.2 redux: 4.2.1 - dnd-multi-backend@8.1.0(dnd-core@16.0.1): + dnd-multi-backend@8.1.1(dnd-core@16.0.1): dependencies: dnd-core: 16.0.1 @@ -7072,7 +7095,7 @@ snapshots: has-proto: 1.2.0 has-symbols: 1.1.0 hasown: 2.0.2 - internal-slot: 1.0.7 + internal-slot: 1.1.0 is-array-buffer: 3.0.4 is-callable: 1.2.7 is-data-view: 1.0.2 @@ -7081,7 +7104,7 @@ snapshots: is-shared-array-buffer: 1.0.3 is-string: 1.1.0 is-typed-array: 1.1.13 - is-weakref: 1.0.2 + is-weakref: 1.1.0 object-inspect: 1.13.3 object-keys: 1.1.1 object.assign: 4.1.5 @@ -7115,8 +7138,8 @@ snapshots: es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.1.0 + is-date-object: 1.1.0 + is-symbol: 1.1.1 esbuild@0.21.5: optionalDependencies: @@ -7225,7 +7248,7 @@ snapshots: dependencies: is-callable: 1.2.7 - force-graph@1.47.0: + force-graph@1.47.1: dependencies: '@tweenjs/tween.js': 25.0.0 accessor-fn: 1.5.1 @@ -7380,7 +7403,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-to-html@9.0.3: + hast-util-to-html@9.0.4: dependencies: '@types/hast': 3.0.4 '@types/unist': 3.0.3 @@ -7483,7 +7506,7 @@ snapshots: dependencies: css-in-js-utils: 3.1.0 - internal-slot@1.0.7: + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 @@ -7524,9 +7547,9 @@ snapshots: dependencies: has-bigints: 1.0.2 - is-boolean-object@1.2.0: + is-boolean-object@1.2.1: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.2 has-tostringtag: 1.0.2 is-callable@1.2.7: {} @@ -7541,8 +7564,9 @@ snapshots: get-intrinsic: 1.2.6 is-typed-array: 1.1.13 - is-date-object@1.0.5: + is-date-object@1.1.0: dependencies: + call-bound: 1.0.2 has-tostringtag: 1.0.2 is-decimal@1.0.4: {} @@ -7610,9 +7634,9 @@ snapshots: dependencies: better-path-resolve: 1.0.0 - is-symbol@1.1.0: + is-symbol@1.1.1: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.2 has-symbols: 1.1.0 safe-regex-test: 1.1.0 @@ -7622,9 +7646,9 @@ snapshots: is-weakmap@2.0.2: {} - is-weakref@1.0.2: + is-weakref@1.1.0: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.2 is-weakset@2.0.3: dependencies: @@ -7876,7 +7900,7 @@ snapshots: devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.1 + parse-entities: 4.0.2 stringify-entities: 4.0.4 unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 @@ -8327,10 +8351,9 @@ snapshots: is-decimal: 1.0.4 is-hexadecimal: 1.0.4 - parse-entities@4.0.1: + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 - character-entities: 2.0.2 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 decode-named-character-reference: 1.0.2 @@ -8425,9 +8448,9 @@ snapshots: dependencies: safe-buffer: 5.2.1 - rdndmb-html5-to-touch@8.1.0(dnd-core@16.0.1): + rdndmb-html5-to-touch@8.1.1(dnd-core@16.0.1): dependencies: - dnd-multi-backend: 8.1.0(dnd-core@16.0.1) + dnd-multi-backend: 8.1.1(dnd-core@16.0.1) react-dnd-html5-backend: 16.0.1 react-dnd-touch-backend: 16.0.1 transitivePeerDependencies: @@ -8459,16 +8482,16 @@ snapshots: dependencies: dnd-core: 16.0.1 - react-dnd-multi-backend@8.1.0(dnd-core@16.0.1)(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-dnd-multi-backend@8.1.1(dnd-core@16.0.1)(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: dnd-core: 16.0.1 - dnd-multi-backend: 8.1.0(dnd-core@16.0.1) + dnd-multi-backend: 8.1.1(dnd-core@16.0.1) react: 18.3.1 react-dnd: 16.0.1(@types/react@18.3.16)(react@18.3.1) - react-dnd-preview: 8.1.0(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react@18.3.1) + react-dnd-preview: 8.1.1(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) - react-dnd-preview@8.1.0(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react@18.3.1): + react-dnd-preview@8.1.1(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dnd: 16.0.1(@types/react@18.3.16)(react@18.3.1) @@ -8516,25 +8539,25 @@ snapshots: react-force-graph-2d@1.26.1(react@18.3.1): dependencies: - force-graph: 1.47.0 + force-graph: 1.47.1 prop-types: 15.8.1 react: 18.3.1 - react-kapsule: 2.5.2(react@18.3.1) + react-kapsule: 2.5.6(react@18.3.1) react-force-graph-3d@1.25.1(react@18.3.1): dependencies: - 3d-force-graph: 1.74.2 + 3d-force-graph: 1.74.3 prop-types: 15.8.1 react: 18.3.1 - react-kapsule: 2.5.2(react@18.3.1) + react-kapsule: 2.5.6(react@18.3.1) - react-hook-form@7.54.0(react@18.3.1): + react-hook-form@7.54.1(react@18.3.1): dependencies: react: 18.3.1 react-is@16.13.1: {} - react-kapsule@2.5.2(react@18.3.1): + react-kapsule@2.5.6(react@18.3.1): dependencies: jerrypick: 1.1.1 react: 18.3.1 @@ -8562,11 +8585,11 @@ snapshots: immutability-helper: 3.1.1 lodash: 4.17.21 prop-types: 15.8.1 - rdndmb-html5-to-touch: 8.1.0(dnd-core@16.0.1) + rdndmb-html5-to-touch: 8.1.1(dnd-core@16.0.1) react: 18.3.1 react-dnd: 16.0.1(@types/react@18.3.16)(react@18.3.1) react-dnd-html5-backend: 16.0.1 - react-dnd-multi-backend: 8.1.0(dnd-core@16.0.1)(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dnd-multi-backend: 8.1.1(dnd-core@16.0.1)(react-dnd@16.0.1(@types/react@18.3.16)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dnd-touch-backend: 16.0.1 uuid: 9.0.1 transitivePeerDependencies: @@ -8708,7 +8731,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.6 gopd: 1.2.0 - which-builtin-type: 1.2.0 + which-builtin-type: 1.2.1 regenerate-unicode-properties@10.2.0: dependencies: @@ -9029,7 +9052,7 @@ snapshots: get-intrinsic: 1.2.6 gopd: 1.2.0 has-symbols: 1.1.0 - internal-slot: 1.0.7 + internal-slot: 1.1.0 regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 side-channel: 1.1.0 @@ -9112,7 +9135,7 @@ snapshots: textarea-caret@3.0.2: {} - three-forcegraph@1.42.5(three@0.170.0): + three-forcegraph@1.42.8(three@0.170.0): dependencies: accessor-fn: 1.5.1 d3-array: 3.2.4 @@ -9126,7 +9149,7 @@ snapshots: three: 0.170.0 tinycolor2: 1.6.0 - three-render-objects@1.32.0(three@0.170.0): + three-render-objects@1.32.1(three@0.170.0): dependencies: '@tweenjs/tween.js': 25.0.0 accessor-fn: 1.5.1 @@ -9134,7 +9157,7 @@ snapshots: polished: 4.3.1 three: 0.170.0 - three-spritetext@1.9.2(three@0.170.0): + three-spritetext@1.9.3(three@0.170.0): dependencies: three: 0.170.0 @@ -9298,9 +9321,9 @@ snapshots: upath@1.2.0: {} - update-browserslist-db@1.1.1(browserslist@4.24.2): + update-browserslist-db@1.1.1(browserslist@4.24.3): dependencies: - browserslist: 4.24.2 + browserslist: 4.24.3 escalade: 3.2.0 picocolors: 1.1.1 @@ -9389,22 +9412,22 @@ snapshots: which-boxed-primitive@1.1.0: dependencies: is-bigint: 1.1.0 - is-boolean-object: 1.2.0 + is-boolean-object: 1.2.1 is-number-object: 1.1.0 is-string: 1.1.0 - is-symbol: 1.1.0 + is-symbol: 1.1.1 - which-builtin-type@1.2.0: + which-builtin-type@1.2.1: dependencies: - call-bind: 1.0.8 + call-bound: 1.0.2 function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 is-async-function: 2.0.0 - is-date-object: 1.0.5 + is-date-object: 1.1.0 is-finalizationregistry: 1.1.0 is-generator-function: 1.0.10 is-regex: 1.2.1 - is-weakref: 1.0.2 + is-weakref: 1.1.0 isarray: 2.0.5 which-boxed-primitive: 1.1.0 which-collection: 1.0.2 diff --git a/src/components/cashu/inline-cashu-card.tsx b/src/components/cashu/inline-cashu-card.tsx index bf4fff132..5d3d338fe 100644 --- a/src/components/cashu/inline-cashu-card.tsx +++ b/src/components/cashu/inline-cashu-card.tsx @@ -3,7 +3,6 @@ import { Box, Button, ButtonGroup, Card, CardProps, Heading, IconButton, Link, S import { Token, getEncodedToken, CheckStateEnum } from "@cashu/cashu-ts"; import { CopyIconButton } from "../copy-icon-button"; -import useUserProfile from "../../hooks/use-user-profile"; import useCurrentAccount from "../../hooks/use-current-account"; import { ECashIcon, WalletIcon } from "../icons"; import CurrencyDollar from "../icons/currency-dollar"; @@ -14,28 +13,11 @@ import CurrencyPound from "../icons/currency-pound"; import CurrencyBitcoin from "../icons/currency-bitcoin"; import { getMintWallet } from "../../services/cashu-mints"; -function RedeemButton({ token }: { token: string }) { - const account = useCurrentAccount()!; - const metadata = useUserProfile(account.pubkey); - - const lnurl = metadata?.lud16 ?? ""; - const url = `https://redeem.cashu.me?token=${encodeURIComponent(token)}&lightning=${encodeURIComponent( - lnurl, - )}&autopay=yes`; - return ( - - ); -} - export default function InlineCachuCard({ token, encoded, ...props }: Omit & { token: Token; encoded?: string }) { - const account = useCurrentAccount(); - encoded = encoded || getEncodedToken(token); const { value: spendable, loading } = useAsync(async () => { if (!token) return; @@ -84,7 +66,7 @@ export default function InlineCachuCard({ } return ( - + @@ -96,7 +78,6 @@ export default function InlineCachuCard({ aria-label="Open Wallet" href={`cashu://` + encoded} /> - {account && } {denomination} {spendable === false ? " (Spent)" : loading ? : undefined} @@ -105,7 +86,6 @@ export default function InlineCachuCard({ {token.unit && Unit: {token.unit}} {token.memo && {token.memo}} - {loading && } ); } diff --git a/src/components/sensitive-content-warning.tsx b/src/components/content-warning.tsx similarity index 92% rename from src/components/sensitive-content-warning.tsx rename to src/components/content-warning.tsx index a66a56f43..ff923c4c1 100644 --- a/src/components/sensitive-content-warning.tsx +++ b/src/components/content-warning.tsx @@ -3,7 +3,7 @@ import { Alert, AlertDescription, AlertIcon, AlertProps, AlertTitle, Button, Spa import { useExpand } from "../providers/local/expanded"; import { useBreakpointValue } from "../providers/global/breakpoint-provider"; -export default function SensitiveContentWarning({ description }: { description?: string } & AlertProps) { +export default function ContentWarning({ description }: { description?: string } & AlertProps) { const expand = useExpand(); const smallScreen = useBreakpointValue({ base: true, md: false }); diff --git a/src/components/event-zap-modal/index.tsx b/src/components/event-zap-modal/index.tsx index d29f9ad22..b70b203e8 100644 --- a/src/components/event-zap-modal/index.tsx +++ b/src/components/event-zap-modal/index.tsx @@ -12,9 +12,9 @@ import dayjs from "dayjs"; import { kinds } from "nostr-tools"; import { getValue } from "applesauce-core/observable"; import { getInboxes, getInvoice, getOutboxes, safeRelayUrls } from "applesauce-core/helpers"; +import { getZapSplits } from "applesauce-core/helpers/zap"; import { DraftNostrEvent, NostrEvent, isDTag } from "../../types/nostr-event"; -import { getZapSplits } from "../../helpers/nostr/zaps"; import { unique } from "../../helpers/array"; import relayScoreboardService from "../../services/relay-scoreboard"; import { getEventCoordinate, isReplaceable } from "../../helpers/nostr/event"; @@ -122,7 +122,7 @@ async function getPayRequestsForEvent( fallbackPubkey?: string, additionalRelays?: Iterable, ) { - const splits = getZapSplits(event, fallbackPubkey); + const splits = getZapSplits(event) ?? (fallbackPubkey ? [{ pubkey: fallbackPubkey, percent: 1, weight: 1 }] : []); const draftZapRequests: PayRequest[] = []; for (const { pubkey, percent } of splits) { diff --git a/src/components/event-zap-modal/input-step.tsx b/src/components/event-zap-modal/input-step.tsx index 580614eaa..db6ed1ed7 100644 --- a/src/components/event-zap-modal/input-step.tsx +++ b/src/components/event-zap-modal/input-step.tsx @@ -1,11 +1,11 @@ import { Box, Button, Flex, Input, Text } from "@chakra-ui/react"; +import { getZapSplits } from "applesauce-core/helpers"; import { useForm } from "react-hook-form"; import { NostrEvent } from "../../types/nostr-event"; import { humanReadableSats } from "../../helpers/lightning"; import { LightningIcon } from "../icons"; import useUserLNURLMetadata from "../../hooks/use-user-lnurl-metadata"; -import { getZapSplits } from "../../helpers/nostr/zaps"; import { EmbedEvent, EmbedProps } from "../embed-event"; import useAppSettings from "../../hooks/use-app-settings"; import CustomZapAmountOptions from "./zap-options"; @@ -71,7 +71,7 @@ export default function InputStep({ }, }); - const splits = event ? getZapSplits(event, pubkey) : []; + const splits = event ? (getZapSplits(event) ?? []) : []; const { metadata: lnurlMetadata } = useUserLNURLMetadata(pubkey); const canZap = lnurlMetadata?.allowsNostr && lnurlMetadata?.nostrPubkey; diff --git a/src/components/note/timeline-note/note-content-with-warning.tsx b/src/components/note/timeline-note/note-content-with-warning.tsx index bcaedf4ea..eea83b7c8 100644 --- a/src/components/note/timeline-note/note-content-with-warning.tsx +++ b/src/components/note/timeline-note/note-content-with-warning.tsx @@ -1,19 +1,20 @@ import { NostrEvent } from "nostr-tools"; +import { getContentWarning } from "applesauce-core/helpers"; import { TextNoteContents } from "./text-note-contents"; import { useExpand } from "../../../providers/local/expanded"; -import SensitiveContentWarning from "../../sensitive-content-warning"; +import ContentWarning from "../../content-warning"; import useAppSettings from "../../../hooks/use-app-settings"; export default function NoteContentWithWarning({ event }: { event: NostrEvent }) { const expand = useExpand(); const settings = useAppSettings(); - const contentWarningTag = event.tags.find((t) => t[0] === "content-warning"); - const showContentWarning = settings.showContentWarning && contentWarningTag && !expand?.expanded; + const warning = getContentWarning(event); + const showContentWarning = settings.showContentWarning && !!warning && !expand?.expanded; return showContentWarning ? ( - + ) : ( ); diff --git a/src/components/post-modal/index.tsx b/src/components/post-modal/index.tsx index 13b4448bc..da72ee9d3 100644 --- a/src/components/post-modal/index.tsx +++ b/src/components/post-modal/index.tsx @@ -12,8 +12,6 @@ import { Input, Switch, ModalProps, - VisuallyHiddenInput, - IconButton, FormLabel, FormControl, FormHelperText, @@ -28,30 +26,20 @@ import { ButtonGroup, Text, } from "@chakra-ui/react"; -import dayjs from "dayjs"; import { useForm } from "react-hook-form"; -import { kinds, UnsignedEvent } from "nostr-tools"; -import { useThrottle } from "react-use"; +import { EventTemplate, UnsignedEvent } from "nostr-tools"; +import { useAsync, useThrottle } from "react-use"; import { useEventFactory, useObservable } from "applesauce-react/hooks"; +import { Emoji, ZapSplit } from "applesauce-core/helpers"; -import { ChevronDownIcon, ChevronUpIcon, UploadImageIcon } from "../icons"; +import { ChevronDownIcon, ChevronUpIcon } from "../icons"; import PublishAction from "../../classes/nostr-publish-action"; import { PublishDetails } from "../../views/task-manager/publish-log/publish-details"; import { TrustProvider } from "../../providers/local/trust-provider"; -import { - correctContentMentions, - createEmojiTags, - ensureNotifyPubkeys, - finalizeNote, - getPubkeysMentionedInContent, - setZapSplit, -} from "../../helpers/nostr/post"; -import { UserAvatarStack } from "../compact-user-stack"; import MagicTextArea, { RefType } from "../magic-textarea"; import { useContextEmojis } from "../../providers/global/emoji-provider"; import CommunitySelect from "./community-select"; -import ZapSplitCreator, { fillRemainingPercent } from "./zap-split-creator"; -import { EventSplit } from "../../helpers/nostr/zaps"; +import ZapSplitCreator from "./zap-split-creator"; import useCurrentAccount from "../../hooks/use-current-account"; import useCacheForm from "../../hooks/use-cache-form"; import useTextAreaUploadFile, { useTextAreaInsertTextWithForm } from "../../hooks/use-textarea-upload-file"; @@ -64,7 +52,6 @@ import localSettings from "../../services/local-settings"; import useLocalStorageDisclosure from "../../hooks/use-localstorage-disclosure"; import InsertGifButton from "../gif/insert-gif-button"; import InsertImageButton from "./insert-image-button"; -import { unixNow } from "applesauce-core/helpers"; type FormValues = { subject: string; @@ -72,7 +59,7 @@ type FormValues = { nsfw: boolean; nsfwReason: string; community: string; - split: EventSplit; + split: Omit[]; difficulty: number; }; @@ -111,7 +98,7 @@ export default function PostModal({ nsfw: false, nsfwReason: "", community: initCommunity, - split: [] as EventSplit, + split: [] as Omit[], difficulty: noteDifficulty || 0, }, mode: "all", @@ -128,47 +115,34 @@ export default function PostModal({ // cache form to localStorage useCacheForm(cacheFormKey, getValues, reset, formState); - const updateDraft = useCallback( - async (values = getValues()) => { - const { content, nsfw, nsfwReason, community, split, subject } = values; + const getDraft = async (values = getValues()) => { + // build draft using factory + let draft = await factory.note(values.content, { + emojis: emojis.filter((e) => !!e.url) as Emoji[], + contentWarning: values.nsfw ? values.nsfwReason || values.nsfw : false, + splits: values.split, + }); - let draft = finalizeNote({ - kind: kinds.ShortTextNote, - content, - tags: [], - created_at: unixNow(), - }); + // TODO: remove when NIP-72 communities are removed + if (values.community) draft.tags.push(["a", values.community]); + if (values.subject) draft.tags.push(["subject", values.subject]); - if (nsfw) draft.tags.push(nsfwReason ? ["content-warning", nsfwReason] : ["content-warning"]); - if (community) draft.tags.push(["a", community]); - if (subject) draft.tags.push(["subject", subject]); + const unsigned = await finalizeDraft(draft); - const contentMentions = getPubkeysMentionedInContent(draft.content); - draft = createEmojiTags(draft, emojis); - draft = ensureNotifyPubkeys(draft, contentMentions); - if (split.length > 0) { - draft = setZapSplit(draft, fillRemainingPercent(split, account.pubkey)); - } - - const unsigned = await finalizeDraft(draft); - setDraft(unsigned); - return unsigned; - }, - [getValues, emojis, finalizeDraft, setDraft, factory], - ); + setDraft(unsigned); + return unsigned; + }; // throttle update the draft every 500ms - const throttleValues = useThrottle(JSON.stringify(getValues()), 500); - useEffect(() => { - updateDraft(getValues()); - }, [throttleValues]); + const throttleValues = useThrottle(getValues(), 500); + const { value: preview } = useAsync(() => getDraft(), [throttleValues]); const textAreaRef = useRef(null); const insertText = useTextAreaInsertTextWithForm(textAreaRef, getValues, setValue); const { onPaste } = useTextAreaUploadFile(insertText); const publishPost = async (unsigned?: UnsignedEvent) => { - unsigned = unsigned || draft || (await updateDraft()); + unsigned = unsigned || draft || (await getDraft()); const pub = await publish("Post", unsigned); if (pub) setPublishAction(pub); @@ -177,13 +151,12 @@ export default function PostModal({ if (values.difficulty > 0) { setMiningTarget(values.difficulty); } else { - const unsigned = await updateDraft(values); + const unsigned = await getDraft(values); publishPost(unsigned); } }); const canSubmit = getValues().content.length > 0; - const mentions = getPubkeysMentionedInContent(correctContentMentions(getValues().content)); const renderBody = () => { if (publishAction) { @@ -229,13 +202,13 @@ export default function PostModal({ if ((e.ctrlKey || e.metaKey) && e.key === "Enter") submit(); }} /> - {draft && draft.content.length > 0 && ( + {preview && preview.content.length > 0 && ( Preview: - + @@ -253,7 +226,6 @@ export default function PostModal({ More Options - {mentions.length > 0 && } @@ -305,8 +277,8 @@ export default function PostModal({ setValue("split", s, { shouldDirty: true })} + splits={getValues().split} + onChange={(splits) => setValue("split", splits, { shouldDirty: true })} authorPubkey={account?.pubkey} /> diff --git a/src/components/post-modal/zap-split-creator.tsx b/src/components/post-modal/zap-split-creator.tsx index 1a2d5b843..1e030c80b 100644 --- a/src/components/post-modal/zap-split-creator.tsx +++ b/src/components/post-modal/zap-split-creator.tsx @@ -7,27 +7,18 @@ import { NumberInput, NumberInputField, NumberInputStepper, - Text, useToast, } from "@chakra-ui/react"; import { CloseIcon } from "@chakra-ui/icons"; import { useForm } from "react-hook-form"; -import { EventSplit } from "../../helpers/nostr/zaps"; import { AddIcon } from "../icons"; import { normalizeToHexPubkey } from "../../helpers/nip19"; import UserAvatar from "../user/user-avatar"; import UserLink from "../user/user-link"; import UserAutocomplete from "../user-autocomplete"; -function getRemainingPercent(split: EventSplit) { - return Math.round((1 - split.reduce((v, p) => v + p.percent, 0)) * 100) / 100; -} -export function fillRemainingPercent(split: EventSplit, pubkey: string) { - const remainingPercent = getRemainingPercent(split); - if (remainingPercent === 0) return split; - return split.concat({ pubkey, percent: remainingPercent }); -} +type Split = { pubkey: string; weight: number }; function validateNpub(input: string) { const pubkey = normalizeToHexPubkey(input); @@ -36,29 +27,20 @@ function validateNpub(input: string) { } } -function AddUserForm({ - onSubmit, - remainingPercent, -}: { - onSubmit: (values: { pubkey: string; percent: number }) => void; - remainingPercent: number; -}) { +function AddUserForm({ onSubmit }: { onSubmit: (values: Split) => void }) { const toast = useToast(); - const { register, handleSubmit, getValues, setValue, reset, watch } = useForm({ + const { register, handleSubmit, reset } = useForm({ defaultValues: { pubkey: "", - percent: Math.min(remainingPercent, 50), }, mode: "all", }); - watch("percent"); const submit = handleSubmit((values) => { try { const pubkey = normalizeToHexPubkey(values.pubkey); if (!pubkey) throw new Error("Invalid npub"); - const percent = values.percent / 100; - onSubmit({ pubkey, percent }); + onSubmit({ pubkey, weight: 1 }); reset(); } catch (e) { if (e instanceof Error) toast({ description: e.message, status: "error" }); @@ -68,88 +50,81 @@ function AddUserForm({ return ( - setValue("percent", n, { shouldDirty: true })} - > - - - - - - - } aria-label="Add" type="submit" /> + } aria-label="Add" type="submit" /> ); } function UserCard({ pubkey, - percent, - showRemove = true, + weight, onRemove, + onChange, }: { pubkey: string; - percent: number; - showRemove?: boolean; + weight: number; onRemove?: () => void; + onChange?: (split: Split) => void; }) { return ( - - {Math.round(percent * 10000) / 100}% - - {showRemove && ( - } - aria-label="Remove from split" - title="Remove" - onClick={onRemove} - /> - )} + + onChange?.({ pubkey, weight: Number.isFinite(n) ? n : 1 })} + ml="auto" + > + + + + + + + } + aria-label="Remove from split" + title="Remove" + onClick={onRemove} + /> ); } export default function ZapSplitCreator({ - split, + splits, onChange, - authorPubkey, }: { - split: EventSplit; - onChange: (split: EventSplit) => void; + splits: Split[]; + onChange: (split: Split[]) => void; authorPubkey?: string; }) { - const remainingPercent = getRemainingPercent(split); - - const addUser = ({ pubkey, percent }: { pubkey: string; percent: number }) => { - if (percent > remainingPercent) throw new Error("Not enough percent left"); - if (split.some((s) => s.pubkey === pubkey)) throw new Error("User already in split"); - onChange(split.concat({ pubkey, percent })); + const addUser = ({ pubkey, weight }: Split) => { + if (splits.some((s) => s.pubkey === pubkey)) throw new Error("User already in split"); + onChange(splits.concat({ pubkey, weight })); }; const removeUser = (pubkey: string) => { - onChange(split.filter((p) => p.pubkey !== pubkey)); + onChange(splits.filter((p) => p.pubkey !== pubkey)); + }; + const changeUser = (split: Split) => { + onChange(splits.map((s) => (s.pubkey === split.pubkey ? split : s))); }; - - const displaySplit = authorPubkey ? fillRemainingPercent(split, authorPubkey) : split; return ( Zap Splits - {remainingPercent > 0 && } - {displaySplit.map(({ pubkey, percent }) => ( + + {splits.map(({ pubkey, weight }) => ( removeUser(pubkey)} + onChange={changeUser} /> ))} diff --git a/src/helpers/nostr/post.ts b/src/helpers/nostr/post.ts index 9fd17f3c8..c98707629 100644 --- a/src/helpers/nostr/post.ts +++ b/src/helpers/nostr/post.ts @@ -1,13 +1,9 @@ -import { EventTemplate, kinds, nip18, nip19 } from "nostr-tools"; -import { EventPointer } from "nostr-tools/nip19"; -import { getInboxes, isPTag } from "applesauce-core/helpers"; +import { EventTemplate, nip19 } from "nostr-tools"; import { NostrEvent, Tag } from "../../types/nostr-event"; -import { getMatchEmoji, getMatchHashtag, getMatchNostrLink } from "../regexp"; +import { getMatchNostrLink } from "../regexp"; import { getThreadReferences } from "./event"; import { safeDecode } from "../nip19"; -import { Emoji } from "../../providers/global/emoji-provider"; -import { EventSplit } from "./zaps"; import { unique } from "../array"; import { getEventPointerRelayHint } from "../../services/relay-hints"; @@ -44,23 +40,6 @@ function AddEtag(tags: Tag[], eventId: string, relayHint?: string, type?: string return [...tags, tag]; } -/** @deprecated use event factory instead */ -function AddQuotePointerTag(tags: Tag[], pointer: EventPointer) { - const hint = pointer.relays?.[0] || getEventPointerRelayHint(pointer.id) || ""; - - const tag: string[] = ["q", pointer.id, hint]; - if (pointer.author) tag.push(pointer.author); - - if (tags.some((t) => t[0] === tag[0] && t[1] === tag[1] && t[3] === tag[3])) { - // replace the tag - return tags.map((t) => { - if (t[0] === tag[0] && t[1] === tag[1]) return tag; - return t; - }); - } - return [...tags, tag]; -} - /** adds the "root" and "reply" E tags */ export function addReplyTags(draft: EventTemplate, replyTo: NostrEvent) { const updated: EventTemplate = { ...draft, tags: Array.from(draft.tags) }; @@ -88,10 +67,6 @@ export function ensureNotifyPubkeys(draft: EventTemplate, pubkeys: string[]) { return updated; } -export function correctContentMentions(content: string) { - return content.replaceAll(/(?<=^|\s)(?:@)?(npub1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{58})/gi, "nostr:$1"); -} - export function getPubkeysMentionedInContent(content: string, direct = false) { const matched = content.matchAll(getMatchNostrLink()); @@ -120,11 +95,6 @@ export function getPubkeysMentionedInContent(content: string, direct = false) { return unique(pubkeys); } -export function ensureNotifyContentMentions(draft: EventTemplate) { - const mentions = getPubkeysMentionedInContent(draft.content); - return mentions.length > 0 ? ensureNotifyPubkeys(draft, mentions) : draft; -} - export function getAllEventsMentionedInContent(content: string) { const matched = content.matchAll(getMatchNostrLink()); @@ -146,63 +116,3 @@ export function getAllEventsMentionedInContent(content: string) { return events; } -export function ensureTagContentMentions(draft: EventTemplate) { - const mentions = getAllEventsMentionedInContent(draft.content); - const updated: EventTemplate = { ...draft, tags: Array.from(draft.tags) }; - - for (const pointer of mentions) { - updated.tags = AddEtag(updated.tags, pointer.id, pointer.relays?.[0] ?? "", "mention", false); - updated.tags = AddQuotePointerTag(updated.tags, pointer); - } - - return updated; -} - -/** @deprecated use includeContentHashtags from applesauce-factory instead */ -export function createHashtagTags(draft: EventTemplate) { - const updatedDraft: EventTemplate = { ...draft, tags: Array.from(draft.tags) }; - - // create tags for all occurrences of #hashtag - const matches = updatedDraft.content.matchAll(getMatchHashtag()); - for (const [_, space, hashtag] of matches) { - const lower = hashtag.toLocaleLowerCase(); - if (!updatedDraft.tags.find((t) => t[0] === "t" && t[1] === lower)) { - updatedDraft.tags.push(["t", lower]); - } - } - - return updatedDraft; -} - -export function createEmojiTags(draft: EventTemplate, emojis: Emoji[]) { - const updatedDraft: EventTemplate = { ...draft, tags: Array.from(draft.tags) }; - - // create tags for all occurrences of #hashtag - const matches = updatedDraft.content.matchAll(getMatchEmoji()); - for (const [_, name] of matches) { - const emoji = emojis.find((e) => e.name === name); - if (emoji?.url) { - updatedDraft.tags = addTag(updatedDraft.tags, ["emoji", emoji.name, emoji.url]); - } - } - - return updatedDraft; -} - -export function setZapSplit(draft: EventTemplate, split: EventSplit) { - const updatedDraft: EventTemplate = { ...draft, tags: Array.from(draft.tags) }; - - // TODO: get best input relay for user - const zapTags = split.map((p) => ["zap", p.pubkey, "", String(p.percent * 100)]); - updatedDraft.tags.push(...zapTags); - - return updatedDraft; -} - -/** @deprecated use event factory instead */ -export function finalizeNote(draft: EventTemplate) { - let updated: EventTemplate = { ...draft, tags: Array.from(draft.tags) }; - updated.content = correctContentMentions(updated.content); - updated = ensureTagContentMentions(updated); - return updated; -} diff --git a/src/helpers/nostr/stream.ts b/src/helpers/nostr/stream.ts index d0a8c7e49..2ca0d0570 100644 --- a/src/helpers/nostr/stream.ts +++ b/src/helpers/nostr/stream.ts @@ -1,9 +1,6 @@ -import { kinds } from "nostr-tools"; -import { getEventPointerFromETag, getTagValue, safeRelayUrl, unixNow } from "applesauce-core/helpers"; +import { getEventPointerFromETag, getTagValue, safeRelayUrl } from "applesauce-core/helpers"; -import { DraftNostrEvent, NostrEvent, isPTag } from "../../types/nostr-event"; -import { ensureNotifyContentMentions } from "./post"; -import { getEventCoordinate } from "./event"; +import { NostrEvent, isPTag } from "../../types/nostr-event"; export type StreamStatus = "live" | "ended" | "planned"; @@ -80,16 +77,3 @@ export function getStreamParticipants(stream: NostrEvent) { export function getStreamHashtags(stream: NostrEvent) { return stream.tags.filter((t) => t[0] === "t").map((t) => t[1]); } - -export function buildChatMessage(stream: NostrEvent, content: string) { - let draft: DraftNostrEvent = { - tags: [["a", getEventCoordinate(stream), "", "root"]], - content, - created_at: unixNow(), - kind: kinds.LiveChatMessage, - }; - - draft = ensureNotifyContentMentions(draft); - - return draft; -} diff --git a/src/helpers/nostr/zaps.ts b/src/helpers/nostr/zaps.ts index 7c28b2d62..7f39d6b8a 100644 --- a/src/helpers/nostr/zaps.ts +++ b/src/helpers/nostr/zaps.ts @@ -41,17 +41,3 @@ export function isProfileZap(event: NostrEvent) { export function totalZaps(zaps: NostrEvent[]) { return zaps.map(getZapPayment).reduce((t, p) => t + (p?.amount ?? 0), 0); } - -export type EventSplit = { pubkey: string; percent: number; relay?: string }[]; -export function getZapSplits(event: NostrEvent, fallbackPubkey?: string): EventSplit { - const tags = event.tags.filter((t) => t[0] === "zap" && t[1] && t[3]) as [string, string, string, string][]; - - if (tags.length > 0) { - const targets = tags - .map((t) => ({ pubkey: t[1], relay: t[2], percent: parseFloat(t[3]) })) - .filter((p) => Number.isFinite(p.percent)); - - const total = targets.reduce((v, p) => v + p.percent, 0); - return targets.map((p) => ({ ...p, percent: p.percent / total })); - } else return [{ pubkey: fallbackPubkey || event.pubkey, relay: "", percent: 1 }]; -} diff --git a/src/providers/global/event-factory-provider.tsx b/src/providers/global/event-factory-provider.tsx index 73cab02cb..53b6cff23 100644 --- a/src/providers/global/event-factory-provider.tsx +++ b/src/providers/global/event-factory-provider.tsx @@ -1,8 +1,8 @@ +import { PropsWithChildren } from "react"; import { useObservable } from "applesauce-react/hooks"; import { FactoryProvider } from "applesauce-react/providers"; import eventFactoryService from "../../services/event-factory"; -import { PropsWithChildren } from "react"; export default function EventFactoryProvider({ children }: PropsWithChildren) { const factory = useObservable(eventFactoryService.subject); diff --git a/src/providers/global/index.tsx b/src/providers/global/index.tsx index 5b97e982a..ad993750c 100644 --- a/src/providers/global/index.tsx +++ b/src/providers/global/index.tsx @@ -34,19 +34,19 @@ export const GlobalProviders = ({ children }: { children: React.ReactNode }) => - - - - - - + + + + + + {children} - - - - - - + + + + + + diff --git a/src/providers/global/publish-provider.tsx b/src/providers/global/publish-provider.tsx index 5cae63df9..6e321aebb 100644 --- a/src/providers/global/publish-provider.tsx +++ b/src/providers/global/publish-provider.tsx @@ -69,25 +69,7 @@ export default function PublishProvider({ children }: PropsWithChildren) { const outBoxes = useUserOutbox(account?.pubkey); const finalizeDraft = useCallback( - async (event: EventTemplate | NostrEvent) => { - let draft = cloneEvent(event.kind, event); - - // add client tag - if ( - localSettings.addClientTag.value && - !NEVER_ATTACH_CLIENT_TAG.includes(draft.kind) && - !draft.tags.some((t) => t[0] === "client") - ) { - // TODO: this should be removed when all events are created using the event factory - draft = await includeClientTag( - NIP_89_CLIENT_APP.name, - NIP_89_CLIENT_APP.address ? { ...NIP_89_CLIENT_APP.address, kind: kinds.Handlerinformation } : undefined, - )(draft, {}); - } - - // request signature - return await signerFinalize(draft); - }, + (event: EventTemplate | NostrEvent) => signerFinalize(event), [signerFinalize], ); diff --git a/src/services/event-factory.ts b/src/services/event-factory.ts index acceaa5e7..7519dd164 100644 --- a/src/services/event-factory.ts +++ b/src/services/event-factory.ts @@ -1,9 +1,12 @@ import { BehaviorSubject, Observable } from "rxjs"; import { EventFactory } from "applesauce-factory"; +import { combineLatest } from "rxjs"; + import { Account } from "../classes/accounts/account"; import { getEventRelayHint, getPubkeyRelayHint } from "./relay-hints"; import { NIP_89_CLIENT_APP } from "../const"; import accountService from "./account"; +import localSettings from "./local-settings"; class EventFactoryService { subject = new BehaviorSubject(null); @@ -12,22 +15,20 @@ class EventFactoryService { return this.subject.value; } - constructor(account: Observable) { - account.subscribe((current) => { - if (!current) this.subject.next(null); - else - this.subject.next( - new EventFactory({ - signer: current.signer, - getRelayHint: getEventRelayHint, - getPubkeyRelayHint: getPubkeyRelayHint, - client: NIP_89_CLIENT_APP, - }), - ); + constructor(account: Observable, includeClient: Observable) { + combineLatest([account, includeClient]).subscribe(([current, client]) => { + this.subject.next( + new EventFactory({ + signer: current ? current.signer : undefined, + getRelayHint: getEventRelayHint, + getPubkeyRelayHint: getPubkeyRelayHint, + client: client ? NIP_89_CLIENT_APP : undefined, + }), + ); }); } } -const eventFactoryService = new EventFactoryService(accountService.current); +const eventFactoryService = new EventFactoryService(accountService.current, localSettings.addClientTag); export default eventFactoryService; diff --git a/src/views/streams/stream/stream-chat/stream-chat-form.tsx b/src/views/streams/stream/stream-chat/stream-chat-form.tsx index 698625165..5c5d87a52 100644 --- a/src/views/streams/stream/stream-chat/stream-chat-form.tsx +++ b/src/views/streams/stream/stream-chat/stream-chat-form.tsx @@ -2,10 +2,12 @@ import { useMemo, useRef } from "react"; import { Box, Button, Flex, useToast } from "@chakra-ui/react"; import { useForm } from "react-hook-form"; import { NostrEvent } from "nostr-tools"; +import { useEventFactory } from "applesauce-react/hooks"; +import { LiveChatMessageBlueprint } from "applesauce-factory/blueprints"; +import { Emoji } from "applesauce-core/helpers"; -import { buildChatMessage, getStreamHost } from "../../../../helpers/nostr/stream"; +import { getStreamHost } from "../../../../helpers/nostr/stream"; import { unique } from "../../../../helpers/array"; -import { createEmojiTags, ensureNotifyContentMentions } from "../../../../helpers/nostr/post"; import { useContextEmojis } from "../../../../providers/global/emoji-provider"; import { MagicInput, RefType } from "../../../../components/magic-textarea"; import StreamZapButton from "../../components/stream-zap-button"; @@ -19,22 +21,27 @@ import InsertGifButton from "../../../../components/gif/insert-gif-button"; export default function ChatMessageForm({ stream, hideZapButton }: { stream: NostrEvent; hideZapButton?: boolean }) { const toast = useToast(); const publish = usePublishEvent(); + const factory = useEventFactory(); const emojis = useContextEmojis(); const streamRelays = useReadRelays(useAdditionalRelayContext()); const host = getStreamHost(stream); const hostReadRelays = useUserInbox(host); - const relays = useMemo(() => unique([...streamRelays, ...(hostReadRelays ?? [])]), [hostReadRelays, streamRelays]); + const writeRelays = useMemo( + () => unique([...streamRelays, ...(hostReadRelays ?? [])]), + [hostReadRelays, streamRelays], + ); const { setValue, handleSubmit, formState, reset, getValues, watch } = useForm({ defaultValues: { content: "" }, }); const sendMessage = handleSubmit(async (values) => { try { - let draft = buildChatMessage(stream, values.content); - draft = ensureNotifyContentMentions(draft); - draft = createEmojiTags(draft, emojis); - const pub = await publish("Send Chat", draft, relays); + const draft = await factory.create(LiveChatMessageBlueprint, stream, values.content, { + emojis: emojis.filter((e) => !!e.url) as Emoji[], + }); + + const pub = await publish("Send Chat", draft, writeRelays); if (pub) reset(); } catch (error) { if (error instanceof Error) toast({ description: error.message, status: "error" }); diff --git a/src/views/thread/components/reply-form.tsx b/src/views/thread/components/reply-form.tsx index 3262d0d70..dbf526ad4 100644 --- a/src/views/thread/components/reply-form.tsx +++ b/src/views/thread/components/reply-form.tsx @@ -1,26 +1,16 @@ import { useMemo, useRef } from "react"; import { Box, Button, ButtonGroup, Flex } from "@chakra-ui/react"; import { useForm } from "react-hook-form"; -import { useThrottle } from "react-use"; +import { useAsync, useThrottle } from "react-use"; import { kinds } from "nostr-tools"; import { ThreadItem } from "applesauce-core/queries"; -import dayjs from "dayjs"; +import { useEventFactory } from "applesauce-react/hooks"; +import { Emoji } from "applesauce-core/helpers"; import { NostrEvent } from "../../../types/nostr-event"; -import { UserAvatarStack } from "../../../components/compact-user-stack"; -import { getThreadMembers } from "../../../helpers/thread"; -import { - addReplyTags, - createEmojiTags, - ensureNotifyPubkeys, - finalizeNote, - getPubkeysMentionedInContent, -} from "../../../helpers/nostr/post"; -import useCurrentAccount from "../../../hooks/use-current-account"; import MagicTextArea, { RefType } from "../../../components/magic-textarea"; import { useContextEmojis } from "../../../providers/global/emoji-provider"; import { TrustProvider } from "../../../providers/local/trust-provider"; -import { unique } from "../../../helpers/array"; import { usePublishEvent } from "../../../providers/global/publish-provider"; import { TextNoteContents } from "../../../components/note/timeline-note/text-note-contents"; import useCacheForm from "../../../hooks/use-cache-form"; @@ -37,10 +27,10 @@ export type ReplyFormProps = { export default function ReplyForm({ item, onCancel, onSubmitted, replyKind = kinds.ShortTextNote }: ReplyFormProps) { const publish = usePublishEvent(); - const account = useCurrentAccount(); + const factory = useEventFactory(); const emojis = useContextEmojis(); + const customEmojis = useMemo(() => emojis.filter((e) => !!e.url) as Emoji[], [emojis]); - const threadMembers = useMemo(() => getThreadMembers(item, account?.pubkey), [item, account?.pubkey]); const { setValue, getValues, watch, handleSubmit, formState, reset } = useForm({ defaultValues: { content: "", @@ -50,32 +40,31 @@ export default function ReplyForm({ item, onCancel, onSubmitted, replyKind = kin const clearCache = useCacheForm<{ content: string }>(`reply-${item.event.id}`, getValues, reset, formState); - const contentMentions = getPubkeysMentionedInContent(getValues().content); - const notifyPubkeys = unique([...threadMembers, ...contentMentions]); - watch("content"); const textAreaRef = useRef(null); const insertText = useTextAreaInsertTextWithForm(textAreaRef, getValues, setValue); const { onPaste } = useTextAreaUploadFile(insertText); - const draft = useMemo(() => { - let updated = finalizeNote({ kind: replyKind, content: getValues().content, created_at: dayjs().unix(), tags: [] }); - updated = createEmojiTags(updated, emojis); - updated = addReplyTags(updated, item.event); - updated = ensureNotifyPubkeys(updated, notifyPubkeys); - return updated; - }, [getValues().content, emojis]); - const submit = handleSubmit(async (values) => { - const pub = await publish("Reply", { ...draft, created_at: dayjs().unix() }); + const draft = await factory.noteReply(item.event, values.content, { + emojis: customEmojis, + }); + + const pub = await publish("Reply", draft); if (pub && onSubmitted) onSubmitted(pub.event); clearCache(); }); const formRef = useRef(null); - const previewDraft = useThrottle(draft, 500); + + // throttle preview + const throttleValues = useThrottle(getValues(), 500); + const { value: preview } = useAsync( + () => factory.noteReply(item.event, throttleValues.content, { emojis: customEmojis }), + [throttleValues, customEmojis], + ); return ( @@ -96,7 +85,6 @@ export default function ReplyForm({ item, onCancel, onSubmitted, replyKind = kin - {onCancel && } - {previewDraft.content.length > 0 && ( + {preview && preview.content.length > 0 && ( - + )}