├── .commitlintrc.json ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── build.yml │ ├── dependabot-auto-merge.yml │ ├── lint.yml │ ├── publish.yml │ └── release.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg └── pre-commit ├── .npmignore ├── .release-it.yml ├── CHANGELOG.md ├── LICENSE ├── LICENSE.header ├── README.md ├── license-checker-config.json ├── package.json ├── public ├── config.js ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.js ├── assets │ ├── bg-chat.png │ ├── hand-ok.png │ ├── hand-watch.png │ ├── ic_download_chat.svg │ ├── ic_loader_chat.svg │ ├── login-v2.72cd8a26.svg │ └── notification.mp3 ├── components │ ├── AlertDialog │ │ └── index.tsx │ ├── BackdropComponent │ │ ├── index.js │ │ └── style.js │ ├── ChangeSession │ │ ├── Dialog │ │ │ ├── index.js │ │ │ └── style.js │ │ └── index.js │ ├── ChatPage │ │ ├── AudioComponent │ │ │ ├── index.js │ │ │ └── style.js │ │ ├── ChatComponent │ │ │ ├── ImageModal │ │ │ │ ├── index.js │ │ │ │ └── style.js │ │ │ ├── QuotedMessage │ │ │ │ └── index.tsx │ │ │ ├── index.js │ │ │ └── style.js │ │ ├── ContactsComponent │ │ │ ├── index.js │ │ │ └── style.js │ │ └── ConversasComponent │ │ │ ├── index.js │ │ │ └── style.js │ ├── Drawer │ │ ├── DrawerLeft │ │ │ └── index.tsx │ │ └── index.ts │ ├── ErrorModal │ │ ├── index.js │ │ └── style.js │ ├── Group │ │ └── CreateGroup │ │ │ ├── index.js │ │ │ └── style.js │ ├── Header │ │ ├── index.js │ │ └── style.js │ ├── MenuModal │ │ ├── index.js │ │ └── style.js │ ├── MyTooltip │ │ └── index.tsx │ └── Sidebar │ │ ├── index.js │ │ └── style.js ├── history.js ├── index.js ├── pages │ ├── ChatPage │ │ ├── index.js │ │ └── style.js │ ├── Contacts │ │ ├── index.js │ │ └── style.js │ ├── GroupPage │ │ ├── index.js │ │ └── style.js │ └── LoginPage │ │ ├── index.js │ │ └── style.js ├── react-app-env.d.ts ├── routes │ ├── dashboad.tsx │ └── index.js ├── services │ ├── api.js │ ├── auth.js │ └── socket-listener.js ├── style │ ├── GlobalStyle.js │ └── themes │ │ ├── dark.js │ │ └── light.js └── util │ ├── functionsMarkdown.js │ └── sessionHeader.js ├── tsconfig.json └── yarn.lock /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/commitlintrc.json", 3 | "extends": ["@commitlint/config-conventional"], 4 | "rules": { 5 | "body-max-line-length": [0, "always", 100], 6 | "header-max-length": [2, "always", 120], 7 | "subject-case": [1, "never", ["sentence-case", "start-case", "pascal-case", "upper-case"]] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | issuehunt: wppconnect-team 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | 8 | - package-ecosystem: 'npm' 9 | directory: '/' 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 20 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | 18 | - name: Setup Node 19 | uses: actions/setup-node@v3.3.0 20 | with: 21 | node-version: 16.x 22 | 23 | - name: Get yarn cache directory 24 | id: yarn-cache 25 | run: | 26 | echo "::set-output name=dir::$(yarn cache dir)" 27 | 28 | - name: Setup yarn cache 29 | uses: actions/cache@v3 30 | with: 31 | path: ${{ steps.yarn-cache.outputs.dir }} 32 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-yarn- 35 | 36 | - name: Install Dependencies 37 | run: yarn install 38 | 39 | - name: Build source 40 | run: yarn build 41 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | 3 | on: pull_request_target 4 | 5 | permissions: 6 | pull-requests: write 7 | contents: write 8 | 9 | jobs: 10 | dependabot: 11 | runs-on: ubuntu-latest 12 | if: ${{ github.actor == 'dependabot[bot]' }} 13 | steps: 14 | - name: Wait for tests to succeed 15 | uses: lewagon/wait-on-check-action@v1.1.1 16 | with: 17 | ref: ${{ github.event.pull_request.head.sha }} 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | running-workflow-name: dependabot 20 | wait-interval: 15 21 | 22 | - name: Dependabot metadata 23 | id: metadata 24 | uses: dependabot/fetch-metadata@v1.3.2 25 | with: 26 | github-token: '${{ secrets.GITHUB_TOKEN }}' 27 | 28 | - name: Enable auto-merge for Dependabot PRs 29 | if: >- 30 | ${{steps.metadata.outputs.update-type == 'version-update:semver-patch' 31 | || contains(steps.metadata.outputs.dependency-names, '@types')}} 32 | || contains(steps.metadata.outputs.dependency-names, 'commitlint')}} 33 | || contains(steps.metadata.outputs.dependency-names, 'eslint')}} 34 | || contains(steps.metadata.outputs.dependency-names, 'webpack')}} 35 | run: gh pr merge --auto --squash "$PR_URL" 36 | env: 37 | PR_URL: ${{github.event.pull_request.html_url}} 38 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 39 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | 18 | - name: Setup Node 19 | uses: actions/setup-node@v3.3.0 20 | with: 21 | node-version: 16.x 22 | 23 | - name: Get yarn cache directory 24 | id: yarn-cache 25 | run: | 26 | echo "::set-output name=dir::$(yarn cache dir)" 27 | 28 | - name: Setup yarn cache 29 | uses: actions/cache@v3 30 | with: 31 | path: ${{ steps.yarn-cache.outputs.dir }} 32 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-yarn- 35 | 36 | - name: Install Dependencies 37 | run: yarn install 38 | 39 | - name: Lint source 40 | run: yarn build 41 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Fetching tags 18 | run: git fetch --tags -f || true 19 | 20 | - name: Setup Node 21 | uses: actions/setup-node@v3.3.0 22 | with: 23 | node-version: 16.x 24 | registry-url: 'https://registry.npmjs.org' 25 | env: 26 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | 28 | - name: Get yarn cache directory 29 | id: yarn-cache 30 | run: | 31 | echo "::set-output name=dir::$(yarn cache dir)" 32 | 33 | - name: Setup yarn cache 34 | uses: actions/cache@v3 35 | with: 36 | path: ${{ steps.yarn-cache.outputs.dir }} 37 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 38 | restore-keys: | 39 | ${{ runner.os }}-yarn- 40 | 41 | - name: Install Dependencies 42 | run: yarn install 43 | env: 44 | PUPPETEER_SKIP_DOWNLOAD: true 45 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 46 | 47 | - name: Build 48 | run: yarn build 49 | 50 | - name: Publish in NPM 51 | run: yarn publish --access public 52 | env: 53 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 54 | 55 | - name: Generate Changelog 56 | id: generate_changelog 57 | run: | 58 | changelog=$(npm run changelog:last --silent) 59 | changelog="${changelog//$'\n'/'%0A'}" 60 | changelog="${changelog//$'\r'/'%0D'}" 61 | echo -e "set-output name=changelog::${changelog-}\n" 62 | echo -e "::set-output name=changelog::${changelog}\n" 63 | 64 | - name: Create Release 65 | id: create_release 66 | uses: actions/create-release@v1 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | with: 70 | tag_name: ${{ github.ref }} 71 | release_name: ${{ github.ref }} 72 | body: ${{ steps.generate_changelog.outputs.changelog }} 73 | draft: false 74 | prerelease: false 75 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | increment: 7 | description: 'Tipo de incremento: patch, minor, major ou pre*' 8 | required: true 9 | default: 'patch' 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | with: 18 | token: ${{ secrets.PERSONAL_TOKEN }} 19 | 20 | - name: Setup GIT 21 | run: | 22 | git config --global user.name "github-actions[bot]" 23 | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" 24 | 25 | - name: Setup Node 26 | uses: actions/setup-node@v3.3.0 27 | with: 28 | node-version: 16.x 29 | 30 | - name: Get yarn cache directory 31 | id: yarn-cache 32 | run: | 33 | echo "::set-output name=dir::$(yarn cache dir)" 34 | 35 | - name: Setup yarn cache 36 | uses: actions/cache@v3 37 | with: 38 | path: ${{ steps.yarn-cache.outputs.dir }} 39 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-yarn- 42 | 43 | - name: Install Dependencies 44 | run: yarn install 45 | 46 | - name: Release 47 | run: "npx release-it --increment ${{ github.event.inputs.increment }}" 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /node_modules 3 | *.png 4 | *.tgz 5 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | # npx --no-install pretty-quick --staged 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.tgz 3 | *.zip 4 | /*.png 5 | /log 6 | /src 7 | /tokens 8 | /WhatsAppImages 9 | license-checker-config.json 10 | LICENSE.header 11 | nodemon.json 12 | public 13 | requests.http 14 | yarn.lock 15 | -------------------------------------------------------------------------------- /.release-it.yml: -------------------------------------------------------------------------------- 1 | git: 2 | commitMessage: 'chore(release): v${version}' 3 | tagAnnotation: 'chore(release): v${version}' 4 | tagName: 'v${version}' 5 | 6 | hooks: 7 | after:bump: 8 | - 'npm run changelog:update' 9 | 10 | # automatic publish from github workflow 11 | npm: 12 | publish: false 13 | private: true 14 | registry: 'OMITTED' 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.3 (2022-06-21) 2 | 3 | 4 | 5 | ## 1.0.1 (2021-07-14) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * Removido dependências diretas de pacotes de terceiro ([5b4b62e](https://github.com/wppconnect-team/wppconnect-frontend/commit/5b4b62ec71904fcfb21e76b4c8a844d45ee48c30)) 11 | 12 | 13 | 14 | # 1.0.0 (2021-06-12) 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2021 WPPConnect Team 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE.header: -------------------------------------------------------------------------------- 1 | Copyright 2021 WPPConnect Team 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WPPConnect FrontEnd 2 | ---- 3 | 4 | 5 | 6 | 7 | > The repository has been without maintenance for quite some time now, and it's likely not functioning properly. 8 | 9 | 10 | ## Our online channels 11 | 12 | [![Discord](https://img.shields.io/discord/844351092758413353?color=blueviolet&label=Discord&logo=discord&style=flat)](https://discord.gg/JU5JGGKGNG) 13 | [![Telegram Group](https://img.shields.io/badge/Telegram-Group-32AFED?logo=telegram)](https://t.me/wppconnect) 14 | [![WhatsApp Group](https://img.shields.io/badge/WhatsApp-Group-25D366?logo=whatsapp)](https://chat.whatsapp.com/LJaQu6ZyNvnBPNAVRbX00K) 15 | [![YouTube](https://img.shields.io/youtube/channel/subscribers/UCD7J9LG08PmGQrF5IS7Yv9A?label=YouTube)](https://www.youtube.com/c/wppconnect) 16 | 17 | ## Installation 18 | 19 | WPPConnect FrontEnd has been designed to demonstrate how multi-session works dynamically. **The project was developed in ReactJS** 20 | 21 | ## Change port backend 22 | 23 | To change host and port, just access [config.js](/public/config.js). 24 | 25 | And change the value of **window.IP_SERVER** & **window.IP_SOCKET_IO** 26 | 27 | ## Get Started 28 | To run the project is very simple, just follow the steps below. 29 | 30 | Clone the repository and open the unzipped project and run the following command in the terminal: 31 | 32 | 33 | **Installing** 34 | 35 | ```jsx 36 | yarn install 37 | // or 38 | npm install 39 | ``` 40 | 41 | **Running** 42 | 43 | ```jsx 44 | yarn start 45 | //or 46 | npm start 47 | ``` 48 | 49 | ## Features 50 | 51 | - [x] Chat 52 | - [x] Multisession 53 | - [x] Receiving messages (real time) 54 | - [x] Send messages (real time) 55 | - [x] Choose session before sending message 56 | - [x] Receive Images 57 | - [x] Receive Audio 58 | - [x] Receive Sticker 59 | - [x] Send Images 60 | 61 | ## API 62 | 63 | This site uses [this project](https://github.com/wppconnect-team/wppconnect-server/) to populate the data. Then you will need something similar to run the project successfully. 64 | 65 | ## Demo 66 | ![Peek 2021-05-29 00-05](https://user-images.githubusercontent.com/40338524/120056309-aa5b0180-c011-11eb-848b-94569c32a8c6.gif) 67 | -------------------------------------------------------------------------------- /license-checker-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | ".github", 4 | ".husky", 5 | "**/.*", 6 | "**/*.http", 7 | "**/*.lock", 8 | "**/*.md", 9 | "build", 10 | "public", 11 | "src/assets" 12 | ], 13 | "license": "LICENSE.header", 14 | "licenseFormats": { 15 | "js|ts": { 16 | "prepend": "/*", 17 | "append": " */", 18 | "eachLine": { 19 | "prepend": " * " 20 | } 21 | }, 22 | "dotfile": { 23 | "eachLine": { 24 | "prepend": "# " 25 | } 26 | } 27 | }, 28 | "trailingWhitespace": "TRIM" 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@wppconnect/frontend", 3 | "version": "1.0.3", 4 | "description": "Projeto feito para autenticar a automomacao do WhatsappWeb com multi-clientes de forma dinamica. Backend feito em Nodejs(express, socketio), FrontEnd (ReactJS)", 5 | "main": "index.js", 6 | "author": "kingaspx", 7 | "license": "Apache-2.0", 8 | "scripts": { 9 | "build": "cross-env GENERATE_SOURCEMAP=false react-scripts build", 10 | "changelog:last": "conventional-changelog -p angular -r 2", 11 | "changelog:preview": "conventional-changelog -p angular -u", 12 | "changelog:update": "conventional-changelog -p angular -i CHANGELOG.md -s", 13 | "commit": "git-cz", 14 | "eject": "react-scripts eject", 15 | "license:add": "license-check-and-add add", 16 | "license:check": "license-check-and-add check", 17 | "prepare": "husky install", 18 | "release": "release-it", 19 | "start": "react-scripts start", 20 | "test": "react-scripts test" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | }, 34 | "config": { 35 | "commitizen": { 36 | "path": "@commitlint/cz-commitlint" 37 | } 38 | }, 39 | "dependencies": {}, 40 | "devDependencies": { 41 | "@commitlint/cli": "^17.0.0", 42 | "@commitlint/config-angular": "^17.0.0", 43 | "@commitlint/config-conventional": "^17.0.0", 44 | "@commitlint/cz-commitlint": "^17.0.0", 45 | "@emotion/react": "^11.9.0", 46 | "@emotion/styled": "^11.8.1", 47 | "@material-ui/core": "^4.11.3", 48 | "@material-ui/data-grid": "^4.0.0-alpha.24", 49 | "@material-ui/icons": "^4.11.2", 50 | "@material-ui/lab": "^4.0.0-alpha.58", 51 | "@mui/material": "^5.8.3", 52 | "@mui/styled-engine": "^5.8.0", 53 | "@mui/system": "^5.8.3", 54 | "@mui/x-data-grid": "^5.12.0", 55 | "@types/emoji-mart": "^3.0.5", 56 | "@types/react": "^18.0.0", 57 | "@types/react-dom": "^18.0.0", 58 | "@types/react-router-dom": "^5.1.8", 59 | "@types/react-toastify": "^4.1.0", 60 | "@types/styled-components": "^5.1.11", 61 | "axios": "^0.27.0", 62 | "commitizen": "^4.2.4", 63 | "conventional-changelog-cli": "^2.1.1", 64 | "cross-env": "^7.0.3", 65 | "emoji-mart": "^3.0.1", 66 | "emoji-regex": "^10.0.0", 67 | "husky": "^8.0.0", 68 | "license-check-and-add": "^4.0.2", 69 | "lucide-react": "^0.72.0", 70 | "mic-recorder-to-mp3": "^2.2.2", 71 | "prop-types": "^15.7.2", 72 | "react": "^17.0.2", 73 | "react-dom": "^17.0.2", 74 | "react-feather": "^2.0.9", 75 | "react-json-csv": "^2.0.0", 76 | "react-router-dom": "^5.3.0", 77 | "react-scripts": "5.0.1", 78 | "react-toastify": "^9.0.0", 79 | "release-it": "^15.0.0", 80 | "serialize-javascript": "^6.0.0", 81 | "socket.io-client": "^4.0.0", 82 | "styled-components": "^5.2.1", 83 | "sweetalert2": "^11.0.12", 84 | "typescript": "^4.3.5" 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /public/config.js: -------------------------------------------------------------------------------- 1 | window.IP_SERVER = "http://localhost:21465/api/"; 2 | window.IP_SOCKET_IO = "http://localhost:21465/"; 3 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | WPPConnect - Automatize o seu WhatsApp 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "WPPConnect", 3 | "name": "WPPConnect", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#fff", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import {ToastContainer} from "react-toastify"; 17 | import {BrowserRouter} from "react-router-dom"; 18 | import {ThemeProvider} from "styled-components"; 19 | import GlobalStyle, {Container, Layout} from "./style/GlobalStyle"; 20 | import Routes from "./routes"; 21 | import "react-toastify/dist/ReactToastify.css"; 22 | import light from "./style/themes/light"; 23 | // import dark from "./style/themes/dark"; 24 | 25 | function App() { 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | } 41 | 42 | export default App; -------------------------------------------------------------------------------- /src/assets/bg-chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/src/assets/bg-chat.png -------------------------------------------------------------------------------- /src/assets/hand-ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/src/assets/hand-ok.png -------------------------------------------------------------------------------- /src/assets/hand-watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/src/assets/hand-watch.png -------------------------------------------------------------------------------- /src/assets/ic_download_chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/ic_loader_chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | chat (2) 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/assets/notification.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wppconnect-team/wppconnect-frontend/8b6b0fdf4e9317bfa3e4707970a0fcb5c446747e/src/assets/notification.mp3 -------------------------------------------------------------------------------- /src/components/AlertDialog/index.tsx: -------------------------------------------------------------------------------- 1 | import Button from "@material-ui/core/Button"; 2 | import Dialog from "@material-ui/core/Dialog"; 3 | import DialogActions from "@material-ui/core/DialogActions"; 4 | import DialogContent from "@material-ui/core/DialogContent"; 5 | import DialogContentText from "@material-ui/core/DialogContentText"; 6 | import DialogTitle from "@material-ui/core/DialogTitle"; 7 | 8 | type Props = { 9 | title: string; 10 | content: string; 11 | close: () => void; 12 | confirm: () => void; 13 | }; 14 | export function AlertDialog({ 15 | title = "", 16 | content = "", 17 | close, 18 | confirm, 19 | }: Props) { 20 | return ( 21 | 22 | {title} 23 | 24 | {content} 25 | 26 | 27 | 30 | 33 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/components/BackdropComponent/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React from "react"; 17 | import Backdrop from "@material-ui/core/Backdrop"; 18 | import { makeStyles } from "@material-ui/core/styles"; 19 | import HandOk from "../../assets/hand-ok.png"; 20 | import { Image, Layout } from "./style"; 21 | import PropTypes from "prop-types"; 22 | 23 | const useStyles = makeStyles((theme) => ({ 24 | backdrop: { 25 | zIndex: theme.zIndex.drawer + 1, 26 | color: "#fff", 27 | backdropFilter: "blur(15px)" 28 | }, 29 | })); 30 | 31 | export default function BackdropComponent({ open }) { 32 | const classes = useStyles(); 33 | 34 | return ( 35 |
36 | 37 | 38 |

39 | Please wait... 40 |

41 |
42 |
43 |
44 | ); 45 | } 46 | 47 | BackdropComponent.propTypes = { 48 | open: PropTypes.bool.isRequired, 49 | }; -------------------------------------------------------------------------------- /src/components/BackdropComponent/style.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import styled from "styled-components"; 17 | 18 | export const Layout = styled.div` 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | align-items: center; 23 | 24 | max-width: 500px; 25 | 26 | h1 { 27 | text-align: center; 28 | font-weight: 400; 29 | margin-top: 1em; 30 | } 31 | `; 32 | 33 | export const Image = styled.img` 34 | margin-bottom: 3em; 35 | width: 200px; 36 | 37 | animation: heartbeat 1.5s ease-in-out infinite both; 38 | 39 | @keyframes heartbeat { 40 | from { 41 | transform: scale(1); 42 | transform-origin: center center; 43 | animation-timing-function: ease-out; 44 | } 45 | 10% { 46 | transform: scale(0.91); 47 | animation-timing-function: ease-in; 48 | } 49 | 17% { 50 | transform: scale(0.98); 51 | animation-timing-function: ease-out; 52 | } 53 | 33% { 54 | transform: scale(0.87); 55 | animation-timing-function: ease-in; 56 | } 57 | 45% { 58 | transform: scale(1); 59 | animation-timing-function: ease-out; 60 | } 61 | } 62 | `; -------------------------------------------------------------------------------- /src/components/ChangeSession/Dialog/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import {makeStyles} from "@material-ui/core/styles"; 17 | import {blue} from "@material-ui/core/colors"; 18 | import Dialog from "@material-ui/core/Dialog"; 19 | import DialogTitle from "@material-ui/core/DialogTitle"; 20 | import List from "@material-ui/core/List"; 21 | import ListItem from "@material-ui/core/ListItem"; 22 | import ListItemAvatar from "@material-ui/core/ListItemAvatar"; 23 | import Avatar from "@material-ui/core/Avatar"; 24 | import ListItemText from "@material-ui/core/ListItemText"; 25 | import React from "react"; 26 | import {Plus, User} from "react-feather"; 27 | import {Layout} from "./style"; 28 | import history from "../../../history"; 29 | import {login} from "../../../services/auth"; 30 | import PropTypes from "prop-types"; 31 | 32 | const useStyles = makeStyles({ 33 | avatar: { 34 | backgroundColor: blue[100], 35 | color: blue[600], 36 | }, 37 | }); 38 | 39 | function AllSessionsDialog({onClose, selectedValue, open, sessions}) { 40 | const classes = useStyles(); 41 | 42 | const handleClose = () => { 43 | onClose(selectedValue); 44 | }; 45 | 46 | const handleListItemClick = (value) => { 47 | onClose(value.session); 48 | login(JSON.stringify({session: value.session, token: value.token})); 49 | window.location.reload(); 50 | }; 51 | 52 | return ( 53 | 54 | 55 | Escolha uma sessão 56 | 57 | {sessions.map((sessao, index) => ( 58 | handleListItemClick(sessao)} key={index}> 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | ))} 68 | 69 | history.push("nova-sessao")}> 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | ); 81 | } 82 | 83 | AllSessionsDialog.propTypes = { 84 | onClose: PropTypes.func.isRequired, 85 | selectedValue: PropTypes.string.isRequired, 86 | open: PropTypes.bool.isRequired, 87 | sessions: PropTypes.array.isRequired 88 | }; 89 | 90 | export default AllSessionsDialog; -------------------------------------------------------------------------------- /src/components/ChangeSession/Dialog/style.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import styled from "styled-components"; 17 | 18 | export const Layout = styled.div` 19 | span, h1, h2 { 20 | font-size: 1rem; 21 | } 22 | `; -------------------------------------------------------------------------------- /src/components/ChangeSession/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, {useEffect, useState} from "react"; 17 | import AllSessionsDialog from "./Dialog"; 18 | import api from "../../services/api"; 19 | import {getSession} from "../../services/auth"; 20 | import config from "../../util/sessionHeader"; 21 | import PropTypes from "prop-types"; 22 | 23 | function ChangeSessionDialog({open, handleClose, selectedValue}) { 24 | const [sessions, setSessions] = useState([]); 25 | 26 | useEffect(() => { 27 | async function getAllSessions() { 28 | const {data: {response}} = await api.get(`${getSession()}/show-all-sessions`, config()); 29 | setSessions(response); 30 | } 31 | 32 | getAllSessions(); 33 | return () => { 34 | setSessions([]); 35 | }; 36 | }, []); 37 | 38 | return ( 39 |
40 | 46 |
47 | ); 48 | } 49 | 50 | ChangeSessionDialog.propTypes = { 51 | open: PropTypes.bool.isRequired, 52 | handleClose: PropTypes.func.isRequired, 53 | selectedValue: PropTypes.string.isRequired, 54 | }; 55 | 56 | export default ChangeSessionDialog; -------------------------------------------------------------------------------- /src/components/ChatPage/AudioComponent/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, {useEffect, useRef, useState} from "react"; 17 | import {AudioPlayer, Layout, PlayButton, Player, Timeline, User} from "./style"; 18 | import PropTypes from "prop-types"; 19 | 20 | const AudioComponent = ({url, isMe, profileImage, downloadAudio}) => { 21 | const audioPlayer = useRef(null); 22 | const sliderRef = useRef(null); 23 | const [audio, setAudio] = useState(null); 24 | 25 | useEffect(() => { 26 | setAudio(new Audio(audioPlayer.current.dataset.url)); 27 | }, [url]); 28 | 29 | const onClickPlay = () => { 30 | audio.paused ? audio.play() : audio.pause(); 31 | }; 32 | 33 | async function checkIsDownloaded() { 34 | if (audioPlayer.current.dataset.url === undefined) { 35 | downloadAudio("audio").then(() => { 36 | start(); 37 | }); 38 | } else { 39 | start(); 40 | } 41 | } 42 | 43 | function start() { 44 | onClickPlay(); 45 | audio.onloadstart = () => { 46 | setMessageDate(); 47 | audioPlayer.current.classList.add("loading"); 48 | }; 49 | 50 | audio.onplay = () => audioPlayer.current.classList.add("playing"); 51 | audio.onpause = () => audioPlayer.current.classList.remove("playing"); 52 | audio.onloadeddata = () => audioPlayer.current.classList.remove("loading"); 53 | audio.ondurationchange = showTimeDuration; 54 | audio.onended = () => (audio.currentTime = 0); 55 | audio.ontimeupdate = () => { 56 | const {currentTime} = audio; 57 | const currentTimeDisplay = formatTimeToDisplay(currentTime); 58 | updateCurrentTimeDisplay(currentTimeDisplay); 59 | updateCurrentPercent(); 60 | if (currentTime === 0) { 61 | showTimeDuration(); 62 | } 63 | }; 64 | } 65 | 66 | function formatTimeToDisplay(seconds) { 67 | const milliseconds = seconds * 1000; 68 | return new Date(milliseconds).toISOString().substr(14, 5); 69 | } 70 | 71 | function setMessageDate() { 72 | let currentDateTime = new Date().toISOString().substr(11, 5); 73 | audioPlayer.current.style.setProperty( 74 | "--player-current-date-time", 75 | `'${currentDateTime}'` 76 | ); 77 | } 78 | 79 | function handleSlider(e) { 80 | const {duration} = audio; 81 | const percent = e.target.value; 82 | const currentTimeInSeconds = ((percent * duration) / 100).toFixed(2); 83 | audio.currentTime = currentTimeInSeconds; 84 | } 85 | 86 | function showTimeDuration() { 87 | const {duration} = audio; 88 | const durationDisplay = formatTimeToDisplay(duration); 89 | updateCurrentTimeDisplay(durationDisplay); 90 | } 91 | 92 | function updateCurrentTimeDisplay(time) { 93 | audioPlayer.current.style.setProperty("--player-current-time", `'${time}'`); 94 | } 95 | 96 | function updateCurrentPercent() { 97 | const {currentTime, duration} = audio; 98 | const percentPlayed = (currentTime * 100) / duration; 99 | sliderRef.current.value = percentPlayed; 100 | audioPlayer.current.style.setProperty( 101 | "--player-percent-played", 102 | `${percentPlayed}%` 103 | ); 104 | } 105 | 106 | return ( 107 | 108 | 109 | 110 | checkIsDownloaded()}> 111 | play_arrow 112 | pause 113 | loop 114 | 115 | 116 | 117 |
118 | 120 |
121 |
122 |
123 |
124 |
125 |
126 | 127 | 128 | 129 | {"ProfileImg"}/ 130 | mic 131 | 132 | 133 | 134 | ); 135 | }; 136 | 137 | export default AudioComponent; 138 | 139 | AudioComponent.propTypes = { 140 | url: PropTypes.string, 141 | isMe: PropTypes.bool.isRequired, 142 | profileImage: PropTypes.string.isRequired, 143 | downloadAudio: PropTypes.func.isRequired, 144 | }; -------------------------------------------------------------------------------- /src/components/ChatPage/AudioComponent/style.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import styled from "styled-components"; 17 | 18 | export const Layout = styled.div` 19 | width: 100%; 20 | 21 | :root { 22 | --bg-color: #1b1b22; 23 | --green: #00e5c0; 24 | --body-font-family: "Jost", sans-serif; 25 | --player-color-featured: #00e5c0; 26 | --player-color-background: #262d31; 27 | --player-color-text: #c5c6c8; 28 | --player-percent-played: 0; 29 | --player-current-time: "00:00"; 30 | --player-current-date-time: "00:00"; 31 | } 32 | 33 | @keyframes load { 34 | to { 35 | transform: rotate(360deg); 36 | } 37 | } 38 | 39 | .audio-player .player .btn-play span:not(.icon-play), 40 | .audio-player.playing .player .btn-play span:not(.icon-pause), 41 | .audio-player.loading .player .btn-play span:not(.icon-loop) { 42 | display: none; 43 | } 44 | 45 | .audio-player.playing .player .btn-play .icon-pause { 46 | display: inline-block; 47 | } 48 | `; 49 | 50 | export const AudioPlayer = styled.div` 51 | background: var(--player-color-background); 52 | display: inline-flex; 53 | flex-direction: ${({isMe}) => isMe ? "row" : "row-reverse"}; 54 | min-width: 240px; 55 | width: 336px; 56 | max-width: 100%; 57 | border-radius: 0.4rem; 58 | //padding: 0.4rem; 59 | //box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 60 | user-select: none; 61 | 62 | .loading { 63 | .player { 64 | .btn-play { 65 | span { 66 | &:not(.icon-loop) { 67 | display: none; 68 | } 69 | 70 | animation: load 1s linear infinite; 71 | } 72 | 73 | pointer-events: none; 74 | 75 | .icon-loop { 76 | display: inline-block; 77 | } 78 | } 79 | } 80 | } 81 | 82 | .mine { 83 | --player-color-background: #056162; 84 | 85 | .user { 86 | margin-left: 0; 87 | 88 | span { 89 | right: 0; 90 | left: auto; 91 | transform: translateX(50%); 92 | } 93 | } 94 | 95 | .player { 96 | margin-right: 0.8rem; 97 | 98 | .btn-play { 99 | padding: 0 0.8rem; 100 | } 101 | } 102 | } 103 | 104 | + { 105 | .audio-player { 106 | margin-top: 1rem; 107 | } 108 | } 109 | `; 110 | 111 | export const Player = styled.div` 112 | flex: 1; 113 | display: flex; 114 | `; 115 | 116 | export const PlayButton = styled.button` 117 | outline: none; 118 | appearance: none; 119 | cursor: pointer; 120 | background: none; 121 | border: 0; 122 | padding: 0 0.8rem 0 0.4rem; 123 | 124 | &:disabled { 125 | cursor: default; 126 | } 127 | 128 | span { 129 | color: var(--player-color-text); 130 | font-size: 38px; 131 | opacity: 0.8; 132 | 133 | &:not(.icon-play) { 134 | display: none; 135 | } 136 | } 137 | `; 138 | 139 | export const Timeline = styled.div` 140 | flex: 1; 141 | display: flex; 142 | flex-direction: column; 143 | position: relative; 144 | padding-bottom: 0.2rem; 145 | 146 | .line { 147 | --line-height: 0.24rem; 148 | flex: 1; 149 | display: flex; 150 | align-items: center; 151 | position: relative; 152 | 153 | &:before { 154 | content: ""; 155 | width: var(--player-percent-played); 156 | position: absolute; 157 | background: var(--player-color-featured); 158 | height: var(--line-height); 159 | border-radius: calc(var(--line-height) / 2); 160 | } 161 | 162 | input[type="range"] { 163 | flex: 1; 164 | all: unset; 165 | appearance: none; 166 | background: rgba(0, 0, 0, .3); 167 | border: none; 168 | outline: none; 169 | width: 100%; 170 | position: relative; 171 | 172 | &::-webkit-slider-thumb { 173 | appearance: none; 174 | background: #999; 175 | width: 0.9rem; 176 | height: 0.9rem; 177 | border-radius: 50%; 178 | margin-top: calc(var(--line-height) * -1.4); 179 | } 180 | 181 | &::-moz-range-thumb { 182 | unset: all; 183 | appearance: none; 184 | border: 0; 185 | background: #286cc1; 186 | width: 0.9rem; 187 | height: 0.9rem; 188 | border-radius: 50%; 189 | margin-top: calc(var(--line-height) * -1.4); 190 | } 191 | 192 | &::-ms-thumb { 193 | appearance: none; 194 | background: var(--player-color-featured); 195 | width: 0.9rem; 196 | height: 0.9rem; 197 | border-radius: 50%; 198 | margin-top: calc(var(--line-height) * -1.4); 199 | } 200 | 201 | &::-webkit-slider-runnable-track { 202 | background: rgba(255, 255, 255, 0.2); 203 | height: var(--line-height); 204 | border-radius: calc(var(--line-height) / 2); 205 | } 206 | 207 | &::-moz-range-track { 208 | background: rgb(182, 205, 179); 209 | height: var(--line-height); 210 | border-radius: calc(var(--line-height) / 2); 211 | } 212 | 213 | &::-ms-track { 214 | background: rgba(255, 255, 255, 0.2); 215 | height: var(--line-height); 216 | border-radius: calc(var(--line-height) / 2); 217 | } 218 | } 219 | } 220 | 221 | .data { 222 | display: flex; 223 | align-items: center; 224 | justify-content: space-between; 225 | font-size: 0.68rem; 226 | color: var(--player-color-text); 227 | position: absolute; 228 | width: 100%; 229 | bottom: 0; 230 | 231 | .current-time { 232 | font-size: 1rem; 233 | 234 | &::before { 235 | content: var(--player-current-time); 236 | } 237 | } 238 | 239 | .time { 240 | display: flex; 241 | align-items: center; 242 | 243 | &::before { 244 | content: var(--player-current-date-time); 245 | } 246 | 247 | span { 248 | font-size: 1rem; 249 | margin-left: 0.4rem; 250 | color: var(--player-color-featured); 251 | } 252 | } 253 | } 254 | `; 255 | 256 | export const User = styled.div` 257 | position: relative; 258 | width: 55px; 259 | height: 55px; 260 | margin-left: ${({isMe}) => isMe ? "1.4rem;" : "10px"}; 261 | 262 | img { 263 | width: 55px; 264 | height: 55px; 265 | border-radius: 50%; 266 | object-fit: cover; 267 | background: rgba(255, 255, 255, 0.01); 268 | } 269 | 270 | span { 271 | position: absolute; 272 | left: 10px; 273 | bottom: 0; 274 | color: var(--player-color-featured); 275 | transform: translateX(-50%); 276 | font-size: 2.5rem; 277 | text-shadow: -1px -1px 0 var(--player-color-background), 1px -1px 0 var(--player-color-background), -1px 1px 0 var(--player-color-background), 1px 1px 0 var(--player-color-background); 278 | } 279 | `; -------------------------------------------------------------------------------- /src/components/ChatPage/ChatComponent/ImageModal/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, {useEffect, useState} from "react"; 17 | import {makeStyles} from "@material-ui/core/styles"; 18 | import Modal from "@material-ui/core/Modal"; 19 | import {ImageComponent, Layout, TopButtons} from "./style"; 20 | import {Download, X} from "react-feather"; 21 | import PropTypes from "prop-types"; 22 | 23 | const useStyles = makeStyles((theme) => ({ 24 | modal: { 25 | display: "flex", 26 | alignItems: "center", 27 | justifyContent: "center", 28 | }, 29 | paper: { 30 | backgroundColor: "#000", 31 | border: 0, 32 | outline: 0, 33 | boxShadow: theme.shadows[1], 34 | padding: theme.spacing(2, 4, 3), 35 | }, 36 | })); 37 | 38 | export default function ImageModal({open, handleClose, message, image}) { 39 | const classes = useStyles(); 40 | const [name, setName] = useState(""); 41 | const [profileImage, setProfileImage] = useState(""); 42 | 43 | useEffect(() => { 44 | if (open) { 45 | setProfileImage(message?.sender?.profilePicThumbObj?.eurl); 46 | if (message.fromMe) { 47 | setName(message.sender.formattedName); 48 | } else { 49 | setName(message.sender.pushname); 50 | } 51 | } 52 | }, [open]); 53 | 54 | return ( 55 |
56 | 63 |
64 | 65 | 66 |
67 | e.target.src = "https://pbs.twimg.com/profile_images/1259926100261601280/OgmLtUZJ_400x400.png"} 71 | alt={name} 72 | /> 73 | 74 |

75 | {name} 76 |

77 |
78 | 79 |
80 | 86 | 87 | 88 | 89 | 90 |
91 |
92 | 93 | 94 | {message}/ 95 | 96 |
97 |
98 |
99 |
100 | ); 101 | } 102 | 103 | ImageModal.propTypes = { 104 | message: PropTypes.any.isRequired, 105 | open: PropTypes.bool.isRequired, 106 | handleClose: PropTypes.func.isRequired, 107 | image: PropTypes.string.isRequired, 108 | }; -------------------------------------------------------------------------------- /src/components/ChatPage/ChatComponent/ImageModal/style.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import styled from "styled-components"; 17 | 18 | export const Layout = styled.div` 19 | width: 100vw; 20 | height: 100vh; 21 | background-color: black; 22 | 23 | display: flex; 24 | flex-direction: column; 25 | //justify-content: space-between; 26 | `; 27 | 28 | export const TopButtons = styled.header` 29 | border-bottom: 1px solid rgba(255, 255, 255, .5); 30 | padding: 2em; 31 | height: 68px; 32 | width: 100%; 33 | 34 | display: flex; 35 | align-items: center; 36 | justify-content: space-between; 37 | 38 | svg { 39 | color: #999; 40 | margin-right: 2em; 41 | cursor: pointer; 42 | transition-duration: 200ms; 43 | 44 | :hover { 45 | color: #fff; 46 | } 47 | } 48 | 49 | .info-user { 50 | display: flex; 51 | align-items: center; 52 | 53 | p { 54 | color: #fff; 55 | font-size: 1.7rem; 56 | font-weight: 600; 57 | cursor: default; 58 | } 59 | 60 | img { 61 | width: 40px; 62 | height: 40px; 63 | border-radius: 50%; 64 | object-fit: cover; 65 | margin-right: 15px; 66 | } 67 | } 68 | `; 69 | 70 | export const ImageComponent = styled.div` 71 | width: 80%; 72 | height: 80%; 73 | display: flex; 74 | justify-content: center; 75 | align-items: center; 76 | margin: auto; 77 | 78 | img { 79 | width: 100%; 80 | height: 100%; 81 | object-fit: contain; 82 | } 83 | `; 84 | -------------------------------------------------------------------------------- /src/components/ChatPage/ChatComponent/QuotedMessage/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | const Container = styled.div` 4 | border: solid 1px rgba(0, 0, 0, 0.1); 5 | padding: 4px; 6 | border-radius: 3px; 7 | border-left: solid 3px var(--blue); 8 | max-width: 500px; 9 | width: 100%; 10 | background-color: rgba(0, 0, 0, 0.1); 11 | cursor: pointer; 12 | margin: 5px 0; 13 | `; 14 | 15 | const Content = styled.div` 16 | display: flex; 17 | flex-direction: row; 18 | flex-wrap: wrap; 19 | `; 20 | 21 | const Message = styled.span` 22 | width: 100%; 23 | flex: 1; 24 | `; 25 | 26 | const Img = styled.img` 27 | max-height: 200px; 28 | object-fit: contain; 29 | width: auto; 30 | `; 31 | 32 | export function QuotedMessage({ message }) { 33 | function scrollToMessage() { 34 | const element = document.getElementById(message.quotedMsgId); 35 | if (element) { 36 | const originalBg = element.style.background; 37 | const originalPadding = element.style.padding; 38 | element.style.background = "#ffffff33"; 39 | element.style.padding = "10px 5px"; 40 | element.scrollIntoView({ behavior: "smooth" }); 41 | setTimeout(() => { 42 | element.style.background = originalBg; 43 | element.style.padding = originalPadding; 44 | }, 2000); 45 | } 46 | } 47 | 48 | function getTitle() { 49 | return (message?.quotedParticipant || "").replace(/[@c.us, @g.us]/g, ""); 50 | } 51 | 52 | return ( 53 | 54 | 55 | 56 | {getTitle()} 57 | 58 | {message?.quotedMsg?.type === "chat" ? ( 59 | 60 | {message?.quotedMsg?.caption || message?.quotedMsg?.body} 61 | 62 | ) : ( 63 | <> 64 | {message?.quotedMsg?.body && ( 65 | 66 | )} 67 | 68 | )} 69 | 70 | 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /src/components/ChatPage/ChatComponent/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 WPPConnect Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import React, { useEffect, useRef, useState } from "react"; 17 | import MenuItem from "@material-ui/core/MenuItem"; 18 | import IconButton from "@material-ui/core/IconButton"; 19 | import Menu from "@material-ui/core/Menu"; 20 | import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; 21 | import { 22 | ChatLayout, 23 | DocumentComponent, 24 | ImageContainer, 25 | MessageContainer, 26 | MessageContent, 27 | MessageContentText, 28 | StickerComponent, 29 | } from "./style"; 30 | import AudioComponent from "../AudioComponent"; 31 | import api from "../../../services/api"; 32 | import ImageModal from "./ImageModal"; 33 | import PropTypes from "prop-types"; 34 | import config from "../../../util/sessionHeader"; 35 | import formatWppMarkdown from "../../../util/functionsMarkdown"; 36 | import { QuotedMessage } from "./QuotedMessage"; 37 | import { Download } from "react-feather"; 38 | import { AlertDialog } from "../../AlertDialog"; 39 | import { toast } from "react-toastify"; 40 | 41 | const defaultImage = 42 | "https://pbs.twimg.com/profile_images/1259926100261601280/OgmLtUZJ_400x400.png"; 43 | 44 | const ChatComponent = ({ 45 | message, 46 | session, 47 | isMe, 48 | isWarning, 49 | selectMessageId, 50 | }) => { 51 | const imageRef = useRef(null); 52 | const audioRef = useRef(null); 53 | const [audioUrl, setAudioUrl] = useState(undefined); 54 | const [display, setDisplay] = useState("block"); 55 | const [openModalImage, setOpenModalImage] = useState(false); 56 | const [clickedUrl, setClickedUrl] = useState(""); 57 | const textRef = useRef(null); 58 | const [anchorEl, setAnchorEl] = useState(null); 59 | const open = Boolean(anchorEl); 60 | const [openDeleteDialog, setOpenDeleteDialog] = useState(false); 61 | 62 | const handleClick = (event) => { 63 | setAnchorEl(event.currentTarget); 64 | }; 65 | 66 | const handleClose = () => { 67 | setAnchorEl(null); 68 | }; 69 | 70 | useEffect(() => { 71 | formatWppMarkdown(textRef); 72 | }, [textRef]); 73 | 74 | const onClickDownload = async (type, option) => { 75 | const response = await api.get( 76 | `${session}/get-media-by-message/${message.id}`, 77 | config() 78 | ); 79 | 80 | if (type === "image") { 81 | imageRef.current.src = `data:image/png;base64, ${response.data}`; 82 | setDisplay("none"); 83 | } else if (type === "video") { 84 | imageRef.current.src = `data:video/webm;base64, ${response.data}`; 85 | setDisplay("none"); 86 | } else if (type === "audio") { 87 | setAudioUrl(`data:audio/ogg;base64, ${response.data}`); 88 | } else if (type === "document") { 89 | const a = document.createElement("a"); 90 | a.href = `data:${option.mimetype};base64, ${response.data}`; 91 | a.download = `${option.filename}`; 92 | a.click(); 93 | } 94 | }; 95 | 96 | const handleOpenModalImage = () => { 97 | setClickedUrl(imageRef.current.src); 98 | setOpenModalImage(true); 99 | }; 100 | 101 | const handleCloseModalImage = () => { 102 | setOpenModalImage(false); 103 | }; 104 | 105 | function getMessageTime(m) { 106 | const date = new Date(m.timestamp * 1000); 107 | const lang = navigator.language || navigator.languages[0]; 108 | const date_locale = date.toLocaleDateString(lang, { 109 | day: "numeric", 110 | month: "short", 111 | year: "numeric", 112 | }); 113 | const time_locale = date.toLocaleTimeString(lang); 114 | const formatted = `${date_locale} ${time_locale}`; 115 | return ( 116 |
117 | 118 | {formatted} 119 | 120 |
121 | ); 122 | } 123 | 124 | function getSender(m) { 125 | let sender = m?.sender?.id?.user; 126 | if (m.sender) { 127 | const key = [ 128 | "name", 129 | "shortName", 130 | "pushname", 131 | "verifiedName", 132 | "formattedName", 133 | ].find((n) => { 134 | if (n in m.sender && !!String(m.sender[n]).trim()) { 135 | return m.sender[n]; 136 | } 137 | }); 138 | sender = m.sender[key]; 139 | } 140 | return sender; 141 | } 142 | 143 | function getReason(m) { 144 | try { 145 | const sender = getSender(m); 146 | if (m.type === "revoked") return `${sender} apagou mensagem`; 147 | if (m.type === "gp2") { 148 | let users = []; 149 | if (m.recipients && Array.isArray(m.recipients)) { 150 | users = m.recipients.reduce( 151 | (c, acc, i) => c + (i > 0 ? ", " : "") + acc.user, 152 | "" 153 | ); 154 | } 155 | if (m.subtype === "leave") return `${m?.recipients[0]?.user} saiu`; 156 | if (m.subtype === "remove") return `${sender} removeu \n${users}`; 157 | if (m.subtype === "add") return `${sender} adicionou \n${users}`; 158 | } 159 | } catch (error) { 160 | return ""; 161 | } 162 | } 163 | 164 | function getBodyMessage() { 165 | if (!!message?.body && message.type === "chat") return message.body; 166 | if ( 167 | !!message?.body && 168 | message.type === "gp2" && 169 | message.subtype === "picture" 170 | ) { 171 | const sender = getSender(message); 172 | return `${sender} Alterou a imagem do grupo`; 173 | } 174 | 175 | if (!message?.body) return getReason(message); 176 | } 177 | 178 | const options = [ 179 | { 180 | label: "Responder", 181 | method() { 182 | setAnchorEl(null); 183 | selectMessageId(); 184 | }, 185 | }, 186 | { 187 | label: "Apagar", 188 | method() { 189 | setAnchorEl(null); 190 | setOpenDeleteDialog(true); 191 | }, 192 | }, 193 | ]; 194 | 195 | return ( 196 | 197 | 203 | 204 | 205 |
206 | 212 | 213 | 214 | 226 | {options.map((option) => ( 227 | option.method()}> 228 | {option.label} 229 | 230 | ))} 231 | 232 |
233 | 234 | {!message.fromMe && ( 235 | 236 | 237 | {message.sender?.name || message.sender?.id?.user} -{" "} 238 | {message?.sender?.pushname} 239 | 240 | 241 | )} 242 | 243 | {message.isMedia ? ( 244 | message.type === "video" ? ( 245 | 246 |