diff --git a/.eslintrc.json b/.eslintrc.json index 4adfdba8c..a34c81fad 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,6 +7,7 @@ "markdown", "prettier", "react-hooks", + "import", "sort-destructure-keys" ], "extends": [ @@ -67,6 +68,14 @@ "valid-typeof": 2, "import/prefer-default-export": 0, "import/extensions": [0], + "import/no-extraneous-dependencies": [ + "error", + { + "devDependencies": true, // TODO: set to false once React is in the dependencies (not devDependencies) + "optionalDependencies": false, + "peerDependencies": false + } + ], "max-classes-per-file": 0, "camelcase": 0, "react-hooks/rules-of-hooks": 1, diff --git a/.github/workflows/docusaurus.yml b/.github/workflows/docusaurus.yml index b0e087533..0b4884579 100644 --- a/.github/workflows/docusaurus.yml +++ b/.github/workflows/docusaurus.yml @@ -12,8 +12,23 @@ env: jobs: push_docusaurus: runs-on: ubuntu-latest + outputs: + target-version: $${{steps.target-version.outputs}} steps: - - uses: actions/checkout@v3 + - name: Pull stream-chat-react + uses: actions/checkout@v3 + # This part here is cloning a second repository + # While cloning the repository: + # - it clones the repo into the given `path` + # - it checks out the branch defined at `ref` (version tag) + - name: Pull stream-chat-css + uses: actions/checkout@v3 + with: + repository: GetStream/stream-chat-css + path: stream-chat-css + ref: v${{ node -e 'pkg=require("./package.json"); console.log(pkg.dependencies["@stream-io/stream-chat-css"])' }} + - name: Merge docs stream-chat-css + run: bash scripts/merge-stream-chat-css-docs.sh stream-chat-css/docs - name: Push to stream-chat-docusaurus uses: GetStream/push-stream-chat-docusaurus-action@main with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a8b26f15..bb17c7896 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,6 @@ on: - master - main - beta - jobs: release: name: Release diff --git a/.ladle/components.tsx b/.ladle/components.tsx index bb1ffc8ab..35536c791 100644 --- a/.ladle/components.tsx +++ b/.ladle/components.tsx @@ -1,6 +1,7 @@ import type { GlobalProvider } from '@ladle/react'; import React from 'react'; +import '@stream-io/stream-chat-css/dist/v2/css/index.css'; import './styles.css'; // https://ladle.dev/docs/providers diff --git a/.ladle/styles.css b/.ladle/styles.css index 22fa94383..71a7473b7 100644 --- a/.ladle/styles.css +++ b/.ladle/styles.css @@ -1,9 +1,18 @@ .ladle-main { padding: 0 1rem 3rem; } + +.chat-wrapper { + display: flex; + flex-direction: row; +} + .str-chat { height: 700px !important; } +.str-chat-channel { + flex-grow: 1; +} .str-chat-channel-list { overflow: auto; diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d7932327..b7471487d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,147 @@ +# [10.0.0-theming-v2.3](https://github.com/GetStream/stream-chat-react/compare/v10.0.0-theming-v2.2...v10.0.0-theming-v2.3) (2022-09-06) + + +### Bug Fixes + +* include mdast-util-find-and-replace into our CJS bundle ([#1702](https://github.com/GetStream/stream-chat-react/issues/1702)) ([#1703](https://github.com/GetStream/stream-chat-react/issues/1703)) ([8010889](https://github.com/GetStream/stream-chat-react/commit/801088972032dcfd0374b00aa424a5952b7e72ae)), closes [#1698](https://github.com/GetStream/stream-chat-react/issues/1698) +* prevent double submissions in korean ([#1720](https://github.com/GetStream/stream-chat-react/issues/1720)) ([5d781d8](https://github.com/GetStream/stream-chat-react/commit/5d781d896cb9153bcf3554d04714215c0bbf5c12)) +* ThemingV2 beta adjustments ([#1728](https://github.com/GetStream/stream-chat-react/issues/1728)) ([785ee11](https://github.com/GetStream/stream-chat-react/commit/785ee11f736ff1e8698e47c5f1ca3e2b8a222bae)) +* **ThemingV2:** MessageInputFlat missing lodash/zipObject ([#1721](https://github.com/GetStream/stream-chat-react/issues/1721)) ([dd8a457](https://github.com/GetStream/stream-chat-react/commit/dd8a4576483d114d1c8f0f6c9bb383896d926a97)) +* **Vite:** add emoji-mart (emoji, picker) re-export ([#1724](https://github.com/GetStream/stream-chat-react/issues/1724)) ([c90cf4b](https://github.com/GetStream/stream-chat-react/commit/c90cf4bfc6b6aa74233fd041200e8180a70604e4)) + + +### Features + +* increase and support overriding jump to message limit ([#1718](https://github.com/GetStream/stream-chat-react/issues/1718)) ([8c720f4](https://github.com/GetStream/stream-chat-react/commit/8c720f41e349f753a126ad5e062c1475e3893771)) +* **ThemingV2:** PopperTooltip component ([#1714](https://github.com/GetStream/stream-chat-react/issues/1714)) ([9b6301e](https://github.com/GetStream/stream-chat-react/commit/9b6301e9f7089560ba4cfe694583bd93c1aa10de)) + +# [10.0.0-theming-v2.2](https://github.com/GetStream/stream-chat-react/compare/v10.0.0-theming-v2.1...v10.0.0-theming-v2.2) (2022-08-22) + + +### Features + +* add svg image class to attachment ([3d0237d](https://github.com/GetStream/stream-chat-react/commit/3d0237d3dad20b4aeb605c8bf78c7165f7469fd8)) + +# [10.0.0-theming-v2.1](https://github.com/GetStream/stream-chat-react/compare/v9.4.0...v10.0.0-theming-v2.1) (2022-08-18) + + +### Bug Fixes + +* add str-chat__message--other class to MessageDeleted ([25f3190](https://github.com/GetStream/stream-chat-react/commit/25f3190cab15e190f13bcd67ad6b4776f5e5addc)) +* **Card:** prefer title_link over og_scrape_url ([843990e](https://github.com/GetStream/stream-chat-react/commit/843990e034a1dcccac0ed17a5f3c86ff73200301)) +* do not generate class names to contain string 'undefined', do not pass Media prop to Card ([40342fe](https://github.com/GetStream/stream-chat-react/commit/40342fe5ef4e78a228e653cda25a6cb08600ba7b)) +* **EditMessageForm:** remove circular dependency ([6218a65](https://github.com/GetStream/stream-chat-react/commit/6218a651cbcd5df0a8f10dbf7874ee1abe1c2278)) +* File attachment UI in theme-v1 ([0a80bef](https://github.com/GetStream/stream-chat-react/commit/0a80bef1948559ed10f1f7d2fa0bc542a727a4d9)) +* File attachment UI in theme-v1 ([9604ca6](https://github.com/GetStream/stream-chat-react/commit/9604ca6f79fcd74d87c019352c8e5a155b00ab59)) +* **FilePreviewItem:** add file type for correct file icons ([6e6fce5](https://github.com/GetStream/stream-chat-react/commit/6e6fce5cba110bcf92cc365219d038355879ce37)) +* improve scrollToBottom with image attachments ([be8bb7a](https://github.com/GetStream/stream-chat-react/commit/be8bb7ae4a986cb6556674c1bec896c5539bb822)) +* include mdast-util-find-and-replace into our CJS bundle ([#1702](https://github.com/GetStream/stream-chat-react/issues/1702)) ([61c4eec](https://github.com/GetStream/stream-chat-react/commit/61c4eecf5c03ab36109a94b7afa5f678e99fcc8b)), closes [#1698](https://github.com/GetStream/stream-chat-react/issues/1698) +* **MessageInput:** add container className ([a5e7908](https://github.com/GetStream/stream-chat-react/commit/a5e7908e0e7d9e86cfa0d82b52b274ee75ac3d65)) +* **MessageInputFlat:** send button adjustments ([f456704](https://github.com/GetStream/stream-chat-react/commit/f456704a1b48a87612cc94b5bf0015a382e16783)) +* **MessageInput:** remove useId, add quotedMessage patch ([64e07d3](https://github.com/GetStream/stream-chat-react/commit/64e07d36a8589c2ceb5344b7c23b808808580a39)) +* **MessageInput:** update dropzone markup ([974802b](https://github.com/GetStream/stream-chat-react/commit/974802bebee8d0128772031e719868d9bfb4f797)) +* **MessageList:** prevent redundant calls to scroll to bottom, don't use ResizeObserver ([363676e](https://github.com/GetStream/stream-chat-react/commit/363676ea91a31da1991504233add53e05f500581)) +* **MessageStatus:** add V2 TooltipContainer component "shim" ([dcfbbfb](https://github.com/GetStream/stream-chat-react/commit/dcfbbfbc0d626df23f7e7cf8b53fab059fedbecd)) +* move card caption to card content and rename to source link ([c44bcd7](https://github.com/GetStream/stream-chat-react/commit/c44bcd771ce5fd677bf27fd3949c1d503941495f)) +* **QuotedMessagePreview:** use themingVersion instead of PreviewHeader property ([4f79b07](https://github.com/GetStream/stream-chat-react/commit/4f79b07d840c332ca14fc38fc5dd1dee43a4f1dd)) +* reduce mount/unmount of image attachments ([34082a4](https://github.com/GetStream/stream-chat-react/commit/34082a4600281a77c8792a1aad8a3c75b7bcd3c5)) +* replace FileReader with URL.createObjectURL ([#1701](https://github.com/GetStream/stream-chat-react/issues/1701)) ([c8a490e](https://github.com/GetStream/stream-chat-react/commit/c8a490ebc53da03c2b0f064de88c0cb634ed2a70)) +* Responsive layout ([7551650](https://github.com/GetStream/stream-chat-react/commit/7551650b3541d8dbd221659b0059cfd1e79c73c8)) +* **SuggestionList:** update trigger limits ([2e1f025](https://github.com/GetStream/stream-chat-react/commit/2e1f025e2261be1f1c22239a6f2d06584b767351)) +* sync event listener keyDown type btw the image attachment and gallery modal ([51e7c14](https://github.com/GetStream/stream-chat-react/commit/51e7c14183c08b07792f7afb15606ab9affe9c34)) +* **TypingIndicator:** adjust position of the indicator ([f5db199](https://github.com/GetStream/stream-chat-react/commit/f5db199593806e2f5ec46c742765d41f6c128547)) +* **TypingIndicator:** use MessageListMainPanel to position the indicator ([865cbc8](https://github.com/GetStream/stream-chat-react/commit/865cbc8f2e0e600307e912b8d888441de3f9a1ac)) + + +* Merge pull request #1697 from GetStream/theming-v2-user-testing ([2c133ad](https://github.com/GetStream/stream-chat-react/commit/2c133ad2a511773c0381d1edd9dbd7fe892e485e)), closes [#1697](https://github.com/GetStream/stream-chat-react/issues/1697) +* Remove useMobilePress and useBreakpoint hooks (#1648) ([430bf24](https://github.com/GetStream/stream-chat-react/commit/430bf24db033c7a9010f259dc5fc326b1c768206)), closes [#1648](https://github.com/GetStream/stream-chat-react/issues/1648) + + +### Features + +* adapt MessageOptions to theming v2 ([23c2d93](https://github.com/GetStream/stream-chat-react/commit/23c2d93cc2452f5711625a5a198d01d1a2d9d7c0)) +* adapt MessageStatus to theming v2 ([a5b0fae](https://github.com/GetStream/stream-chat-react/commit/a5b0fae589ae72eff24436ec06d49d16aad22106)) +* add "str-chat__message--error-message" class to message error div ([c4f7520](https://github.com/GetStream/stream-chat-react/commit/c4f7520aa0a113547a1f44834dcf9636df42fc00)) +* add Attachment icons for theming v2 ([97e8047](https://github.com/GetStream/stream-chat-react/commit/97e8047651b788d5eb588da99c9e7e0c67657873)) +* add Card component for theming v2 ([5f5341f](https://github.com/GetStream/stream-chat-react/commit/5f5341ff112658c75675cbf2a77b76aa80f330a3)) +* add class "str-chat__message-sender-avatar" to Avatar root to display it for sender only ([ecd0b4b](https://github.com/GetStream/stream-chat-react/commit/ecd0b4b255023d3f665dfebb651ea69e5b5885a2)) +* add class str-chat__message-list-scroll to virtuoso root element ([7d2284e](https://github.com/GetStream/stream-chat-react/commit/7d2284ea0dbc9342d1b98247980b23b83aa80472)) +* add common IconProps type ([3ff89bb](https://github.com/GetStream/stream-chat-react/commit/3ff89bb75a7204e0805e88ee61988a172932a394)) +* add FileAttachment component for theming v2 ([52acd80](https://github.com/GetStream/stream-chat-react/commit/52acd80b6b7ccd939aae357b0019f1b99975f585)) +* add group styles to virtualized message list items ([ff2044e](https://github.com/GetStream/stream-chat-react/commit/ff2044e7414bb57644767d3288d96cb76594646d)) +* add Message icons for theming v2 - MessageDeliveredIcon, MessageErrorIcon ([9ff9034](https://github.com/GetStream/stream-chat-react/commit/9ff9034aa036a382326bb737fc68cc28664ed856)) +* add ModalGallery to the ComponentContext ([27e149a](https://github.com/GetStream/stream-chat-react/commit/27e149a64ad0cbf05342e8a0132fb03c153da7eb)) +* add realistic giphy attachment generator ([e4c2a7b](https://github.com/GetStream/stream-chat-react/commit/e4c2a7bde291a9fdad51b336abadd233998558ec)) +* add str-chat__simple-message--error-failed class to str-chat__message-inner ([a5f8f94](https://github.com/GetStream/stream-chat-react/commit/a5f8f94a50239fbe8b3bc24c6cdb5c889d613193)) +* add str-chat-react__modal__inner class to str-chat__modal__inner ([0234522](https://github.com/GetStream/stream-chat-react/commit/0234522547d7d68754ae4cc7ba1e1bcfab509626)) +* add theme v2 class to CustomNotification ([037dc89](https://github.com/GetStream/stream-chat-react/commit/037dc89e7dd5035f162da610f02fbb575bd8efcb)) +* add theme v2 to Reaction components, extract shared logic ReactionList & SimpleReactionList ([f6a12d0](https://github.com/GetStream/stream-chat-react/commit/f6a12d0ca7b2fa939ba2a3f4319d2e02ff4daabe)) +* add themeVersion flag to ChatProps & ChatContext ([70cbfcb](https://github.com/GetStream/stream-chat-react/commit/70cbfcbce643b230d8f7c66f615b4cc9bde9b816)) +* add ThemeVersion type to ChatContext ([695f30a](https://github.com/GetStream/stream-chat-react/commit/695f30a0f6137c4c84f07ef8528e97f6666a40db)) +* add theming v2 changes for channel and channel header ([#1632](https://github.com/GetStream/stream-chat-react/issues/1632)) ([3f8fddb](https://github.com/GetStream/stream-chat-react/commit/3f8fddba0ad361fe00c39191324ec18de902ace5)) +* add theming v2 classes to MessageActions elements ([d62e04a](https://github.com/GetStream/stream-chat-react/commit/d62e04a8791338c5a4eef4b44a5a1e210bd6e2d7)) +* add theming v2 classes to QuotedMessage ([8c5d2ff](https://github.com/GetStream/stream-chat-react/commit/8c5d2ffd2517375dcad67a740207f0d76d720d4b)) +* add theming v2 classes to ReactionSelector & ReactionList ([fbedb42](https://github.com/GetStream/stream-chat-react/commit/fbedb42cf565922ac59b64779d70c64ecc074511)) +* add v2 classes to send and cancel button of EditMessageForm ([#1669](https://github.com/GetStream/stream-chat-react/issues/1669)) ([ab75c2c](https://github.com/GetStream/stream-chat-react/commit/ab75c2c8b51a54384d327fc97c0db3151ffaa10c)) +* adjust Audio widget for theming v2 ([f08c6f5](https://github.com/GetStream/stream-chat-react/commit/f08c6f5f3d06d856f2e0946a6fda8475c9c637c8)) +* adjust Gallery and Image widget for theming v2 ([de29a73](https://github.com/GetStream/stream-chat-react/commit/de29a7337d7472ac2a8541632a03f829beecee0c)) +* adjust MessageRepliesCountButton to theming v2, add classes ([5076fd5](https://github.com/GetStream/stream-chat-react/commit/5076fd5163e3394a0881b5fe7c1ac0f1252038fc)) +* adjust MessageSimple for theming v2 ([ebd4bd7](https://github.com/GetStream/stream-chat-react/commit/ebd4bd7758ae0afaf3547e281377f46b3b5dc6ab)) +* allow card image enlargement in modal ([47bf301](https://github.com/GetStream/stream-chat-react/commit/47bf301d75c4ad71bcbbc8f19f2dd28ecd136364)) +* apply theme-v2 to channel list and preview ([#1603](https://github.com/GetStream/stream-chat-react/issues/1603)) ([cc88f1f](https://github.com/GetStream/stream-chat-react/commit/cc88f1fe5937fd837b70281d642db2c8af6e4159)) +* change the close icon for modal and remove Close text ([88a5f7c](https://github.com/GetStream/stream-chat-react/commit/88a5f7c6659e520b629bf735e3dba395c452ac5e)) +* compute the themeVersion value, remove themeVersion Chat prop ([3421087](https://github.com/GetStream/stream-chat-react/commit/34210879aaede5ecf63dc4c85da47440372af058)) +* convert attachment render functions into components, group attachments in order ([aeee078](https://github.com/GetStream/stream-chat-react/commit/aeee07884310043d08594a767b4a9c735bf2cfc6)) +* do not sanitize attachment scrape urls ([aa1624a](https://github.com/GetStream/stream-chat-react/commit/aa1624a619a89351217ad8e5512b4471512db4ba)) +* extract CardAudio and render only uploaded audio data in Audio component ([8027908](https://github.com/GetStream/stream-chat-react/commit/80279083cbd336cf3a8de2a5ce4658e1926aa838)) +* include the parent message in virtualized scrollable message list ([dd63427](https://github.com/GetStream/stream-chat-react/commit/dd63427376460ca964b8560fc6086adbe8049fa2)) +* message is considered top if it has reactions and bottom if the next message has reactions ([638aead](https://github.com/GetStream/stream-chat-react/commit/638aead0c3dd8202653d25351402f73c84f23909)) +* **MessageInput:** add drag & drop upload functionality ([e731b67](https://github.com/GetStream/stream-chat-react/commit/e731b676af8a9b9bfc81e87a0a3b5af506ab4ea0)) +* **ProgressBar:** add "seeking" feature to the progress bar ([0320864](https://github.com/GetStream/stream-chat-react/commit/032086456bb76a36f51fa701faf5f5fb60671548)) +* **ProgressBar:** add onClick property ([4d9d06c](https://github.com/GetStream/stream-chat-react/commit/4d9d06c9fa3aa2426fb61d78dc331856db6a02da)) +* remove avatar from the thread header ([dec0d8d](https://github.com/GetStream/stream-chat-react/commit/dec0d8ddaff5d87d130aa08e90f68b9270da145a)) +* remove deprecated components: MessageCommerce, MessageLivestream, MessageTeam ([9d75fb8](https://github.com/GetStream/stream-chat-react/commit/9d75fb892e20cbdfd705cc94210c2ae12a5f9650)) +* remove translations for deprecated components: MessageCommerce, MessageLivestream, MessageTeam ([e524d0a](https://github.com/GetStream/stream-chat-react/commit/e524d0a986c0f417d3a0c1106d1134856b32d5eb)) +* render cards for each attachment with scraped data ([0a59806](https://github.com/GetStream/stream-chat-react/commit/0a598067bf1dad6a8bb4451ba2338ec3ec0d5d9c)) +* show always ScrollToBottomButton on scroll up and show unread message count ([e554356](https://github.com/GetStream/stream-chat-react/commit/e5543560c8ae52c38980892cdc4bd86c201a9b58)) +* stop using FixedHeightMessage as default VirtualMessage component ([fc67915](https://github.com/GetStream/stream-chat-react/commit/fc67915f36c31c4302b325250376d8de8cb26eb2)) +* switch ladle to v2 ([ecd1cc6](https://github.com/GetStream/stream-chat-react/commit/ecd1cc6e2774ace8daed2e364a44db1d7ba48179)) +* **theming-v2:** add channel search for theme v2 ([#1685](https://github.com/GetStream/stream-chat-react/issues/1685)) ([b735c30](https://github.com/GetStream/stream-chat-react/commit/b735c30817e0113aec58761aa166351fae5691b9)), closes [#1669](https://github.com/GetStream/stream-chat-react/issues/1669) +* **TypingIndicator:** add translations ([f079e26](https://github.com/GetStream/stream-chat-react/commit/f079e26a48942b1a945e2e00e09ae1f2810ae427)) +* update message componets with theme v2 designs ([e5192d5](https://github.com/GetStream/stream-chat-react/commit/e5192d59b29625a3b961b4a130f1368de0f8a5d7)) +* use FileIcon with version in UploadsPreview ([4d150b1](https://github.com/GetStream/stream-chat-react/commit/4d150b108ef8bc406800ad7bd7edc1c4f8afcc66)) +* wrap ThreadHead content in a div to enable styling for class str-chat__parent-message-li ([9323edb](https://github.com/GetStream/stream-chat-react/commit/9323edb46aa0bf0c83d23817698756d9cbdf2da4)) + + +### Reverts + +* fix: File attachment UI in theme-v1 ([789dd27](https://github.com/GetStream/stream-chat-react/commit/789dd273b016be42f72baeeb0423f79f380e05eb)) + + +### BREAKING CHANGES + +* ThemingV2 - user testing and adjustments +* useMobilePress and useBreakpoint hooks are removed. + +useMobilePress: +Historically, this hook programmatically handled the user interaction with Message components +by toggling `mobile-press` class upon user interaction. +The goal of this operation was to have the message actions displayed on the screen. +Internally, we found a better solution by offloading this behavior to the browser and +utilizing `:focus` and `:focus-within` CSS pseudo-selectors. + +useBreakpoint: +This hook did hold the "programmatic" responsive UI breakpoints. +We realized they aren't always in line with our stylesheet breakpoints and possibly with our +customer's breakpoints. This misalignment was causing some inconsistencies and issues. +We are removing this hook because we believe defining UI breakpoints should be +responsibility of our customers. + +SearchResults: +During the refactoring, we stumbled upon one side-effect where `popupResults` prop +wasn't always respected. The fix of it could be a breaking change for a small percentage +of our customers, but we believe this fix is the right thing to do. + ## [9.5.1](https://github.com/GetStream/stream-chat-react/compare/v9.5.0...v9.5.1) (2022-09-08) diff --git a/commitlint.config.js b/commitlint.config.js index 067351c97..cc6666d36 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line no-undef module.exports = { extends: ['@commitlint/config-conventional'], rules: { diff --git a/docusaurus/docs/React/_docusaurus-components/GHComponentLink.jsx b/docusaurus/docs/React/_docusaurus-components/GHComponentLink.jsx new file mode 100644 index 000000000..844b95368 --- /dev/null +++ b/docusaurus/docs/React/_docusaurus-components/GHComponentLink.jsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const GHComponentLink = ({text, path}) => { + return {text} +} + +export default GHComponentLink; diff --git a/docusaurus/docs/React/contexts/_category_.json b/docusaurus/docs/React/contexts/_category_.json index 4da0a3a00..0140d2013 100644 --- a/docusaurus/docs/React/contexts/_category_.json +++ b/docusaurus/docs/React/contexts/_category_.json @@ -1,4 +1,4 @@ { "label": "React Contexts", - "position": 8 + "position": 9 } diff --git a/docusaurus/docs/React/contexts/component-context.mdx b/docusaurus/docs/React/contexts/component-context.mdx index 818242428..4685c9594 100644 --- a/docusaurus/docs/React/contexts/component-context.mdx +++ b/docusaurus/docs/React/contexts/component-context.mdx @@ -259,6 +259,14 @@ Custom UI component for send button. | --------- | -------------------------------------------------------------------------------------------------------------- | | component | [SendButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/icons.tsx) | +### ThreadHead + +Custom UI component to be displayed at the beginning of a thread. By default it is the thread parent message. It is composed of [Message](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/Message.tsx) context provider component and [ThreadStart](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Thread/ThreadStart.tsx) component. The latter can be customized by passing custom component to `Channel` props. The `ThreadHead` component defaults to and accepts the same props as [MessageSimple](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageSimple.tsx). + +| Type | Default | +| --------- | ---------------------------------------------------------------------------------------------------------------------- | +| component | [ThreadHead](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Thread/ThreadHead.tsx) | + ### ThreadHeader Custom UI component to display the header of a `Thread`. diff --git a/docusaurus/docs/React/custom-code-examples/_category_.json b/docusaurus/docs/React/custom-code-examples/_category_.json index edc85c9dc..429852a05 100644 --- a/docusaurus/docs/React/custom-code-examples/_category_.json +++ b/docusaurus/docs/React/custom-code-examples/_category_.json @@ -1,4 +1,4 @@ { "label": "Custom Code Examples", - "position": 6 + "position": 7 } diff --git a/docusaurus/docs/React/custom-code-examples/channel-search.mdx b/docusaurus/docs/React/custom-code-examples/channel-search.mdx index a55bdd2a9..7490bdf8b 100644 --- a/docusaurus/docs/React/custom-code-examples/channel-search.mdx +++ b/docusaurus/docs/React/custom-code-examples/channel-search.mdx @@ -31,7 +31,7 @@ are drilled to the component via the `additionalChannelSearchProps` prop on `Cha Since we've enabled the search to also search for channels, let's customize the dropdown results container to fit a design that separates the result by type. ```jsx -const CustomDropdown = (props: DropdownContainerProps) => { +const CustomDropdown = (props: SearchResultsListProps) => { const { results, focusedUser, selectResult, SearchResultItem } = props; let items: ChannelOrUserResponse[] = results.filter((x) => x.cid); @@ -67,7 +67,7 @@ const CustomDropdown = (props: DropdownContainerProps) => { ``` ```jsx -const DropDown = (props: DropdownContainerProps) => ; +const DropDown = (props: SearchResultsListProps) => ; const additionalProps = { DropdownContainer: DropDown searchForChannels: true, @@ -198,7 +198,7 @@ const additionalProps = { ``` ```jsx - const CustomDropdown = (props: DropdownContainerProps) => { + const CustomDropdown = (props: SearchResultsListProps) => { const { results, focusedUser, selectResult, SearchResultItem } = props; let items: ChannelOrUserResponse[] = results.filter(x => x.cid); @@ -275,7 +275,7 @@ const additionalProps = { ``` ```jsx - const DropDown = (props: DropdownContainerProps) => ; + const DropDown = (props: SearchResultsListProps) => ; const SearchResult = (props: SearchResultItemProps) => ; const additionalProps = { @@ -338,4 +338,4 @@ const customSearchFunction = async (props: ChannelSearchFunctionParams, event: { showChannelSearch additionalChannelSearchProps={{searchFunction: customSearchFunction}} /> -``` \ No newline at end of file +``` diff --git a/docusaurus/docs/React/hooks/_category_.json b/docusaurus/docs/React/hooks/_category_.json index ee6e1ca80..cd9863311 100644 --- a/docusaurus/docs/React/hooks/_category_.json +++ b/docusaurus/docs/React/hooks/_category_.json @@ -1,4 +1,4 @@ { "label": "Custom Hooks", - "position": 9 + "position": 10 } diff --git a/docusaurus/docs/React/hooks/message-hooks.mdx b/docusaurus/docs/React/hooks/message-hooks.mdx index 2221f8d37..866441165 100644 --- a/docusaurus/docs/React/hooks/message-hooks.mdx +++ b/docusaurus/docs/React/hooks/message-hooks.mdx @@ -12,10 +12,6 @@ The Stream Chat React library provides a variety of useful hooks for use in your A [custom hook](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/hooks/useActionHandler.ts) to handler function to handle when an action is sent in a `Channel`. -### useBreakpoint - -A [custom hook](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/hooks/useBreakpoint.ts) that returns an object with the device type (ex: 'mobile' \| 'tablet' \| 'full') and width, for responsive development. - ### useDeleteHandler A [custom hook](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/hooks/useDeleteHandler.ts) to handle message deletion. @@ -101,10 +97,6 @@ const MyCustomMessageComponent = () => { }; ``` -### useMobileHandler - -A [custom hook](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/hooks/useMobileHandler.ts) to handle click events for mobile devices, returns an event handler. - ### useMuteHandler A [custom hook](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/hooks/useMuteHandler.ts) to handle muting users. Returns an event handler that processes user muting. @@ -117,7 +109,7 @@ const MyCustomMessageComponent = () => { return (
- {message.text + {message.text}
); diff --git a/docusaurus/docs/React/message-components/reactions.mdx b/docusaurus/docs/React/message-components/reactions.mdx index 404266940..1ffbbac95 100644 --- a/docusaurus/docs/React/message-components/reactions.mdx +++ b/docusaurus/docs/React/message-components/reactions.mdx @@ -195,6 +195,14 @@ If true, adds a CSS class that reverses the horizontal positioning of the select ## ReactionsList Props +### additionalEmojiProps + +Additional props to be passed to the [NimbleEmoji](https://github.com/missive/emoji-mart/blob/master/src/components/emoji/nimble-emoji.js) component from `emoji-mart`. + +| Type | +| ------ | +| object | + ### onClick Custom on click handler for an individual reaction in the list (overrides the function stored in `MessageContext`). @@ -203,6 +211,14 @@ Custom on click handler for an individual reaction in the list (overrides the fu | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | (event: React.BaseSyntheticEvent) => Promise \| void | [MessageContextValue['onReactionListClick']](../contexts/message-context.mdx#onreactionlistclick) | +### own_reactions + +An array of the own reaction objects to distinguish own reactions visually (overrides `message.own_reactions` from `MessageContext`). + +| Type | +| ----- | +| array | + ### reaction_counts An object that keeps track of the count of each type of reaction on a message (overrides `message.reaction_counts` from `MessageContext`). @@ -253,6 +269,14 @@ Function that adds/removes a reaction on a message (overrides the function store | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | (reactionType: string, event: React.BaseSyntheticEvent) => Promise | [MessageContextValue['handleReaction']](../contexts/message-context.mdx#handlereaction) | +### own_reactions + +An array of the own reaction objects to distinguish own reactions visually (overrides `message.own_reactions` from `MessageContext`). + +| Type | +| ----- | +| array | + ### reaction_counts An object that keeps track of the count of each type of reaction on a message (overrides `message.reaction_counts` from `MessageContext`). diff --git a/docusaurus/docs/React/resources/_category_.json b/docusaurus/docs/React/resources/_category_.json index beb72b9ec..74c9bff23 100644 --- a/docusaurus/docs/React/resources/_category_.json +++ b/docusaurus/docs/React/resources/_category_.json @@ -1,4 +1,4 @@ { "label": "Resources", - "position": 11 + "position": 12 } diff --git a/docusaurus/docs/React/troubleshooting/_category_.json b/docusaurus/docs/React/troubleshooting/_category_.json index e7ef3ae09..b941f8880 100644 --- a/docusaurus/docs/React/troubleshooting/_category_.json +++ b/docusaurus/docs/React/troubleshooting/_category_.json @@ -1,4 +1,4 @@ { "label": "Troubleshooting", - "position": 10 + "position": 11 } diff --git a/docusaurus/docs/React/utility-components/_category_.json b/docusaurus/docs/React/utility-components/_category_.json index 8387ee6c0..e7d8dfd16 100644 --- a/docusaurus/docs/React/utility-components/_category_.json +++ b/docusaurus/docs/React/utility-components/_category_.json @@ -1,4 +1,4 @@ { "label": "Utility Components", - "position": 7 + "position": 8 } diff --git a/e2e/edit-message.test.ts b/e2e/edit-message.test.ts index 6d2259ea0..a0b9762e5 100644 --- a/e2e/edit-message.test.ts +++ b/e2e/edit-message.test.ts @@ -56,7 +56,7 @@ test.describe('edit text message', () => { user.sees(Attachment.Card).count(2); user.sees(Attachment.Gallery).count(0); - user.sees(Attachment.Image).count(1); + user.sees(Attachment.Image).count(3); }); test('message has only 1 attachment after removing all of the links from the message', async ({ diff --git a/e2e/fixtures.mjs b/e2e/fixtures.mjs index 5145b6dae..55045fd66 100644 --- a/e2e/fixtures.mjs +++ b/e2e/fixtures.mjs @@ -16,25 +16,29 @@ dotenv.config({ path: `.env.local` }); E2E_TEST_USER_2, } = process.env; - async function generateMessages(start, stop, channel, parent_id) { + async function generateMessages({start, stop, channel, parent_id, quoteMap = {}}) { const count = stop - start; - let messageToQuote; + const messagesToQuote = {}; const messageResponses = []; for (let i = start; i < stop; i++) { if (process.stdout.clearLine && process.stdout.cursorTo) { printProgress((i - start) / count); } - + const indexString = i.toString(); + const messageToQuote = messagesToQuote[indexString]; const res = await channel.sendMessage({ text: `Message ${i}`, user: { id: i % 2 ? E2E_TEST_USER_1 : E2E_TEST_USER_2 }, - ...(i === start + 140 ? { quoted_message_id: messageToQuote.message.id } : {}), + ...(messageToQuote ? { quoted_message_id: messageToQuote.message.id } : {}), ...(parent_id ? { parent_id } : {}), }); - if (i === start + 20) { - messageToQuote = res; + if (Object.keys(quoteMap).includes(indexString)) { + const quotingMessageText = quoteMap[indexString]; + messagesToQuote[quotingMessageText] = res; } + + messageResponses.push(res); } return messageResponses; @@ -58,7 +62,12 @@ dotenv.config({ path: `.env.local` }); await channel.create(); await channel.truncate(); - await generateMessages(0, MESSAGES_COUNT, channel); + await generateMessages({ + channel, + quoteMap: {'20': '140'}, + start: 0, + stop: MESSAGES_COUNT + }); process.stdout.write('\n'); } @@ -74,8 +83,42 @@ dotenv.config({ path: `.env.local` }); await channel.create(); await channel.truncate(); - const messages = await generateMessages(0, 150, channel); - await generateMessages(150, 300, channel, messages.slice(-1)[0].message.id); + const messages = await generateMessages({ + channel, + quoteMap: {'99': '149', '137': '148'}, + start:0, + stop: 150 + }); + + await generateMessages({ + channel, + parent_id:messages.slice(-51)[0].message.id, + start: 150, + stop: 300, + }); + + await generateMessages({ + channel, + parent_id:messages.slice(-26)[0].message.id, + start: 150, + stop: 300, + }); + + await generateMessages({ + channel, + parent_id:messages.slice(-13)[0].message.id, + start: 150, + stop: 300, + }); + + await generateMessages({ + channel, + parent_id:messages.slice(-1)[0].message.id, + start: 150, + stop: 300, + }); + + process.stdout.write('\n'); } diff --git a/e2e/navigate-long-message-lists.test.ts b/e2e/navigate-long-message-lists.test.ts index 4ab8012f5..8fe565f70 100644 --- a/e2e/navigate-long-message-lists.test.ts +++ b/e2e/navigate-long-message-lists.test.ts @@ -1,8 +1,9 @@ /* eslint-disable jest/expect-expect */ -import dotenv from 'dotenv'; +import { Page } from '@playwright/test'; +import * as dotenv from 'dotenv'; import selectors from './user/selectors'; -import { test } from './user/test'; +import { CustomTestContext, test } from './user/test'; import ChannelPreview from './user/components/ChannelPreview'; import Message from './user/components/Message/MessageSimple'; @@ -11,6 +12,7 @@ import MessageList from './user/components/MessageList/MessageList'; import MessageNotification, { getMessageNotificationSelector, } from './user/components/MessageList/MessageNotification'; +import QuotedMessage from './user/components/Message/QuotedMessage'; import Thread, { composeThreadSelector } from './user/components/Thread/Thread'; import type { TestingUser } from './user/User'; @@ -25,68 +27,125 @@ const OTHER_USER_ADDED_REPLY_TEXT = 'Reply back' as const; const OTHER_USER_ADDED_MESSAGE_TEXT = "Other user's message" as const; const USER1_CHAT_VIEW_CLASSNAME = `.${user1Id}`; const NEW_MESSAGE_NOTIFICATION_TEXT = 'New Messages!' as const; +const LAST_REPLY_TEXT = 'Message 299'; +const MESSAGES_WITH_REPLIES = ['Message 149', 'Message 137', 'Message 124', 'Message 99']; -test.describe('thread autoscroll', () => { - test.beforeEach(async ({ controller, page, user }) => { - await controller.openStory( - 'navigate-long-message-lists--user1', - selectors.channelPreviewButton, - ); - await user.clicks(ChannelPreview).text(CHANNEL_NAME); - await Promise.all([ - page.waitForResponse((r) => r.url().includes('/replies') && r.ok()), - user.clicks(Thread).open('replies', -1), - ]); - }); - - test.afterEach(async ({ controller, page }) => { - const lastReplyText = await page - .locator( - `${USER1_CHAT_VIEW_CLASSNAME} ${selectors.threadReplyListWithReplies} li:last-of-type ${selectors.messageText}`, - ) - .textContent(); - if (!lastReplyText) return; - if (lastReplyText.match(OTHER_USER_ADDED_REPLY_TEXT)) { - await controller.deleteOtherUserLastReply(); - } else if (lastReplyText.match(MY_ADDED_REPLY_TEXT)) { - await controller.deleteMyLastReply(); - } - }); +const QUOTED_MESSAGES = ['Message 99', 'Message 137']; - test('only if I send a message', async ({ page, user }) => { - const selector = composeThreadSelector(USER1_CHAT_VIEW_CLASSNAME); - await user.sees(Thread).not.isScrolledToBottom(selector); - - await Promise.all([ - page.waitForResponse((r) => r.url().includes('/message') && r.ok()), - user.submits(MessageInput).reply(MY_ADDED_REPLY_TEXT), - ]); +const scrollInSteps = async (user: TestingUser, msgNumbers = ['142', '135', '128'], cycles = 3) => { + for (let i = 0; i < cycles; i++) { + await Promise.all( + msgNumbers.map((num: string) => user.get(Message)(`Message ${num}`).scrollIntoViewIfNeeded()), + ); + } +}; - await user.sees(Thread).isScrolledToBottom(selector); +test.describe('thread autoscroll', () => { + test.describe('on thread open', () => { + test.beforeEach(async ({ controller, user }) => { + await controller.openStory( + 'navigate-long-message-lists--user1', + selectors.channelPreviewButton, + ); + await user.clicks(ChannelPreview).text(CHANNEL_NAME); + }); + + const expectToOpenThreadAndSeeLatestMessage = async ( + page: Page, + user: CustomTestContext['user'], + messageText: string, + ) => { + await Promise.all([ + page.waitForResponse((r) => r.url().includes('/replies') && r.ok()), + user.clicks(Thread).openFor(messageText), + ]); + await user.sees(Message).displayed(LAST_REPLY_TEXT); + }; + + test('if I do not scroll primary msg list', async ({ page, user }) => { + await expectToOpenThreadAndSeeLatestMessage(page, user, MESSAGES_WITH_REPLIES[0]); + }); + + test('if I load more messages by scrolling primary msg list', async ({ page, user }) => { + const message = await user.get(Message)(MESSAGES_WITH_REPLIES[1]); + await message.scrollIntoViewIfNeeded(); + await expectToOpenThreadAndSeeLatestMessage(page, user, MESSAGES_WITH_REPLIES[1]); + }); + + test('if I scroll primary message list by clicking a quoted message already loaded in state', async ({ + page, + user, + }) => { + await user.clicks(QuotedMessage).nth(QUOTED_MESSAGES[0]); + await expectToOpenThreadAndSeeLatestMessage(page, user, QUOTED_MESSAGES[0]); + }); + + test('if I scroll primary message list by clicking a quoted message that has to be loaded in state', async ({ + page, + user, + }) => { + await user.clicks(QuotedMessage).nth(QUOTED_MESSAGES[1], 2); + await Promise.all([ + page.waitForResponse((r) => r.url().includes('/messages') && r.ok()), + expectToOpenThreadAndSeeLatestMessage(page, user, QUOTED_MESSAGES[1]), + ]); + }); }); - test('not if I receive a message', async ({ controller, page, user }) => { - const selector = composeThreadSelector(USER1_CHAT_VIEW_CLASSNAME); - await user.sees(Thread).not.isScrolledToBottom(selector); - - await Promise.all([ - page.waitForResponse((r) => r.url().includes('/message') && r.ok()), - await controller.sendOtherUserReply(), - ]); - await user.sees(Thread).not.isScrolledToBottom(selector); + test.describe('on new message', () => { + test.beforeEach(async ({ controller, page, user }) => { + await controller.openStory( + 'navigate-long-message-lists--user1', + selectors.channelPreviewButton, + ); + await user.clicks(ChannelPreview).text(CHANNEL_NAME); + await Promise.all([ + page.waitForResponse((r) => r.url().includes('/replies') && r.ok()), + user.clicks(Thread).open('replies', -1), + ]); + }); + + test.afterEach(async ({ controller, page }) => { + const lastReplyText = await page + .locator( + `${USER1_CHAT_VIEW_CLASSNAME} ${selectors.threadReplyListWithReplies} li:last-of-type ${selectors.messageText}`, + ) + .textContent(); + if (!lastReplyText) return; + if (lastReplyText.match(OTHER_USER_ADDED_REPLY_TEXT)) { + await controller.deleteOtherUserLastReply(); + } else if (lastReplyText.match(MY_ADDED_REPLY_TEXT)) { + await controller.deleteMyLastReply(); + } + }); + + test('only if I send a message', async ({ page, user }) => { + const selector = composeThreadSelector(USER1_CHAT_VIEW_CLASSNAME); + await user.sees(Thread).isScrolledToBottom(selector); + await scrollInSteps(user, ['292', '285', '278']); + await Promise.all([ + page.waitForResponse((r) => r.url().includes('/message') && r.ok()), + user.submits(MessageInput).reply(MY_ADDED_REPLY_TEXT), + ]); + + await user.sees(Thread).isScrolledToBottom(selector); + }); + + test('not if I receive a message', async ({ controller, page, user }) => { + const selector = composeThreadSelector(USER1_CHAT_VIEW_CLASSNAME); + await user.sees(Thread).isScrolledToBottom(selector); + await scrollInSteps(user, ['292', '285', '278']); + + await Promise.all([ + page.waitForResponse((r) => r.url().includes('/message') && r.ok()), + await controller.sendOtherUserReply(), + ]); + await user.sees(Thread).not.isScrolledToBottom(selector); + }); }); }); test.describe('scroll to the bottom', () => { - const scrollInSteps = async (user: TestingUser, cycles = 1) => { - for (let i = 0; i < cycles; i++) { - await Promise.all( - ['142', '135', '128'].map((num: string) => - user.get(Message)(`Message ${num}`).scrollIntoViewIfNeeded(), - ), - ); - } - }; test.beforeEach(async ({ controller, user }) => { await controller.openStory( 'navigate-long-message-lists--user1', @@ -113,7 +172,7 @@ test.describe('scroll to the bottom', () => { user, }) => { // scroll without loading more messages - await scrollInSteps(user, 3); + await scrollInSteps(user); await controller.sendOtherUserMessage(); @@ -133,7 +192,7 @@ test.describe('scroll to the bottom', () => { user, }) => { // scroll without loading more messages - await scrollInSteps(user, 3); + await scrollInSteps(user); // trigger load more messages const firstLoadedMessage = await page.locator( diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-chromium-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-chromium-linux.png new file mode 100644 index 000000000..6bd7dfc78 Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-chromium-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-firefox-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-firefox-linux.png new file mode 100644 index 000000000..fe0321bf5 Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-firefox-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-linux.png new file mode 100644 index 000000000..b3083998c Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-webkit-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-webkit-linux.png new file mode 100644 index 000000000..2ca58850f Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-not-if-I-receive-a-message-1-webkit-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-chromium-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-chromium-linux.png new file mode 100644 index 000000000..1b367cc3e Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-chromium-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-firefox-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-firefox-linux.png new file mode 100644 index 000000000..3954bc594 Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-firefox-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-linux.png new file mode 100644 index 000000000..735f73b3d Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-linux.png differ diff --git a/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-webkit-linux.png b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-webkit-linux.png new file mode 100644 index 000000000..81e7b4178 Binary files /dev/null and b/e2e/navigate-long-message-lists.test.ts-snapshots/thread-autoscroll-on-new-message-only-if-I-send-a-message-1-webkit-linux.png differ diff --git a/e2e/scripts/run_e2e.sh b/e2e/scripts/run_e2e.sh index 3642ff14e..c0b35016d 100755 --- a/e2e/scripts/run_e2e.sh +++ b/e2e/scripts/run_e2e.sh @@ -1,4 +1,4 @@ #!/bin/bash for browser in "webkit" "chromium" "firefox"; do - yarn e2e-fixtures && yarn e2e --browser $browser + yarn e2e-fixtures && yarn e2e --browser $browser $@ done diff --git a/e2e/scripts/run_in_container.sh b/e2e/scripts/run_in_container.sh index 52e134039..48ce49dd7 100755 --- a/e2e/scripts/run_in_container.sh +++ b/e2e/scripts/run_in_container.sh @@ -1,4 +1,4 @@ #!/bin/bash # generate snapshots in a container to match the CI env -docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.22.0-focal /bin/bash +docker run --rm --network host -v $(pwd):/work/ -w /work/ mcr.microsoft.com/playwright:v1.22.0-focal /bin/bash e2e/scripts/run_e2e.sh $@ diff --git a/e2e/user/components/AutocompleteSuggestionList.ts b/e2e/user/components/AutocompleteSuggestionList.ts index 07ac072cd..96ef72d78 100644 --- a/e2e/user/components/AutocompleteSuggestionList.ts +++ b/e2e/user/components/AutocompleteSuggestionList.ts @@ -1,7 +1,9 @@ import type { Page } from '@playwright/test'; export const getAutocompleteSuggestionItem = (page: Page, num: number) => - page.locator(`button.rta__entity >> :nth-match(span.str-chat__user-item--name, ${num})`); + page.locator( + `.str-chat__suggestion-list-item >> :nth-match(span.str-chat__user-item--name, ${num})`, + ); export default (page: Page) => ({ click: { diff --git a/e2e/user/components/Message/QuotedMessage.ts b/e2e/user/components/Message/QuotedMessage.ts index 9d8de4d3a..91e614f87 100644 --- a/e2e/user/components/Message/QuotedMessage.ts +++ b/e2e/user/components/Message/QuotedMessage.ts @@ -10,7 +10,9 @@ export default (page: Page) => ({ click: { //"nth-match" engine expects a one-based index as the last argument nth(text: string, index = 1) { - return page.click(`${selectors.quotedMessage} :nth-match(:text("${text}"),${index})`); + return page.click(`${selectors.quotedMessage} :nth-match(:text("${text}"),${index})`, { + force: true, // onClickCapture registered on the quoted message intercepts pointer events + }); }, }, get: (text: string) => getQuotedMessage(page, text), diff --git a/e2e/user/components/MessageList/MessageList.ts b/e2e/user/components/MessageList/MessageList.ts index 491c25d6d..855e29b7f 100644 --- a/e2e/user/components/MessageList/MessageList.ts +++ b/e2e/user/components/MessageList/MessageList.ts @@ -16,7 +16,7 @@ export default (page: Page) => ({ }, }, async empty() { - return await expect(getMessageList(page)).toBeEmpty(); + return await expect(getMessageList(page)).toContainText('No chats here yet…'); }, async hasLength(count: number) { const listItems = page.locator(selectors.messagesInMessageList); diff --git a/e2e/user/components/Thread/Thread.ts b/e2e/user/components/Thread/Thread.ts index 01724fc24..48b9c8bdc 100644 --- a/e2e/user/components/Thread/Thread.ts +++ b/e2e/user/components/Thread/Thread.ts @@ -18,12 +18,19 @@ export default (page: Page) => ({ .locator(`${selectors.messageRepliesButton} >> nth=${nth} `, { hasText: text }) .click(); }, + async openFor(messageText: string, nth = 0) { + await page + .locator(selectors.message, { hasText: messageText }) + .locator(selectors.messageRepliesButton) + .nth(nth) + .click(); + }, }, get: (prependSelectors?: string) => page.locator(composeThreadSelector(prependSelectors)), see: { empty() { - const replies = page.locator(`${selectors.threadReplyList}`); - return expect(replies).toBeEmpty(); + const replies = page.locator(`${selectors.threadReplyList} ${selectors.message}`); + return expect(replies).toHaveCount(1); }, inViewport: { async nthMessage(text: string, nth?: number) { @@ -38,6 +45,8 @@ export default (page: Page) => ({ }, }, async isScrolledToBottom(selector: string) { + await page.waitForTimeout(500); + expect( await page.evaluate( ([selector]) => { diff --git a/e2e/user/selectors.ts b/e2e/user/selectors.ts index 50205ff68..5da366340 100644 --- a/e2e/user/selectors.ts +++ b/e2e/user/selectors.ts @@ -6,7 +6,7 @@ export default { buttonAddMessage: 'data-testid=add-message', buttonAddOtherUserMessage: 'data-testid=add-other-user-message', buttonCancel: 'text=Cancel', - buttonCancelUpload: 'button[aria-label="Cancel upload"]', + buttonCancelUpload: '.str-chat__attachment-preview-delete', buttonCloseThread: 'button[aria-label="Close thread"]', buttonOpenActionsBox: 'button[aria-label="Open Message Actions Menu"]', buttonOpenThread: 'button[aria-label="Open Thread"]', @@ -23,7 +23,7 @@ export default { messageActionsBox: '.str-chat__message-actions-box', messageData: `.str-chat__message-data`, messageInput: 'data-testid=message-input', - messageInputTextareaThread: '.str-chat__small-message-input [data-testid="message-input"]', + messageInputTextareaThread: '.str-chat__message-input [data-testid="message-input"]', messageList: '.str-chat__list', messageNotification: '.str-chat__message-notification', messageRepliesButton: '.str-chat__message-simple-reply-button', diff --git a/e2e/user/test.ts b/e2e/user/test.ts index 1012d0ba9..ae5b187b4 100644 --- a/e2e/user/test.ts +++ b/e2e/user/test.ts @@ -2,10 +2,10 @@ import { test as _test } from '@playwright/test'; import { Controller } from './Controller'; import { makeUser, TestingUser } from './User'; -interface CustomTestContext { +export type CustomTestContext = { controller: Controller; user: TestingUser; -} +}; export const test = _test.extend({ controller: async ({ baseURL, page }, use) => { diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 508b7a3f2..02f818a6b 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -18,7 +18,8 @@ "@types/jest": "^24.0.0", "@types/node": "^12.0.0", "@types/react": "link:../../node_modules/@types/react", - "@types/react-dom": "link:../../node_modules/@types/react-dom" + "@types/react-dom": "link:../../node_modules/@types/react-dom", + "sass": "^1.52.3" }, "scripts": { "start": "react-scripts start", diff --git a/examples/typescript/src/App.tsx b/examples/typescript/src/App.tsx index 0a58996ad..f4fb3d722 100644 --- a/examples/typescript/src/App.tsx +++ b/examples/typescript/src/App.tsx @@ -10,7 +10,7 @@ import { Thread, Window, } from 'stream-chat-react'; -import '@stream-io/stream-chat-css/dist/css/index.css'; + import './App.css'; const apiKey = process.env.REACT_APP_STREAM_KEY as string; diff --git a/examples/typescript/src/index.css b/examples/typescript/src/index.css deleted file mode 100644 index ec2585e8c..000000000 --- a/examples/typescript/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/examples/typescript/src/index.scss b/examples/typescript/src/index.scss new file mode 100644 index 000000000..2489dae8b --- /dev/null +++ b/examples/typescript/src/index.scss @@ -0,0 +1,140 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', + 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; +} + +$stream-chat-theme-version: '2'; + +@if $stream-chat-theme-version == '2' { + #root { + display: flex; + height: 100%; + + .str-chat-channel-list { + position: fixed; + z-index: 1; + width: 0; + + &--open { + width: 100%; + } + } + + .str-chat-channel { + width: 100%; + } + + .str-chat__thread { + width: 100%; + height: 100%; + position: fixed; + z-index: 1; + } + + .str-chat__channel-header .str-chat__header-hamburger { + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border: none; + background: transparent; + + svg { + width: 25px; + height: 25px; + } + + &:hover { + svg path { + fill: var(--primary-color); + } + } + } + + @media screen and (min-width: 768px) { + //.str-chat-channel-list.thread-open { + // &.menu-open { + // width: 30%; + // height: 100%; + // position: fixed; + // z-index: 1; + // } + // + // &.menu-close { + // width: 0; + // } + // + // & + .channel .menu-button { + // display: block; + // } + //} + + .str-chat-channel-list { + width: 30%; + max-width: 420px; + position: initial; + z-index: 0; + } + + .str-chat__thread { + position: initial; + z-index: 0; + } + + .str-chat__channel-header .str-chat__header-hamburger { + display: none; + } + } + + @media screen and (min-width: 1024px) { + //.str-chat-channel-list { + // max-width: 420px; + // position: initial; + // z-index: 0; + //} + + .str-chat__thread { + width: 45%; + //position: initial; + //z-index: 0; + } + + .str-chat__channel-header .str-chat__header-hamburger { + display: none; + } + } + } +} @else { + .menu-button { + display: none; + } + .str-chat-channel-list { + max-height: 100vh; + overflow-y: auto; + + .str-chat__channel-list-messenger { + flex: 1; + } + } + + .str-chat__thread { + min-width: 100%; + max-width: 100%; + } + + @media only screen and (min-device-width: 768px) { + .str-chat__thread { + min-width: 35%; + max-width: 35%; + } + } +} diff --git a/examples/typescript/src/index.tsx b/examples/typescript/src/index.tsx index e9d9d29a0..0a9c84979 100644 --- a/examples/typescript/src/index.tsx +++ b/examples/typescript/src/index.tsx @@ -1,6 +1,9 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; -import './index.css'; +// import '@stream-io/stream-chat-css/dist/css/index.css'; +// import '@stream-io/stream-chat-css/dist/v2/css/index.css'; +import 'stream-chat-react/dist/css/v2/index.css'; +import './index.scss'; import App from './App'; import * as serviceWorker from './serviceWorker'; diff --git a/examples/typescript/yarn.lock b/examples/typescript/yarn.lock index 26fc8dc5f..af4684a37 100644 --- a/examples/typescript/yarn.lock +++ b/examples/typescript/yarn.lock @@ -3116,6 +3116,21 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +"chokidar@>=3.0.0 <4.0.0": + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -5677,6 +5692,11 @@ immer@1.10.0: resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== +immutable@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -9998,6 +10018,15 @@ sass-loader@8.0.2: schema-utils "^2.6.1" semver "^6.3.0" +sass@^1.52.3: + version "1.52.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.52.3.tgz#b7cc7ffea2341ccc9a0c4fd372bf1b3f9be1b6cb" + integrity sha512-LNNPJ9lafx+j1ArtA7GyEJm9eawXN8KlA1+5dF6IZyoONg1Tyo/g+muOsENWJH/2Q1FHbbV4UwliU0cXMa/VIA== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -10324,6 +10353,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +"source-map-js@>=0.6.2 <2.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" diff --git a/examples/vite/package.json b/examples/vite/package.json index dc33f168e..37efdf679 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -7,8 +7,10 @@ "serve": "vite preview" }, "dependencies": { + "@stream-io/stream-chat-css": "link:../../../stream-chat-css", "react": "link:../../node_modules/react", "react-dom": "link:../../node_modules/react-dom", + "sass": "^1.54.0", "stream-chat": "link:../../node_modules/stream-chat", "stream-chat-react": "link:../../" }, @@ -16,8 +18,8 @@ "@types/react": "17.0.3", "@types/react-dom": "17.0.3", "@vitejs/plugin-react": "^1.0.0", - "typescript": "^4.3.2", - "vite": "^2.6.4" + "typescript": "^4.7.4", + "vite": "^3.0.3" }, "resolutions": { "@types/react": "link:../../node_modules/@types/react", diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index 664d0a350..d0514328f 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -10,13 +10,17 @@ import { Thread, Window, } from 'stream-chat-react'; -import '@stream-io/stream-chat-css/dist/css/index.css'; +import '@stream-io/stream-chat-css/dist/v2/css/index.css'; + +const params = (new Proxy(new URLSearchParams(window.location.search), { + get: (searchParams, property) => searchParams.get(property as string), +}) as unknown) as Record; const apiKey = import.meta.env.VITE_STREAM_KEY as string; -const userId = import.meta.env.VITE_USER_ID as string; -const userToken = import.meta.env.VITE_USER_TOKEN as string; +const userId = params.uid ?? (import.meta.env.VITE_USER_ID as string); +const userToken = params.ut ?? (import.meta.env.VITE_USER_TOKEN as string); -const filters: ChannelFilters = { type: 'messaging', members: {$in: [userId]} }; +const filters: ChannelFilters = { members: { $in: [userId] }, type: 'messaging' }; const options: ChannelOptions = { limit: 10, presence: true, state: true }; const sort: ChannelSort = { last_message_at: -1, updated_at: -1 }; diff --git a/examples/vite/src/index.css b/examples/vite/src/index.css deleted file mode 100644 index ec2585e8c..000000000 --- a/examples/vite/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/examples/vite/src/index.scss b/examples/vite/src/index.scss new file mode 100644 index 000000000..4c92f0bf2 --- /dev/null +++ b/examples/vite/src/index.scss @@ -0,0 +1,135 @@ +body { + margin: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +html, +body, +#root { + margin: unset; + padding: unset; + height: 100%; +} + +$stream-chat-theme-version: '2'; + +@if $stream-chat-theme-version == '2' { + #root { + display: flex; + height: 100%; + + .str-chat-channel-list { + position: fixed; + z-index: 1; + width: 0; + + &--open { + width: 100%; + } + } + + .str-chat-channel { + width: 100%; + } + + .str-chat__thread { + width: 100%; + height: 100%; + position: fixed; + z-index: 1; + } + + .str-chat__channel-header .str-chat__header-hamburger { + width: 30px; + height: 38px; + padding: var(--xxs-p); + margin-right: var(--xs-m); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border: none; + background: transparent; + + &:hover { + svg path { + fill: var(--primary-color); + } + } + } + + @media screen and (min-width: 768px) { + //.str-chat-channel-list.thread-open { + // &.menu-open { + // width: 30%; + // height: 100%; + // position: fixed; + // z-index: 1; + // } + // + // &.menu-close { + // width: 0; + // } + // + // & + .channel .menu-button { + // display: block; + // } + //} + + .str-chat-channel-list { + width: 30%; + max-width: 420px; + position: initial; + z-index: 0; + } + + .str-chat__thread { + position: initial; + z-index: 0; + } + } + + @media screen and (min-width: 1024px) { + //.str-chat-channel-list { + // max-width: 420px; + // position: initial; + // z-index: 0; + //} + + .str-chat__thread { + width: 45%; + //position: initial; + //z-index: 0; + } + + .str-chat__channel-header .str-chat__header-hamburger { + display: none; + } + } + } +} @else { + .menu-button { + display: none; + } + .str-chat-channel-list { + max-height: 100vh; + overflow-y: auto; + + .str-chat__channel-list-messenger { + flex: 1; + } + } + + .str-chat__thread { + min-width: 100%; + max-width: 100%; + } + + @media only screen and (min-device-width: 768px) { + .str-chat__thread { + min-width: 35%; + max-width: 35%; + } + } +} diff --git a/examples/vite/src/main.tsx b/examples/vite/src/main.tsx index f452ad2e7..9923c663a 100644 --- a/examples/vite/src/main.tsx +++ b/examples/vite/src/main.tsx @@ -1,6 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './index.css'; +import './index.scss'; import App from './App'; ReactDOM.render( diff --git a/examples/vite/yarn.lock b/examples/vite/yarn.lock index e4c6be72e..b6f439a46 100644 --- a/examples/vite/yarn.lock +++ b/examples/vite/yarn.lock @@ -220,13 +220,20 @@ "@babel/plugin-syntax-jsx" "^7.14.5" "@babel/types" "^7.14.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -259,15 +266,20 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@braintree/sanitize-url@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-5.0.0.tgz#3ba791f37b90e7f6170d252b63aacfcae943c039" - integrity sha512-WmKrB/575EJCzbeSJR3YQ5sET5FaizeljLRw1382qVUeGqzuWBgIS+AF5a0FO51uQTrDpoRgvuHC2IWVsgwkkA== +"@braintree/sanitize-url@6.0.0", "@braintree/sanitize-url@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz#fe364f025ba74f6de6c837a84ef44bdb1d61e68f" + integrity sha512-mgmE7XBYY/21erpzhexk4Cj1cyTQ9LzvnTxtzM17BJ7ERMNE6W72mQRo0I1Ud8eFJ+RVVIcBNhLFZ3GX4XFz5w== + +"@juggle/resize-observer@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0" + integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== -"@braintree/sanitize-url@^5.0.0": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-5.0.2.tgz#b23080fa35520e993a8a37a0f5bca26aa4650a48" - integrity sha512-NBEJlHWrhQucLhZGHtSxM2loSaNUMajC7KOYJLyfcdW/6goVoff2HoYI3bz8YCDN0wKGbxtUL0gx2dvHpvnWlw== +"@popperjs/core@^2.11.5": + version "2.11.5" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" + integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== "@rgrove/parse-xml@^3.0.0": version "3.0.0" @@ -287,10 +299,14 @@ resolved "https://registry.yarnpkg.com/@stream-io/escape-string-regexp/-/escape-string-regexp-5.0.1.tgz#362505c92799fea6afe4e369993fbbda8690cc37" integrity sha512-qIaSrzJXieZqo2fZSYTdzwSbZgHHsT3tkd646vvZhh4fr+9nO4NlvqGmPF43Y+OfZiWf+zYDFgNiPGG5+iZulQ== -"@stream-io/stream-chat-css@^2.2.1": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-2.3.2.tgz#fa5aad4367ee8b6a7bacebfb22fe5b2127a376eb" - integrity sha512-wfHDS9GBXs5vlk7zKjMb+Z8ZsTvDeiCrnl/RU3P6i4tZGcXl0qkIWBf2e0O0kMJ5APYtZdRYvYyTlO7qeK9Jpw== +"@stream-io/stream-chat-css@^2.10.0": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-2.10.1.tgz#317b225d2a8a424e056e1d4aff5dc0f7f9b575ed" + integrity sha512-8ykbkvxOWHbagRMCacXBzta3FbB0Vs+1wGRKy9IP2Md4Rm88Id9Cti6ikYi5ltmjnpyvsWSe+G/G1CvTmFpnqA== + +"@stream-io/stream-chat-css@link:../../../stream-chat-css": + version "0.0.0" + uid "" "@stream-io/transliterate@^1.5.5": version "1.5.5" @@ -300,6 +316,13 @@ "@stream-io/escape-string-regexp" "^5.0.1" lodash.deburr "^4.1.0" +"@types/jsonwebtoken@^8.5.6": + version "8.5.8" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz#01b39711eb844777b7af1d1f2b4cf22fda1c0c44" + integrity sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A== + dependencies: + "@types/node" "*" + "@types/linkifyjs@^2.1.3": version "2.1.4" resolved "https://registry.yarnpkg.com/@types/linkifyjs/-/linkifyjs-2.1.4.tgz#6b14e35d8d211f2666f602dcabcdc6859617516f" @@ -314,6 +337,11 @@ dependencies: "@types/unist" "*" +"@types/node@*": + version "18.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.1.tgz#828e4785ccca13f44e2fb6852ae0ef11e3e20ba5" + integrity sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg== + "@types/prop-types@*": version "15.7.4" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" @@ -328,9 +356,11 @@ "@types/react-dom@link:../../node_modules/@types/react-dom": version "0.0.0" + uid "" "@types/react@*": version "0.0.0" + uid "" "@types/react@17.0.3": version "17.0.3" @@ -343,6 +373,7 @@ "@types/react@link:../../node_modules/@types/react": version "0.0.0" + uid "" "@types/scheduler@*": version "0.16.2" @@ -354,6 +385,13 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== +"@types/ws@^7.4.0": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + "@virtuoso.dev/react-urx@^0.2.12": version "0.2.12" resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.12.tgz#6c75cdbea2503c129b34d1ce2ee8cd649503b47c" @@ -361,23 +399,11 @@ dependencies: "@virtuoso.dev/urx" "^0.2.12" -"@virtuoso.dev/react-urx@^0.2.5": - version "0.2.7" - resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.7.tgz#54a32d7dcade67d89b098514026700b63a04cdbb" - integrity sha512-iaJ/1vz7dkfpIxEjOpgOme8X6KkUsz16RoqpFHtqbKiQW1FI+1h7YbyTxhz5D9xmg/VZU9gHpXvPHTzdKIKa3Q== - dependencies: - "@virtuoso.dev/urx" "^0.2.7" - "@virtuoso.dev/urx@^0.2.12": version "0.2.12" resolved "https://registry.yarnpkg.com/@virtuoso.dev/urx/-/urx-0.2.12.tgz#fa9bd1a2777cf70b19d4c9836b8d92b04f079c64" integrity sha512-Q9nlRqYb5Uq4Ynu8cWPSJ7LxpuZsI+MZ09IJlDAAgwuNgfWRArpVRP0VN0coYgUo2fKMjhmV69MTqaUbIBhu/g== -"@virtuoso.dev/urx@^0.2.5", "@virtuoso.dev/urx@^0.2.7": - version "0.2.7" - resolved "https://registry.yarnpkg.com/@virtuoso.dev/urx/-/urx-0.2.7.tgz#080811a31b458819147f2875e7fe7fdefaef12dc" - integrity sha512-mv6V6Gzf1QnGUzb5Rwv5Eqs3DRX5R4nGIojVXtD+fAFnr2SAvpHNPMRNdAclvD/pcdiga6rTzUhFwq4FBC2MDw== - "@vitejs/plugin-react@^1.0.0": version "1.0.6" resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-1.0.6.tgz#edbbe70151ce8b154ed1eb6a6f1962146ce1112b" @@ -399,16 +425,53 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -attr-accept@^2.2.1: +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +attr-accept@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== +axios@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.22.0.tgz#bf702c41fb50fbca4539589d839a077117b79b25" + integrity sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w== + dependencies: + follow-redirects "^1.14.4" + bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + browserslist@^4.16.6: version "4.17.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.5.tgz#c827bbe172a4c22b123f5e337533ceebadfdd559" @@ -420,6 +483,11 @@ browserslist@^4.16.6: node-releases "^2.0.1" picocolors "^1.0.0" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + caniuse-lite@^1.0.30001271: version "1.0.30001271" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001271.tgz#0dda0c9bcae2cf5407cd34cac304186616cc83e8" @@ -449,6 +517,26 @@ character-reference-invalid@^1.0.0: resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== +"chokidar@>=3.0.0 <4.0.0": + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -461,6 +549,13 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -490,6 +585,11 @@ deepmerge@^4.0.0: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dom-serializer@^1.0.1: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" @@ -520,6 +620,13 @@ domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + electron-to-chromium@^1.3.878: version "1.3.880" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.880.tgz#58d1a298c5267f2faf440683d038c50ab39a6401" @@ -548,113 +655,131 @@ entities@^3.0.1: resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== -esbuild-android-arm64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.13.9.tgz#6cc4a0c623332c0830a311ddd8242b1f496ff940" - integrity sha512-Ty0hKldtjJVLHwUwbKR4GFPiXBo5iQ3aE1OLBar9lh3myaRkUGEb+Ypl74LEKa0+t/9lS3Ev1N5+5P2Sq6UvNQ== - -esbuild-darwin-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.13.9.tgz#df44297c2438032cda2b21548a82bb007e2105cc" - integrity sha512-Ay0/b98v0oYp3ApXNQ7QPbaSkCT9WjBU6h8bMB1SYrQ/PmHgwph91fb9V0pfOLKK1rYWypfrNbI0MyT2tWN+rQ== - -esbuild-darwin-arm64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.9.tgz#704ef404a6a38eda190d40ed354e7f2c1c839081" - integrity sha512-nJB8chaJdWathCe6EyIiMIqfyEzbuXPyNsPlL3bYRB1zFCF8feXT874D4IHbJ/w8B6BpY3sM1Clr/I/DK8E4ow== - -esbuild-freebsd-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.9.tgz#fbbf22c99e15f27d0f8a1a040d7961a86f0d3a4e" - integrity sha512-ktaBujf12XLkVXLGx7WjFcmh1tt34tm7gP4pHkhvbzbHrq+BbXwcl4EsW+5JT9VNKl7slOGf4Qnua/VW7ZcnIw== - -esbuild-freebsd-arm64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.9.tgz#809fff4c43653dbbf071ffce9f80a030b278098e" - integrity sha512-vVa5zps4dmwpXwv/amxVpIWvFJuUPWQkpV+PYtZUW9lqjXsQ3LBHP51Q1cXZZBIrqwszLsEyJPa5GuDOY15hzQ== - -esbuild-linux-32@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.13.9.tgz#f9fd3423481e51674e9817d5eea25689889a5f5a" - integrity sha512-HxoW9QNqhO8VW1l7aBiYQH4lobeHq85+blZ4nlZ7sg5CNhGRRwnMlV6S08VYKz6V0YKnHb5OqJxx2HZuTZ7tgQ== - -esbuild-linux-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.13.9.tgz#9d7f66866dae1abaff7cbc3749f2847d5fb72fd5" - integrity sha512-L+eAR8o1lAUr9g64RXnBLuWZjAItAOWSUpvkchpa6QvSnXFA/nG6PgGsOBEqhDXl9qYEpGI0ReDrFkf8ByapvQ== - -esbuild-linux-arm64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.9.tgz#669202e71b9ced4d285bfd1d69de948e013ac28f" - integrity sha512-IjbhZpW5VQYK4nVI4dj/mLvH5oXAIf57OI8BYVkCqrdVXJwR8nVrSqux3zJSY+ElrkOK3DtG9iTPpmqvBXaU0g== - -esbuild-linux-arm@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.13.9.tgz#c3ceb56ec0e3dbd1a3a89dca6cb7fc0ca360bcc8" - integrity sha512-DT0S+ufCVXatPZHjkCaBgZSFIV8FzY4GEHz/BlkitTWzUvT1dIUXjPIRPnqBUVa+0AyS1bZSfHzv9hTT4LHz7A== - -esbuild-linux-mips64le@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.9.tgz#bf4bd389ee14b67c5c77669952f2de6b2cc8a003" - integrity sha512-ec9RgAM4r+fe1ZmG16qeMwEHdcIvqeW8tpnpkfSQu9T4487KtQF6lg3TQasTarrLLEe7Qpy+E+r4VwC8eeZySQ== - -esbuild-linux-ppc64le@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.9.tgz#465b7bdc70577da606b3b5d463028292b6d834ad" - integrity sha512-7b2/wg8T1n/L1BgCWlMSez0aXfGkNjFuOqMBQdnTti3LRuUwzGJcrhRf/FdZGJ5/evML9mqu60vLRuXW1TdXCg== - -esbuild-netbsd-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.13.9.tgz#94f2dabe83520066cc1e1fae3ecff78695a8ebb1" - integrity sha512-PiZu3h4+Szj0iZPgvuD2Y0isOXnlNetmF6jMcOwW54BScwynW24/baE+z7PfDyNFgjV04Ga2THdcpbKBDhgWQw== - -esbuild-openbsd-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.9.tgz#b47f6a641ca37358aeedb2b7c4bb73dd0682c6d5" - integrity sha512-SJKN4Ez+ilY7mu+1gAdGQ9N6dktBfbEkiOAvw+hT7xHrNnTnrTGH0FT4qx9dazB9HX6D04L4PXmVOyynqi+oEQ== - -esbuild-sunos-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.13.9.tgz#b0df4a316b7c98eb490f4bd0db381cf2c391ae73" - integrity sha512-9N0RjZ7cElE8ifrS0nBrLQgBMQNPiIIKO2GzLXy7Ms8AM3KjfLiV2G2+9O0B9paXjRAHchIwazTeOyeWb1vyWA== - -esbuild-windows-32@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.13.9.tgz#e229563e134e634f9748cc8315c691e2013259ef" - integrity sha512-awxWs1kns+RfjhqBbTbdlePjqZrAE2XMaAQJNg9dtu+C7ghC3QKsqXbu0C26OuF5YeAdJcq9q+IdG6WPLjvj9w== - -esbuild-windows-64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.13.9.tgz#103ad3f13e1a0e44934b91f358e81dd201b86b34" - integrity sha512-VmA9GQMCzOr8rFfD72Dum1+AWhJui7ZO6sYwp6rBHYu4vLmWITTSUsd/zgXXmZuHBPkkvxLJLF8XsKFCRKflJA== - -esbuild-windows-arm64@0.13.9: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.9.tgz#545bb58848008258b339b1b00fcfe92c85bc7251" - integrity sha512-P/jPY2JwmTpgEPh9BkXpCe690tcDSSo0K9BHTniSeEAEz26kPpqldVa4XDm0R+hNnFA7ecEgNskr4QAxE1ry0w== - -esbuild@^0.13.2: - version "0.13.9" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.13.9.tgz#aafc4b3375ac443ae7b223c26c4e58d10d2d535b" - integrity sha512-8bYcckmisXjGvBMeylp1PRtu21uOoCDFAgXGGF2BR241zYQDN6ZLNvcmQlnQ7olG0p6PRWmJI8WVH3ca8viPuw== +esbuild-android-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.50.tgz#a46fc80fa2007690e647680d837483a750a3097f" + integrity sha512-H7iUEm7gUJHzidsBlFPGF6FTExazcgXL/46xxLo6i6bMtPim6ZmXyTccS8yOMpy6HAC6dPZ/JCQqrkkin69n6Q== + +esbuild-android-arm64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.50.tgz#bdda7851fa7f5f770d6ff0ad593a8945d3a0fcdd" + integrity sha512-NFaoqEwa+OYfoYVpQWDMdKII7wZZkAjtJFo1WdnBeCYlYikvUhTnf2aPwPu5qEAw/ie1NYK0yn3cafwP+kP+OQ== + +esbuild-darwin-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.50.tgz#f0535435f9760766f30db14a991ee5ca94c022a4" + integrity sha512-gDQsCvGnZiJv9cfdO48QqxkRV8oKAXgR2CGp7TdIpccwFdJMHf8hyIJhMW/05b/HJjET/26Us27Jx91BFfEVSA== + +esbuild-darwin-arm64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.50.tgz#76a41a40e8947a15ae62970e9ed2853883c4b16c" + integrity sha512-36nNs5OjKIb/Q50Sgp8+rYW/PqirRiFN0NFc9hEvgPzNJxeJedktXwzfJSln4EcRFRh5Vz4IlqFRScp+aiBBzA== + +esbuild-freebsd-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.50.tgz#2ed6633c17ed42c20a1bd68e82c4bbc75ea4fb57" + integrity sha512-/1pHHCUem8e/R86/uR+4v5diI2CtBdiWKiqGuPa9b/0x3Nwdh5AOH7lj+8823C6uX1e0ufwkSLkS+aFZiBCWxA== + +esbuild-freebsd-arm64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.50.tgz#cb115f4cdafe9cdbe58875ba482fccc54d32aa43" + integrity sha512-iKwUVMQztnPZe5pUYHdMkRc9aSpvoV1mkuHlCoPtxZA3V+Kg/ptpzkcSY+fKd0kuom+l6Rc93k0UPVkP7xoqrw== + +esbuild-linux-32@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.50.tgz#fe2b724994dcf1d4e48dc4832ff008ad7d00bcfd" + integrity sha512-sWUwvf3uz7dFOpLzYuih+WQ7dRycrBWHCdoXJ4I4XdMxEHCECd8b7a9N9u7FzT6XR2gHPk9EzvchQUtiEMRwqw== + +esbuild-linux-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.50.tgz#7851ab5151df9501a2187bd4909c594ad232b623" + integrity sha512-u0PQxPhaeI629t4Y3EEcQ0wmWG+tC/LpP2K7yDFvwuPq0jSQ8SIN+ARNYfRjGW15O2we3XJvklbGV0wRuUCPig== + +esbuild-linux-arm64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.50.tgz#76a76afef484a0512f1fbbcc762edd705dee8892" + integrity sha512-ZyfoNgsTftD7Rp5S7La5auomKdNeB3Ck+kSKXC4pp96VnHyYGjHHXWIlcbH8i+efRn9brszo1/Thl1qn8RqmhQ== + +esbuild-linux-arm@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.50.tgz#6d7a8c0712091b0c3a668dd5d8b5c924adbaeb12" + integrity sha512-VALZq13bhmFJYFE/mLEb+9A0w5vo8z+YDVOWeaf9vOTrSC31RohRIwtxXBnVJ7YKLYfEMzcgFYf+OFln3Y0cWg== + +esbuild-linux-mips64le@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.50.tgz#43426909c1884c5dc6b40765673a08a7ec1d2064" + integrity sha512-ygo31Vxn/WrmjKCHkBoutOlFG5yM9J2UhzHb0oWD9O61dGg+Hzjz9hjf5cmM7FBhAzdpOdEWHIrVOg2YAi6rTw== + +esbuild-linux-ppc64le@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.50.tgz#c754ea3da1dd180c6e9b6b508dc18ce983d92b11" + integrity sha512-xWCKU5UaiTUT6Wz/O7GKP9KWdfbsb7vhfgQzRfX4ahh5NZV4ozZ4+SdzYG8WxetsLy84UzLX3Pi++xpVn1OkFQ== + +esbuild-linux-riscv64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.50.tgz#f3b2dd3c4c2b91bf191d3b98a9819c8aa6f5ad7f" + integrity sha512-0+dsneSEihZTopoO9B6Z6K4j3uI7EdxBP7YSF5rTwUgCID+wHD3vM1gGT0m+pjCW+NOacU9kH/WE9N686FHAJg== + +esbuild-linux-s390x@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.50.tgz#3dfbc4578b2a81995caabb79df2b628ea86a5390" + integrity sha512-tVjqcu8o0P9H4StwbIhL1sQYm5mWATlodKB6dpEZFkcyTI8kfIGWiWcrGmkNGH2i1kBUOsdlBafPxR3nzp3TDA== + +esbuild-netbsd-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.50.tgz#17dbf51eaa48d983e794b588d195415410ef8c85" + integrity sha512-0R/glfqAQ2q6MHDf7YJw/TulibugjizBxyPvZIcorH0Mb7vSimdHy0XF5uCba5CKt+r4wjax1mvO9lZ4jiAhEg== + +esbuild-openbsd-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.50.tgz#cf6b1a50c8cf67b0725aaa4bce9773976168c50e" + integrity sha512-7PAtmrR5mDOFubXIkuxYQ4bdNS6XCK8AIIHUiZxq1kL8cFIH5731jPcXQ4JNy/wbj1C9sZ8rzD8BIM80Tqk29w== + +esbuild-sunos-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.50.tgz#f705ae0dd914c3b45dc43319c4f532216c3d841f" + integrity sha512-gBxNY/wyptvD7PkHIYcq7se6SQEXcSC8Y7mE0FJB+CGgssEWf6vBPfTTZ2b6BWKnmaP6P6qb7s/KRIV5T2PxsQ== + +esbuild-windows-32@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.50.tgz#6364905a99c1e6c1e2fe7bfccebd958131b1cd6c" + integrity sha512-MOOe6J9cqe/iW1qbIVYSAqzJFh0p2LBLhVUIWdMVnNUNjvg2/4QNX4oT4IzgDeldU+Bym9/Tn6+DxvUHJXL5Zw== + +esbuild-windows-64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.50.tgz#56603cb6367e30d14098deb77de6aa18d76dd89b" + integrity sha512-r/qE5Ex3w1jjGv/JlpPoWB365ldkppUlnizhMxJgojp907ZF1PgLTuW207kgzZcSCXyquL9qJkMsY+MRtaZ5yQ== + +esbuild-windows-arm64@0.14.50: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.50.tgz#e7ddde6a97194051a5a4ac05f4f5900e922a7ea5" + integrity sha512-EMS4lQnsIe12ZyAinOINx7eq2mjpDdhGZZWDwPZE/yUTN9cnc2Ze/xUTYIAyaJqrqQda3LnDpADKpvLvol6ENQ== + +esbuild@^0.14.47: + version "0.14.50" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.50.tgz#7a665392c8df94bf6e1ae1e999966a5ee62c6cbc" + integrity sha512-SbC3k35Ih2IC6trhbMYW7hYeGdjPKf9atTKwBUHqMCYFZZ9z8zhuvfnZihsnJypl74FjiAKjBRqFkBkAd0rS/w== optionalDependencies: - esbuild-android-arm64 "0.13.9" - esbuild-darwin-64 "0.13.9" - esbuild-darwin-arm64 "0.13.9" - esbuild-freebsd-64 "0.13.9" - esbuild-freebsd-arm64 "0.13.9" - esbuild-linux-32 "0.13.9" - esbuild-linux-64 "0.13.9" - esbuild-linux-arm "0.13.9" - esbuild-linux-arm64 "0.13.9" - esbuild-linux-mips64le "0.13.9" - esbuild-linux-ppc64le "0.13.9" - esbuild-netbsd-64 "0.13.9" - esbuild-openbsd-64 "0.13.9" - esbuild-sunos-64 "0.13.9" - esbuild-windows-32 "0.13.9" - esbuild-windows-64 "0.13.9" - esbuild-windows-arm64 "0.13.9" + esbuild-android-64 "0.14.50" + esbuild-android-arm64 "0.14.50" + esbuild-darwin-64 "0.14.50" + esbuild-darwin-arm64 "0.14.50" + esbuild-freebsd-64 "0.14.50" + esbuild-freebsd-arm64 "0.14.50" + esbuild-linux-32 "0.14.50" + esbuild-linux-64 "0.14.50" + esbuild-linux-arm "0.14.50" + esbuild-linux-arm64 "0.14.50" + esbuild-linux-mips64le "0.14.50" + esbuild-linux-ppc64le "0.14.50" + esbuild-linux-riscv64 "0.14.50" + esbuild-linux-s390x "0.14.50" + esbuild-netbsd-64 "0.14.50" + esbuild-openbsd-64 "0.14.50" + esbuild-sunos-64 "0.14.50" + esbuild-windows-32 "0.14.50" + esbuild-windows-64 "0.14.50" + esbuild-windows-arm64 "0.14.50" escalade@^3.1.1: version "3.1.1" @@ -681,13 +806,34 @@ extend@^3.0.0: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -file-selector@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80" - integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA== +file-selector@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.5.0.tgz#21c7126dc9728b31a2742d91cab20d55e67e4fb4" + integrity sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA== dependencies: tslib "^2.0.3" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +follow-redirects@^1.14.4: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" @@ -703,6 +849,13 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -740,12 +893,12 @@ htmlparser2@^7.0: domutils "^2.8.0" entities "^3.0.1" -i18next@^19.8.4: - version "19.9.2" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.9.2.tgz#ea5a124416e3c5ab85fddca2c8e3c3669a8da397" - integrity sha512-0i6cuo6ER6usEOtKajUUDj92zlG+KArFia0857xxiEHAQcUwh/RtOQocui1LPJwunSYT574Pk64aNva1kwtxZg== +i18next@^21.6.14: + version "21.8.14" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.8.14.tgz#03a3a669ef4520aadd9d152c80596f600e287c6a" + integrity sha512-4Yi+DtexvMm/Yw3Q9fllzY12SgLk+Mcmar+rCAccsOPul/2UmnBzoHbTGn/L48IPkFcmrNaH7xTLboBWIbH6pw== dependencies: - "@babel/runtime" "^7.12.0" + "@babel/runtime" "^7.17.2" ical-expander@^3.1.0: version "3.1.0" @@ -759,6 +912,11 @@ ical.js@^1.2.2: resolved "https://registry.yarnpkg.com/ical.js/-/ical.js-1.4.0.tgz#fc5619dc55fe03d909bf04362aa0677f4541b9d7" integrity sha512-ltHZuOFNNjcyEYbzDgjemS7LWIFh2vydJeznxQHUh3dnarbxqOYsWONYteBVAq1MEOHnwXFGN2eskZReHclnrA== +immutable@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + is-alphabetical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" @@ -772,6 +930,13 @@ is-alphanumerical@^1.0.0: is-alphabetical "^1.0.0" is-decimal "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" @@ -784,16 +949,40 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-hexadecimal@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-plain-obj@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -821,6 +1010,39 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + linkifyjs@^2.1.9: version "2.1.9" resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-2.1.9.tgz#af06e45a2866ff06c4766582590d098a4d584702" @@ -846,11 +1068,46 @@ lodash.deburr@^4.1.0: resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b" integrity sha1-3bG7s+8HRYwBd7oH3hRCLLAz/5s= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" @@ -861,7 +1118,7 @@ lodash.uniqby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= -loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -913,40 +1170,62 @@ micromark@~2.11.0: debug "^4.0.0" parse-entities "^2.0.0" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -mml-react@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/mml-react/-/mml-react-0.4.2.tgz#a58d685c61417ed7c99f3caea6616652ebcfb551" - integrity sha512-LqqTSyXjjNqBOE32RB8aPruPLh1UhUCPAiAm1KVBpdfAM4yNXMRhKeWpJkjQJOiXzNMKeKv7youh9nCWNS8qWg== +mml-react@^0.4.7: + version "0.4.7" + resolved "https://registry.yarnpkg.com/mml-react/-/mml-react-0.4.7.tgz#c0646c4647ecdcf89a0818f6e02b2adc50712c21" + integrity sha512-YoEIg5kOWGR3U9QFc8YFHEgyGV/CnLQIjm+naW3K04LXRw6xtcU0+KMjCMu2EJEF/Wjz8H3Yu4ZU2mUDR+143g== dependencies: - "@braintree/sanitize-url" "^5.0.0" + "@braintree/sanitize-url" "^6.0.0" "@rgrove/parse-xml" "^3.0.0" "@types/linkifyjs" "^2.1.3" dayjs "^1.10.4" ical-expander "^3.1.0" linkifyjs "^2.1.9" react-markdown "^5.0.3" - react-virtuoso "^2.2.1" + react-virtuoso "^2.10.2" ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -nanoid@^3.1.30: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== node-releases@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -964,7 +1243,7 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -974,19 +1253,24 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + picomatch@^2.2.2: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -postcss@^8.3.8: - version "8.3.11" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" - integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA== +postcss@^8.4.14: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== dependencies: - nanoid "^3.1.30" + nanoid "^3.3.4" picocolors "^1.0.0" - source-map-js "^0.6.2" + source-map-js "^1.0.2" pretty-bytes@^5.4.1: version "5.6.0" @@ -1002,6 +1286,15 @@ prop-types@^15.6.0, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + ramda@^0.27.1: version "0.27.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" @@ -1009,42 +1302,43 @@ ramda@^0.27.1: "react-dom@link:../../node_modules/react-dom": version "0.0.0" + uid "" -react-dropzone@11.3.4: - version "11.3.4" - resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.3.4.tgz#aeb098df5c4491e165042c9f0b5e2e7185484740" - integrity sha512-B1nzNRZ4F1cnrfEC0T6KXeBN1mCPinu4JCoTrp7NjB+442KSPxqfDrw41QIA2kAwlYs1+wj/0BTedeM5hc2+xw== +react-dropzone@^12.0.5: + version "12.1.0" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-12.1.0.tgz#e097b37e9da6f9e324efc757b7434ebc6f3dc2cb" + integrity sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog== dependencies: - attr-accept "^2.2.1" - file-selector "^0.2.2" - prop-types "^15.7.2" + attr-accept "^2.2.2" + file-selector "^0.5.0" + prop-types "^15.8.1" react-fast-compare@^3.0.1, react-fast-compare@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== -react-file-utils@1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/react-file-utils/-/react-file-utils-1.1.9.tgz#28811b98323f56be3982ad5d4f83959295db8082" - integrity sha512-w9zDaD6jWOWzp8au+Tc5yJabQ3FLj2VP0AE+7cB0V0ET1AafcP/CP7NbHpLYoOojM82uMGyzhPhuP4cCuOyVrA== +react-file-utils@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/react-file-utils/-/react-file-utils-1.2.0.tgz#4420f41b76bfdc19acfbccc092ebd3ca6d41e955" + integrity sha512-pu5mpNNwHZe7X+LG4CNtEwqf+J2sseE3y9dbkt1LLQpYs7TC4sy3XgRkQTzZuvLXsDyGpASbhDJCpB/KHCPbAA== dependencies: - react-dropzone "11.3.4" + react-dropzone "^12.0.5" react-image-gallery@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/react-image-gallery/-/react-image-gallery-1.2.7.tgz#2dfb83c52f32828a5340ca565f91bca58241fcfe" integrity sha512-ts7yMWykvZhslGMuvVAUk7a1dJIx9QtIwK6Clv8dOGxPsJid92FRrLvXbZDvkYcUyj48sHalnBhqU6mZNCB2jg== -react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.1.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== react-markdown@^5.0.3: version "5.0.3" @@ -1062,10 +1356,10 @@ react-markdown@^5.0.3: unist-util-visit "^2.0.0" xtend "^4.0.1" -react-player@^2.7.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.9.0.tgz#ef7fe7073434087565f00ff219824e1e02c4b046" - integrity sha512-jNUkTfMmUhwPPAktAdIqiBcVUKsFKrVGH6Ocutj6535CNfM91yrvWxHg6fvIX8Y/fjYUPoejddwh7qboNV9vGA== +react-player@^2.10.1: + version "2.10.1" + resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.10.1.tgz#f2ee3ec31393d7042f727737545414b951ffc7e4" + integrity sha512-ova0jY1Y1lqLYxOehkzbNEju4rFXYVkr5rdGD71nsiG4UKPzRXQPTd3xjoDssheoMNjZ51mjT5ysTrdQ2tEvsg== dependencies: deepmerge "^4.0.0" load-script "^1.0.0" @@ -1073,6 +1367,14 @@ react-player@^2.7.0: prop-types "^15.7.2" react-fast-compare "^3.0.1" +react-popper@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba" + integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + react-refresh@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" @@ -1087,24 +1389,24 @@ react-textarea-autosize@^8.3.0: use-composed-ref "^1.0.0" use-latest "^1.0.0" -react-virtuoso@^2.2.1: - version "2.2.5" - resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-2.2.5.tgz#9f6dca9ae173ea36937cfd0c6edacdad86511a4a" - integrity sha512-4UxB0JJYtLT5MmDhK1ziGIKQYP8BJJsMyAJttK29H688fdiE1IXcYjkq0/hYedkZ8uHHT6FHHPLcmyzX1GZBkA== - dependencies: - "@virtuoso.dev/react-urx" "^0.2.5" - "@virtuoso.dev/urx" "^0.2.5" - -react-virtuoso@^2.8.2: - version "2.8.2" - resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-2.8.2.tgz#6b98c62d5e3807b41b3e52e3576784f8b6142f33" - integrity sha512-iH48/OOsh12BPqZX3LNgT9zshcqO7ALwG/AxXDTqPYwHSolzwj+V8fpHRBLEbvfBl4EEnMZI+yxvs1DblZT/jQ== +react-virtuoso@^2.10.2: + version "2.16.5" + resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-2.16.5.tgz#807b79ed378b9aa5430d45a94034afef5ab5afab" + integrity sha512-bedbW1eJX/s0/XdDVxYZG1kLwXbLkpASskRQ4+UuvZ3mU0LWqDtjPsEzylXbOGhRaWeiuiG768BGl4zxQUBczg== dependencies: "@virtuoso.dev/react-urx" "^0.2.12" "@virtuoso.dev/urx" "^0.2.12" "react@link:../../node_modules/react": version "0.0.0" + uid "" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" regenerator-runtime@^0.13.4: version "0.13.9" @@ -1126,27 +1428,62 @@ resolve@^1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -rollup@^2.57.0: - version "2.58.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.3.tgz#71a08138d9515fb65043b6a18618b2ed9ac8d239" - integrity sha512-ei27MSw1KhRur4p87Q0/Va2NAYqMXOX++FNEumMBcdreIRLURKy+cE2wcDJKBn0nfmhP2ZGrJkP1XPO+G8FJQw== +resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup@^2.75.6: + version "2.77.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.1.tgz#63463ebdbc04232fc42630ec72d137cd4400975d" + integrity sha512-GhutNJrvTYD6s1moo+kyq7lD9DeR5HDyXo4bDFlDSkepC9kVKY+KK/NSZFzCmeXeia3kEzVuToQmHRdugyZHxw== optionalDependencies: fsevents "~2.3.2" +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +sass@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.0.tgz#24873673265e2a4fe3d3a997f714971db2fba1f4" + integrity sha512-C4zp79GCXZfK0yoHZg+GxF818/aclhp9F48XBu/+bm9vXEVAYov9iU3FBVRMq3Hx3OA4jfKL+p2K9180mEh0xQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== source-map@^0.5.0: version "0.5.7" @@ -1154,39 +1491,12 @@ source-map@^0.5.0: integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= "stream-chat-react@link:../..": - version "0.0.0-development" - dependencies: - "@braintree/sanitize-url" "5.0.0" - "@stream-io/stream-chat-css" "^2.2.1" - dayjs "^1.10.4" - emoji-mart "3.0.1" - emoji-regex "^9.2.0" - i18next "^19.8.4" - isomorphic-ws "^4.0.1" - linkifyjs "^2.1.9" - lodash.debounce "^4.0.8" - lodash.isequal "^4.5.0" - lodash.throttle "^4.1.1" - lodash.uniqby "^4.7.0" - mdast-util-find-and-replace "1.1.1" - pretty-bytes "^5.4.1" - prop-types "^15.7.2" - react-fast-compare "^3.2.0" - react-file-utils "1.1.9" - react-image-gallery "^1.2.7" - react-is "^17.0.1" - react-markdown "^5.0.3" - react-player "^2.7.0" - react-textarea-autosize "^8.3.0" - react-virtuoso "^2.8.2" - textarea-caret "^3.1.0" - uuid "^8.3.1" - optionalDependencies: - "@stream-io/transliterate" "^1.5.5" - mml-react "^0.4.2" + version "0.0.0" + uid "" "stream-chat@link:../../node_modules/stream-chat": version "0.0.0" + uid "" supports-color@^5.3.0: version "5.5.0" @@ -1195,6 +1505,11 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + textarea-caret@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/textarea-caret/-/textarea-caret-3.1.0.tgz#5d5a35bb035fd06b2ff0e25d5359e97f2655087f" @@ -1205,6 +1520,13 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + trough@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" @@ -1220,10 +1542,10 @@ tslib@^2.0.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -typescript@^4.3.2: - version "4.4.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" - integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== +typescript@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== unified@^9.0.0: version "9.2.2" @@ -1290,11 +1612,6 @@ use-latest@^1.0.0: dependencies: use-isomorphic-layout-effect "^1.0.0" -uuid@^8.3.1: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" @@ -1313,18 +1630,30 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -vite@^2.6.4: - version "2.6.13" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.6.13.tgz#16b3ec85a66d5b461ac29a903874d4357f9af432" - integrity sha512-+tGZ1OxozRirTudl4M3N3UTNJOlxdVo/qBl2IlDEy/ZpTFcskp+k5ncNjayR3bRYTCbqSOFz2JWGN1UmuDMScA== +vite@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.3.tgz#c7b2ed9505a36a04be1d5d23aea4ea6fc028043f" + integrity sha512-sDIpIcl3mv1NUaSzZwiXGEy1ZoWwwC2vkxUHY6yiDacR6zf//ZFuBJrozO62gedpE43pmxnLATNR5IYUdAEkMQ== dependencies: - esbuild "^0.13.2" - postcss "^8.3.8" - resolve "^1.20.0" - rollup "^2.57.0" + esbuild "^0.14.47" + postcss "^8.4.14" + resolve "^1.22.1" + rollup "^2.75.6" optionalDependencies: fsevents "~2.3.2" +warning@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +ws@^7.4.4: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + xtend@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" diff --git a/package.json b/package.json index efee6b700..0698f5d6c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "jsnext:main": "dist/index.js", - "style": "dist/css/index.css", + "style": "dist/css/v2/index.css", "sideEffects": [ "*.css" ], @@ -29,8 +29,9 @@ ], "dependencies": { "@braintree/sanitize-url": "6.0.0", - "@juggle/resize-observer": "^3.3.1", - "@stream-io/stream-chat-css": "^2.10.1", + "@popperjs/core": "^2.11.5", + "@stream-io/stream-chat-css": "3.0.0", + "clsx": "^1.1.1", "dayjs": "^1.10.4", "emoji-mart": "3.0.1", "emoji-regex": "^9.2.0", @@ -38,19 +39,20 @@ "isomorphic-ws": "^4.0.1", "linkifyjs": "^2.1.9", "lodash.debounce": "^4.0.8", - "lodash.isequal": "^4.5.0", "lodash.throttle": "^4.1.1", "lodash.uniqby": "^4.7.0", "mdast-util-find-and-replace": "^2.2.1", "nanoid": "^3.3.4", "pretty-bytes": "^5.4.1", "prop-types": "^15.7.2", + "react-dropzone": "^14.2.2", "react-fast-compare": "^3.2.0", - "react-file-utils": "^1.1.12", + "react-file-utils": "^1.2.0", "react-image-gallery": "^1.2.7", "react-is": "^18.1.0", "react-markdown": "^5.0.3", "react-player": "^2.10.1", + "react-popper": "^2.3.0", "react-textarea-autosize": "^8.3.0", "react-virtuoso": "^2.16.5", "textarea-caret": "^3.1.0" @@ -99,10 +101,9 @@ "@types/dotenv": "^8.2.0", "@types/emoji-mart": "^3.0.9", "@types/linkifyjs": "^2.1.3", - "@types/lodash.debounce": "^4.0.6", - "@types/lodash.isequal": "^4.5.5", - "@types/lodash.throttle": "^4.1.6", - "@types/lodash.uniqby": "^4.7.6", + "@types/lodash.debounce": "^4.0.7", + "@types/lodash.throttle": "^4.1.7", + "@types/lodash.uniqby": "^4.7.7", "@types/mdast": "^3.0.10", "@types/moment": "^2.13.0", "@types/react": "^18.0.8", @@ -171,7 +172,7 @@ "style-loader": "^2.0.0", "ts-jest": "^26.5.1", "tslib": "2.3.0", - "typescript": "4.3.5", + "typescript": "^4.7.4", "url-loader": "^4.1.1", "webpack": "4.44.2", "webpack-cli": "^3.3.12", @@ -207,7 +208,8 @@ "browse-examples": "ladle serve", "e2e": "playwright test", "e2e-fixtures": "node e2e/fixtures.mjs", - "e2e-container": "./e2e/scripts/run_in_container.sh" + "e2e-container": "./e2e/scripts/run_in_container.sh", + "docs-merge": "scripts/merge-stream-chat-css-docs.sh" }, "resolutions": { "ast-types": "^0.14.0", diff --git a/rollup.config.js b/rollup.config.js index 7bf9594e9..dd53a56cc 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -91,8 +91,10 @@ const basePlugins = ({ useBrowserResolve = false }) => [ copy({ targets: [ { dest: 'dist/assets', src: './node_modules/@stream-io/stream-chat-css/dist/assets/*' }, - { dest: 'dist/css', src: './node_modules/@stream-io/stream-chat-css/dist/css/index.css' }, + { dest: 'dist/css', src: './node_modules/@stream-io/stream-chat-css/dist/css/*' }, { dest: 'dist/scss', src: './node_modules/@stream-io/stream-chat-css/dist/scss/*' }, + { dest: 'dist/css/v2', src: './node_modules/@stream-io/stream-chat-css/dist/v2/css/*' }, + { dest: 'dist/scss/v2', src: './node_modules/@stream-io/stream-chat-css/dist/v2/scss/*' }, ], verbose: process.env.VERBOSE, watch: process.env.ROLLUP_WATCH, diff --git a/scripts/merge-stream-chat-css-docs.sh b/scripts/merge-stream-chat-css-docs.sh new file mode 100755 index 000000000..1cd6f4bb0 --- /dev/null +++ b/scripts/merge-stream-chat-css-docs.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +usage() { + echo "Missing path to stream-chat-css directory" + echo "Usage: $(basename $0) " +} + +main() { + if [ $# -eq 0 ]; then + usage + exit 1 + fi + + STREAM_CHAT_CSS_DOCS_PATH=$1; + cp -r "$STREAM_CHAT_CSS_DOCS_PATH"/* ./docusaurus/docs/React; +} + + +main $* +exit 0 diff --git a/src/components/Attachment/Attachment.tsx b/src/components/Attachment/Attachment.tsx index 936bafb03..caada6811 100644 --- a/src/components/Attachment/Attachment.tsx +++ b/src/components/Attachment/Attachment.tsx @@ -1,21 +1,25 @@ -import React from 'react'; +import React, { useMemo } from 'react'; +import { sanitizeUrl } from '@braintree/sanitize-url'; +import type { ReactPlayerProps } from 'react-player'; +import type { Attachment as StreamAttachment } from 'stream-chat'; import { + GroupedRenderedAttachment, isAudioAttachment, isFileAttachment, - isGalleryAttachmentType, - isImageAttachment, isMediaAttachment, - renderAudio, - renderCard, - renderFile, - renderGallery, - renderImage, - renderMedia, + isScrapedContent, + isUploadedImage, } from './utils'; -import type { ReactPlayerProps } from 'react-player'; -import type { Attachment as StreamAttachment } from 'stream-chat'; +import { + AudioContainer, + CardContainer, + FileContainer, + GalleryContainer, + ImageContainer, + MediaContainer, +} from './AttachmentContainer'; import type { AttachmentActionsProps } from './AttachmentActions'; import type { AudioProps } from './Audio'; @@ -26,6 +30,22 @@ import type { ActionHandlerReturnType } from '../Message/hooks/useActionHandler' import type { DefaultStreamChatGenerics } from '../../types/types'; +const CONTAINER_MAP = { + audio: AudioContainer, + card: CardContainer, + file: FileContainer, + media: MediaContainer, +} as const; + +export const ATTACHMENT_GROUPS_ORDER = [ + 'card', + 'gallery', + 'image', + 'media', + 'audio', + 'file', +] as const; + export type AttachmentProps< StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics > = { @@ -57,54 +77,95 @@ export const Attachment = < >( props: AttachmentProps, ) => { - const { attachments, ...rest } = props; - - const gallery = { - images: attachments?.filter( - (attachment) => - attachment.type === 'image' && !(attachment.og_scrape_url || attachment.title_link), - ), - type: 'gallery', - }; - - const newAttachments = - gallery.images.length >= 2 - ? [ - ...attachments.filter( - (attachment) => - !( - attachment.type === 'image' && !(attachment.og_scrape_url || attachment.title_link) - ), - ), - gallery, - ] - : attachments; + const { attachments } = props; + + const groupedAttachments = useMemo(() => renderGroupedAttachments(props), [attachments]); return ( - <> - {newAttachments.map((attachment) => { - if (isGalleryAttachmentType(attachment)) { - return renderGallery({ ...rest, attachment }); - } +
+ {ATTACHMENT_GROUPS_ORDER.reduce( + (acc, groupName) => [...acc, ...groupedAttachments[groupName]], + [] as React.ReactNode[], + )} +
+ ); +}; - if (isImageAttachment(attachment)) { - return renderImage({ ...rest, attachment }); - } +const renderGroupedAttachments = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +>({ + attachments, + ...rest +}: AttachmentProps): GroupedRenderedAttachment => { + const uploadedImages: StreamAttachment[] = []; - if (isAudioAttachment(attachment)) { - return renderAudio({ ...rest, attachment }); - } + const containers = attachments.reduce( + (acc, attachment) => { + if (isUploadedImage(attachment)) { + uploadedImages.push({ + ...attachment, + image_url: sanitizeUrl(attachment.image_url), + thumb_url: sanitizeUrl(attachment.thumb_url), + }); + } else { + const attachmentType = getAttachmentType(attachment); - if (isFileAttachment(attachment)) { - return renderFile({ ...rest, attachment }); + if (attachmentType) { + const Container = CONTAINER_MAP[attachmentType]; + acc[attachmentType].push( + , + ); } + } + return acc; + }, + { + audio: [], + card: [], + file: [], + gallery: [], + image: [], + media: [], + }, + ); - if (isMediaAttachment(attachment)) { - return renderMedia({ ...rest, attachment }); - } + if (uploadedImages.length > 1) { + containers['gallery'] = [ + , + ]; + } else if (uploadedImages.length === 1) { + containers['image'] = [ + , + ]; + } + return containers; +}; - return renderCard({ ...rest, attachment }); - })} - - ); +const getAttachmentType = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +>( + attachment: AttachmentProps['attachments'][number], +): keyof typeof CONTAINER_MAP | null => { + if (isScrapedContent(attachment)) { + return 'card'; + } else if (isMediaAttachment(attachment)) { + return 'media'; + } else if (isAudioAttachment(attachment)) { + return 'audio'; + } else if (isFileAttachment(attachment)) { + return 'file'; + } + + return null; }; diff --git a/src/components/Attachment/AttachmentActions.tsx b/src/components/Attachment/AttachmentActions.tsx index 697473f1d..63fae8cec 100644 --- a/src/components/Attachment/AttachmentActions.tsx +++ b/src/components/Attachment/AttachmentActions.tsx @@ -1,9 +1,7 @@ import React from 'react'; - import type { Action, Attachment } from 'stream-chat'; import type { ActionHandlerReturnType } from '../Message/hooks/useActionHandler'; - import type { DefaultStreamChatGenerics } from '../../types/types'; export type AttachmentActionsProps< @@ -30,16 +28,12 @@ const UnMemoizedAttachmentActions = < event: React.MouseEvent, name?: string, value?: string, - ) => { - if (actionHandler) { - actionHandler(name, value, event); - } - }; + ) => actionHandler?.(name, value, event); return (
- {text} + {text} {actions.map((action) => ( +); + +type ProgressBarProps = { + progress: number; +} & Pick, 'onClick'>; + +export const ProgressBar = ({ onClick, progress }: ProgressBarProps) => ( +
+
+
+); + +const AudioV2 = ({ og }: AudioProps) => { + const { asset_url, file_size, title } = og; + const { audioRef, isPlaying, progress, seek, togglePlay } = useAudioController(); + + if (!asset_url) return null; + + const dataTestId = 'audio-widget'; + const rootClassName = 'str-chat__message-attachment-audio-widget'; + + return ( +
+ +
+ +
+
+
+
{title}
+ +
+
+ + +
+
+
+ ); +}; + +const UnMemoizedAudio = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +>( + props: AudioProps, +) => { + const { themeVersion } = useChatContext('Audio'); + + return themeVersion === '1' ? : ; +}; + /** * Audio attachment with play/pause button and progress bar */ diff --git a/src/components/Attachment/Card.tsx b/src/components/Attachment/Card.tsx index 53f9d6be6..4eba24339 100644 --- a/src/components/Attachment/Card.tsx +++ b/src/components/Attachment/Card.tsx @@ -1,12 +1,48 @@ -import React, { PropsWithChildren } from 'react'; +import React from 'react'; +import clsx from 'clsx'; +import ReactPlayer from 'react-player'; +import { AudioProps, PlayButton, ProgressBar } from './Audio'; +import { ImageComponent } from '../Gallery'; import { SafeAnchor } from '../SafeAnchor'; +import { useAudioController } from './hooks/useAudioController'; -import { useTranslationContext } from '../../context/TranslationContext'; +import { useChatContext } from '../../context/ChatContext'; import { useChannelStateContext } from '../../context/ChannelStateContext'; +import { useTranslationContext } from '../../context/TranslationContext'; + import type { Attachment } from 'stream-chat'; +import type { RenderAttachmentProps } from './utils'; +import type { Dimensions } from '../../types/types'; -export type CardProps = { +const getHostFromURL = (url?: string | null) => { + if (url !== undefined && url !== null) { + const [trimmedUrl] = url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '').split('/'); + + return trimmedUrl; + } + return null; +}; + +const UnableToRenderCard = ({ type }: { type?: CardProps['type'] }) => { + const { t } = useTranslationContext('Card'); + + return ( +
+
+
+ {t('this content could not be displayed')} +
+
+
+ ); +}; + +interface CardV1Props { giphy?: Attachment['giphy']; /** The url of the full sized image */ image_url?: string; @@ -22,15 +58,14 @@ export type CardProps = { title_link?: string; /** The card type used in the className attribute */ type?: string; -}; +} -const UnMemoizedCard = (props: PropsWithChildren) => { +const CardV1 = (props: CardV1Props) => { const { giphy, image_url, og_scrape_url, text, thumb_url, title, title_link, type } = props; - const { t } = useTranslationContext('Card'); const { giphyVersion: giphyVersionName } = useChannelStateContext('Card'); let image = thumb_url || image_url; - const dimensions: { height?: string; width?: string } = {}; + const dimensions: Dimensions = {}; if (type === 'giphy' && typeof giphy !== 'undefined') { const giphyVersion = giphy[giphyVersionName as keyof NonNullable]; @@ -39,27 +74,8 @@ const UnMemoizedCard = (props: PropsWithChildren) => { dimensions.width = giphyVersion.width; } - const trimUrl = (url?: string | null) => { - if (url !== undefined && url !== null) { - const [trimmedUrl] = url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '').split('/'); - - return trimmedUrl; - } - return null; - }; - if (!title && !title_link && !image) { - return ( -
-
-
- {t('this content could not be displayed')} -
-
-
- ); + return ; } if (!title_link && !og_scrape_url) { @@ -84,7 +100,7 @@ const UnMemoizedCard = (props: PropsWithChildren) => { rel='noopener noreferrer' target='_blank' > - {trimUrl(title_link || og_scrape_url)} + {getHostFromURL(title_link || og_scrape_url)} )}
@@ -93,6 +109,145 @@ const UnMemoizedCard = (props: PropsWithChildren) => { ); }; +const SourceLink = ({ author_name, url }: Pick & { url: string }) => ( +
+ + {author_name || getHostFromURL(url)} + +
+); + +type CardHeaderProps = Pick< + CardProps, + 'asset_url' | 'title' | 'type' | 'image_url' | 'thumb_url' +> & { + dimensions: Dimensions; + image?: string; +}; + +const CardHeader = (props: CardHeaderProps) => { + const { asset_url, dimensions, image, image_url, thumb_url, title, type } = props; + + let visual = null; + if (asset_url && type === 'video') { + visual = ( + + ); + } else if (image) { + visual = ( + + ); + } + + return visual ? ( +
+ {visual} +
+ ) : null; +}; + +type CardContentProps = RenderAttachmentProps['attachment']; + +const CardContent = (props: CardContentProps) => { + const { author_name, og_scrape_url, text, title, title_link, type } = props; + const url = title_link || og_scrape_url; + + return ( +
+ {type === 'audio' ? ( + + ) : ( +
+ {url && } + {title &&
{title}
} + {text &&
{text}
} +
+ )} +
+ ); +}; + +const CardV2 = (props: CardProps) => { + const { asset_url, giphy, image_url, thumb_url, title, title_link, type } = props; + const { giphyVersion: giphyVersionName } = useChannelStateContext('CardHeader'); + + let image = thumb_url || image_url; + const dimensions: { height?: string; width?: string } = {}; + + if (type === 'giphy' && typeof giphy !== 'undefined') { + const giphyVersion = giphy[giphyVersionName as keyof NonNullable]; + image = giphyVersion.url; + dimensions.height = giphyVersion.height; + dimensions.width = giphyVersion.width; + } + + if (!title && !title_link && !asset_url && !image) { + return ; + } + + return ( +
+ + +
+ ); +}; + +export const CardAudio = ({ + og: { asset_url, author_name, og_scrape_url, text, title, title_link }, +}: AudioProps) => { + const { audioRef, isPlaying, progress, seek, togglePlay } = useAudioController(); + + const url = title_link || og_scrape_url; + const dataTestId = 'card-audio-widget'; + const rootClassName = 'str-chat__message-attachment-card-audio-widget'; + return ( +
+ {asset_url && ( + <> + +
+
+ +
+ +
+ + )} +
+ {url && } + {title &&
{title}
} + {text && ( +
{text}
+ )} +
+
+ ); +}; + +export type CardProps = RenderAttachmentProps['attachment']; + +const UnMemoizedCard = (props: CardProps) => { + const { themeVersion } = useChatContext('Card'); + + return themeVersion === '2' ? : ; +}; + /** * Simple Card Layout for displaying links */ diff --git a/src/components/Attachment/DownloadButton.tsx b/src/components/Attachment/DownloadButton.tsx new file mode 100644 index 000000000..dbd98e861 --- /dev/null +++ b/src/components/Attachment/DownloadButton.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { DownloadIcon } from './icons'; +import { SafeAnchor } from '../SafeAnchor'; + +type DownloadButtonProps = { + assetUrl?: string; +}; + +export const DownloadButton = ({ assetUrl }: DownloadButtonProps) => ( + + + +); diff --git a/src/components/Attachment/FileAttachment.tsx b/src/components/Attachment/FileAttachment.tsx index 53cb09540..e263c2c23 100644 --- a/src/components/Attachment/FileAttachment.tsx +++ b/src/components/Attachment/FileAttachment.tsx @@ -1,10 +1,12 @@ import React from 'react'; -import prettybytes from 'pretty-bytes'; import { FileIcon } from 'react-file-utils'; +import type { Attachment } from 'stream-chat'; -import { SafeAnchor } from '../SafeAnchor'; +import { DownloadButton } from './DownloadButton'; +import { FileSizeIndicator } from './FileSizeIndicator'; +import { SafeAnchor } from '../SafeAnchor/SafeAnchor'; -import type { Attachment } from 'stream-chat'; +import { useChatContext } from '../../context/ChatContext'; import type { DefaultStreamChatGenerics } from '../../types/types'; @@ -14,25 +16,52 @@ export type FileAttachmentProps< attachment: Attachment; }; -const UnMemoizedFileAttachment = < +const UnMemoizedFileAttachmentV1 = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +>({ + attachment, +}: FileAttachmentProps) => ( +
+ +
+ + {attachment.title} + + +
+
+); + +const UnMemoizedFileAttachmentV2 = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics ->( - props: FileAttachmentProps, -) => { - const { attachment } = props; - - return ( -
- -
- +>({ + attachment, +}: FileAttachmentProps) => ( +
+ +
+
+
{attachment.title} - - {attachment.file_size && Number.isFinite(Number(attachment.file_size)) && ( - {prettybytes(attachment.file_size as number)} - )} +
+
+
+
+); + +const UnMemoizedFileAttachment = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics +>({ + attachment, +}: FileAttachmentProps) => { + const { themeVersion } = useChatContext('FileAttachment'); + + return themeVersion === '2' ? ( + + ) : ( + ); }; diff --git a/src/components/Attachment/FileSizeIndicator.tsx b/src/components/Attachment/FileSizeIndicator.tsx new file mode 100644 index 000000000..d518318ec --- /dev/null +++ b/src/components/Attachment/FileSizeIndicator.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import prettybytes from 'pretty-bytes'; + +type FileSizeIndicatorProps = { + fileSize?: number; +}; + +export const FileSizeIndicator = ({ fileSize }: FileSizeIndicatorProps) => { + if (!(fileSize && Number.isFinite(Number(fileSize)))) return null; + + return ( + {prettybytes(fileSize)} + ); +}; diff --git a/src/components/Attachment/__tests__/Attachment.test.js b/src/components/Attachment/__tests__/Attachment.test.js index 0681b044d..a82b93c24 100644 --- a/src/components/Attachment/__tests__/Attachment.test.js +++ b/src/components/Attachment/__tests__/Attachment.test.js @@ -1,182 +1,252 @@ import React from 'react'; -import { render, waitFor } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import { nanoid } from 'nanoid'; import { generateAttachmentAction, generateAudioAttachment, - generateCardAttachment, generateFileAttachment, generateGiphyAttachment, generateImageAttachment, - generateImgurAttachment, + generateScrapedAudioAttachment, + generateScrapedDataAttachment, + generateScrapedImageAttachment, generateVideoAttachment, } from 'mock-builders'; import { Attachment } from '../Attachment'; import { SUPPORTED_VIDEO_FORMATS } from '../utils'; +import { generateScrapedVideoAttachment } from '../../../mock-builders'; -const Audio = () =>
; -const Card = () =>
; -const Media = () =>
; +const Audio = (props) =>
{props.customTestId}
; +const Card = (props) =>
{props.customTestId}
; +const Media = (props) =>
{props.customTestId}
; const AttachmentActions = () =>
; -const Image = () =>
; -const File = () =>
; -const Gallery = () =>
; - -const getAttachmentComponent = (props) => ( - -); - -describe('Attachment', () => { - it('should render Audio component for "audio" type attachment', async () => { - const attachment = generateAudioAttachment(); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('audio-attachment')).toBeInTheDocument(); - }); - }); - it('should render File component for "file" type attachment', async () => { - const attachment = generateFileAttachment(); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('file-attachment')).toBeInTheDocument(); - }); - }); - it('should render Card component for "imgur" type attachment', async () => { - const attachment = generateImgurAttachment(); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('card-attachment')).toBeInTheDocument(); +const Image = (props) =>
{props.customTestId}
; +const File = (props) =>
{props.customTestId}
; +const Gallery = (props) =>
{props.customTestId}
; + +const ATTACHMENTS = { + scraped: { + audio: generateScrapedAudioAttachment(), + giphy: generateGiphyAttachment(), + image: generateScrapedImageAttachment(), + unrecognized: generateScrapedDataAttachment({ type: nanoid() }), + video: generateScrapedVideoAttachment(), + }, + uploaded: { + audio: generateAudioAttachment(), + file: generateFileAttachment(), + gallery: Array.from({ length: 3 }, generateImageAttachment), + image: generateImageAttachment(), + video: generateVideoAttachment(), + }, +}; + +const renderComponent = (props) => + render( + , + ); + +describe('attachment', () => { + describe('non-scraped content', () => { + it('should render empty attachment list if unrecognized type', () => { + const { container } = renderComponent({ attachments: [{}] }); + expect(container.firstChild).toBeEmptyDOMElement(); }); - }); - it('should render Card component for "giphy" type attachment', async () => { - const attachment = generateGiphyAttachment(); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('card-attachment')).toBeInTheDocument(); + + const cases = { + audio: { + attachments: [ATTACHMENTS.uploaded.audio], + case: '"audio" type attachment', + renderedComponent: 'Audio', + testId: 'audio-attachment', + }, + file: { + attachments: [ATTACHMENTS.uploaded.file], + case: '"file" type attachment', + renderedComponent: 'File', + testId: 'file-attachment', + }, + gallery: { + attachments: ATTACHMENTS.uploaded.gallery, + case: 'message with multiple "image" attachments', + renderedComponent: 'Gallery', + testId: 'gallery-attachment', + }, + image: { + attachments: [ATTACHMENTS.uploaded.image], + case: 'message with single image', + renderedComponent: 'Image', + testId: 'image-attachment', + }, + }; + it.each` + attachments | case | renderedComponent | testId + ${cases.audio.attachments} | ${cases.audio.case} | ${cases.audio.renderedComponent} | ${cases.audio.testId} + ${cases.file.attachments} | ${cases.file.case} | ${cases.file.renderedComponent} | ${cases.file.testId} + ${cases.gallery.attachments} | ${cases.gallery.case} | ${cases.gallery.renderedComponent} | ${cases.gallery.testId} + ${cases.image.attachments} | ${cases.image.case} | ${cases.image.renderedComponent} | ${cases.image.testId} + `('should render $renderedComponent component for $case', async ({ attachments, testId }) => { + renderComponent({ attachments }); + await waitFor(() => { + expect(screen.getByTestId(testId)).toBeInTheDocument(); + }); }); + + it.each(SUPPORTED_VIDEO_FORMATS.map((f) => [f]))( + 'should render Media component for video of %s mime-type attachment', + async (mime_type) => { + const attachment = generateVideoAttachment({ mime_type }); + renderComponent({ attachments: [attachment] }); + await waitFor(() => { + expect(screen.getByTestId('media-attachment')).toBeInTheDocument(); + }); + }, + ); }); - describe('gallery type attachment', () => { - it('should render Gallery component if attachments contains multiple type "image" attachments', async () => { - const image = generateImageAttachment({ + describe('scraped content', () => { + it('should render null, if og_scrape_url & title_link are undefined', () => { + const attachment = generateScrapedDataAttachment({ og_scrape_url: undefined, title_link: undefined, }); - const attachments = [image, image, image]; - const { getByTestId } = render(getAttachmentComponent({ attachments })); + const { container } = renderComponent({ attachments: [attachment] }); + expect(container.firstChild).toBeEmptyDOMElement(); + }); + + const cases = [ + { + attachments: [ATTACHMENTS.scraped.unrecognized], + case: 'not recognized, but has title_link or og_scrape_url', + }, + { + attachments: [ATTACHMENTS.scraped.giphy], + case: 'giphy', + }, + { + attachments: [ATTACHMENTS.scraped.image], + case: 'image', + }, + { + attachments: [ATTACHMENTS.scraped.video], + case: 'video', + }, + { + attachments: [ATTACHMENTS.scraped.audio], + case: 'audio', + }, + ]; + it.each` + attachments | case + ${cases[0].attachments} | ${cases[0].case} + ${cases[1].attachments} | ${cases[1].case} + ${cases[2].attachments} | ${cases[2].case} + ${cases[3].attachments} | ${cases[3].case} + ${cases[4].attachments} | ${cases[4].case} + `('should render Card if attachment type is $case', async ({ attachments }) => { + renderComponent({ attachments }); await waitFor(() => { - expect(getByTestId('gallery-attachment')).toBeInTheDocument(); + expect(screen.getByTestId('card-attachment')).toBeInTheDocument(); }); }); + }); + + describe('combines scraped & uploaded content', () => { it('should render Image and Card if one image has title_link or og_scrape_url', async () => { - const image = generateImageAttachment({ - og_scrape_url: undefined, - title_link: undefined, - }); - const card = generateImageAttachment(); + const image = generateImageAttachment(); + const card = generateScrapedImageAttachment(); const attachments = [card, image]; - const { getByTestId } = render(getAttachmentComponent({ attachments })); + renderComponent({ attachments }); await waitFor(() => { - expect(getByTestId('image-attachment')).toBeInTheDocument(); - expect(getByTestId('card-attachment')).toBeInTheDocument(); + expect(screen.getByTestId('image-attachment')).toBeInTheDocument(); + expect(screen.getByTestId('card-attachment')).toBeInTheDocument(); }); }); - it('should render Gallery and Card if threres multiple images without and image with title_link or og_scrape_url', async () => { - const image = generateImageAttachment({ - og_scrape_url: undefined, - title_link: undefined, + it('should render attachments in default order', async () => { + const { container } = renderComponent({ + attachments: [ + ATTACHMENTS.uploaded.file, + ATTACHMENTS.uploaded.audio, + ATTACHMENTS.uploaded.video, + ATTACHMENTS.scraped.audio, + ATTACHMENTS.uploaded.image, + ATTACHMENTS.uploaded.image, + ], }); - const card = generateImageAttachment(); - const attachments = [image, image, card]; - const { getByTestId } = render(getAttachmentComponent({ attachments })); await waitFor(() => { - expect(getByTestId('gallery-attachment')).toBeInTheDocument(); - expect(getByTestId('card-attachment')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); }); - }); - describe('image type attachment', () => { - it('should render Card component if attachment has title_link or og_scrape_url', async () => { - const attachment = generateImageAttachment(); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('card-attachment')).toBeInTheDocument(); - }); - }); - it('should otherwise render Image component', async () => { - const attachment = generateImageAttachment({ - og_scrape_url: undefined, - title_link: undefined, + + it('should render all scraped attachments', async () => { + const scrapedAudio = { ...ATTACHMENTS.scraped.audio, customTestId: nanoid() }; + const scrapedVideo = { ...ATTACHMENTS.scraped.video, customTestId: nanoid() }; + const scrapedImage = { ...ATTACHMENTS.scraped.image, customTestId: nanoid() }; + const { queryAllByTestId } = renderComponent({ + attachments: [ + ATTACHMENTS.uploaded.file, + ATTACHMENTS.uploaded.audio, + ATTACHMENTS.uploaded.video, + scrapedAudio, + scrapedVideo, + scrapedImage, + ATTACHMENTS.uploaded.image, + ATTACHMENTS.uploaded.image, + ], }); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); await waitFor(() => { - expect(getByTestId('image-attachment')).toBeInTheDocument(); + /* eslint-disable jest-dom/prefer-in-document */ + const Card = queryAllByTestId('card-attachment'); + + expect(Card).toHaveLength(3); + + const idList = Array.from(Card).map((v) => v.textContent); + expect(idList).toContain(scrapedAudio.customTestId); + expect(idList).toContain(scrapedVideo.customTestId); + expect(idList).toContain(scrapedImage.customTestId); }); }); - it('should render AttachmentActions component if attachment has actions', async () => { - const attachment = generateImageAttachment({ - actions: [generateAttachmentAction(), generateAttachmentAction()], + + it('should render all uploaded attachments', async () => { + const { container } = renderComponent({ + attachments: [ + ATTACHMENTS.uploaded.file, + ATTACHMENTS.uploaded.file, + ATTACHMENTS.uploaded.audio, + ATTACHMENTS.uploaded.audio, + ATTACHMENTS.uploaded.video, + ATTACHMENTS.uploaded.video, + ATTACHMENTS.uploaded.image, + ATTACHMENTS.uploaded.image, + ], }); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); await waitFor(() => { - expect(getByTestId('attachment-actions')).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); }); }); - describe('video type attachment', () => { - it.each(SUPPORTED_VIDEO_FORMATS.map((f) => [f]))( - 'should render Media component for video of %s mime-type attachment', - async (mime_type) => { - const attachment = generateVideoAttachment({ mime_type }); - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('media-attachment')).toBeInTheDocument(); - }); - }, - ); - it('should render video player if video type is video', async () => { - const attachment = generateVideoAttachment({ - asset_url: 'https://www.youtube.com/embed/UaeOlIa0LL8', - author_name: 'YouTube', - image_url: 'https://i.ytimg.com/vi/UaeOlIa0LL8/maxresdefault.jpg', - mime_type: 'nothing', - og_scrape_url: 'https://www.youtube.com/watch?v=UaeOlIa0LL8', - text: - "It's Gmod TTT! Praise Ben in the church of Bon! Hail the great jetstream in Gmod TTT! Confused? Check out the new roles here: https://twitter.com/yogscast/st...", - thumb_url: 'https://i.ytimg.com/vi/UaeOlIa0LL8/maxresdefault.jpg', - title: 'THE CHURCH OF BON! | Gmod TTT', - title_link: 'https://www.youtube.com/watch?v=UaeOlIa0LL8', - type: 'video', - }); - - const { getByTestId } = render(getAttachmentComponent({ attachments: [attachment] })); - await waitFor(() => { - expect(getByTestId('media-attachment')).toBeInTheDocument(); - }); + it('should render AttachmentActions component if attachment has actions', async () => { + const action = generateAttachmentAction(); + const attachment = generateImageAttachment({ + actions: [action, action], }); - }); - it('should render "Card" if attachment type is not recognized, but has title_link or og_scrape_url', async () => { - const { getByTestId } = render( - getAttachmentComponent({ - attachments: [generateCardAttachment({ type: nanoid() })], - }), - ); + renderComponent({ attachments: [attachment] }); await waitFor(() => { - expect(getByTestId('card-attachment')).toBeInTheDocument(); + expect(screen.getByTestId('attachment-actions')).toBeInTheDocument(); }); }); }); diff --git a/src/components/Attachment/__tests__/Audio.test.js b/src/components/Attachment/__tests__/Audio.test.js index 2f987e478..08a58ebd8 100644 --- a/src/components/Attachment/__tests__/Audio.test.js +++ b/src/components/Attachment/__tests__/Audio.test.js @@ -1,17 +1,28 @@ import React from 'react'; +import prettybytes from 'pretty-bytes'; import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { generateAudioAttachment } from 'mock-builders'; import { Audio } from '../Audio'; +import { PROGRESS_UPDATE_INTERVAL } from '../hooks/useAudioController'; + +import { ChatContext } from '../../../context/ChatContext'; + +import { generateAudioAttachment } from 'mock-builders'; -const mockAudioAsset = generateAudioAttachment(); +const AUDIO = generateAudioAttachment(); const renderComponent = ( props = { - og: mockAudioAsset, + chatContext: { themeVersion: '1' }, + og: AUDIO, }, -) => render(