├── .env.development ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── workflows │ ├── addToProject.yml │ └── build-deploy.yml ├── .gitignore ├── .prettierrc.json ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── docs ├── 220218_PrototypeFundDemoWeek_Blogpost.md ├── ABOUT.md ├── BMBF-Logo.svg ├── BMBF_Logo_new.jpg ├── BMBF_sponsored_by_en.jpg ├── CODE_OF_CONDUCT.md ├── CONTRIBUTION.md ├── Chatmosphere.png ├── Chatmosphere_PrivateConversation.gif ├── INSTALL.md ├── OKFD-Logo.svg ├── OpenDesignChatmosphere.png ├── PF-Logo.svg ├── chatmosphere.gif ├── github-screenshot.png └── playwave_chatmosphere.png ├── package-lock.json ├── package.json ├── public ├── .htaccess ├── favicon.ico ├── index.html ├── libs │ ├── jquery-2.1.1.js │ ├── jquery-2.1.1.min.js │ ├── jquery-2.1.1.min.map │ ├── jquery-3.5.1.min.js │ ├── lib-jitsi-meet.min-135eed5.js │ ├── lib-jitsi-meet.min.js │ ├── lib-jitsi-meet.min.map │ ├── strophe │ │ ├── strophe.disco.min.js │ │ ├── strophe.js │ │ └── strophe.min.js │ └── umd │ │ ├── lib-jitsi-meet.e2ee-worker.js │ │ ├── lib-jitsi-meet.min.js │ │ ├── lib-jitsi-meet.min.js.LICENSE.txt │ │ └── lib-jitsi-meet.min.map ├── manifest.json └── robots.txt ├── src ├── App.test.tsx ├── App.tsx ├── addons │ ├── Chat │ │ └── Chat.tsx │ ├── Screenshare │ │ ├── ScreenshareButton │ │ │ └── ScreenshareButton.tsx │ │ └── components │ │ │ └── LocalDesktop.tsx │ ├── Stage │ │ ├── Stage.tsx │ │ └── components │ │ │ ├── ResizeControl.tsx │ │ │ ├── StageAudio.tsx │ │ │ ├── StageButton.tsx │ │ │ ├── StageUser.tsx │ │ │ ├── StageUsers.tsx │ │ │ └── StageVideo.tsx │ └── addonsStore.ts ├── assets │ ├── call.svg │ ├── fonts │ │ ├── inter-v7 │ │ │ ├── Inter-Regular.woff │ │ │ ├── Inter-Regular.woff2 │ │ │ ├── LICENSE.txt │ │ │ └── inter-v7-latin-regular.ttf │ │ ├── roboto-v20-latin │ │ │ ├── roboto-v20-latin-500.eot │ │ │ ├── roboto-v20-latin-500.svg │ │ │ ├── roboto-v20-latin-500.ttf │ │ │ ├── roboto-v20-latin-500.woff │ │ │ ├── roboto-v20-latin-500.woff2 │ │ │ ├── roboto-v20-latin-700.eot │ │ │ ├── roboto-v20-latin-700.svg │ │ │ ├── roboto-v20-latin-700.ttf │ │ │ ├── roboto-v20-latin-700.woff │ │ │ ├── roboto-v20-latin-700.woff2 │ │ │ ├── roboto-v20-latin-regular.eot │ │ │ ├── roboto-v20-latin-regular.svg │ │ │ ├── roboto-v20-latin-regular.ttf │ │ │ ├── roboto-v20-latin-regular.woff │ │ │ └── roboto-v20-latin-regular.woff2 │ │ └── space-grotesk-v2 │ │ │ ├── AUTHORS.txt │ │ │ ├── CONTRIBUTORS.txt │ │ │ ├── OFL.txt │ │ │ ├── SpaceGrotesk-Bold.ttf │ │ │ ├── SpaceGrotesk-Bold.woff │ │ │ ├── SpaceGrotesk-Bold.woff2 │ │ │ ├── SpaceGrotesk-Medium.ttf │ │ │ ├── SpaceGrotesk-Medium.woff │ │ │ ├── SpaceGrotesk-Medium.woff2 │ │ │ ├── SpaceGrotesk-Regular.ttf │ │ │ ├── SpaceGrotesk-Regular.woff │ │ │ └── SpaceGrotesk-Regular.woff2 │ ├── icons │ │ ├── ArrowUp.js │ │ ├── Camera.js │ │ ├── CameraOff.js │ │ ├── ChatFilledIcon.js │ │ ├── ChatIcon.js │ │ ├── MicIcon.js │ │ ├── MicOff.js │ │ ├── MoreVertical.js │ │ ├── ScreenShare.js │ │ ├── SettingsIcon.js │ │ ├── StageIcon.js │ │ ├── StageIcon.svg │ │ ├── UserFilled.js │ │ └── UserIcon.js │ ├── kissingCat.svg │ ├── loveCat.svg │ ├── muteCatBig.svg │ ├── muteCatCircle.svg │ ├── muteCatSmall.svg │ ├── reloadIcon.svg │ ├── twitter.svg │ └── wave.svg ├── components │ ├── DragWrapper │ │ └── DragWrapper.tsx │ ├── Footer │ │ ├── CallControlBox.tsx │ │ ├── Footer.tsx │ │ ├── JoinButton │ │ │ └── JoinButton.tsx │ │ ├── MoreTab │ │ │ └── MoreTab.tsx │ │ ├── MuteButton │ │ │ └── MuteButton.tsx │ │ ├── Settings │ │ │ ├── Settings.tsx │ │ │ └── SettingsStore.tsx │ │ ├── SocialIcons.tsx │ │ └── VideoButton │ │ │ └── VideoButton.tsx │ ├── Header │ │ └── Header.tsx │ ├── JitsiConnection │ │ ├── JitsiConnection.js │ │ └── jitsiOptions.tsx │ ├── NameTag │ │ └── NameTag.tsx │ ├── PanWrapper │ │ ├── PanWrapper.tsx │ │ └── panOptions.ts │ ├── Room │ │ └── Room.tsx │ ├── User │ │ ├── Localuser │ │ │ ├── LocalUserContainer.tsx │ │ │ ├── Localuser.test.js │ │ │ ├── Localuser.tsx │ │ │ └── components │ │ │ │ ├── AudioRadius.tsx │ │ │ │ ├── LocalAudio.tsx │ │ │ │ ├── LocalVideo.tsx │ │ │ │ ├── MuteIndicator.tsx │ │ │ │ └── NameContainer.tsx │ │ ├── RemoteUser │ │ │ ├── AudioTrack.tsx │ │ │ ├── ConnectedUser.js │ │ │ ├── DesktopVideo.tsx │ │ │ ├── MuteIndicator.tsx │ │ │ ├── Name.tsx │ │ │ ├── Users.tsx │ │ │ └── VideoTrack.tsx │ │ └── components │ │ │ └── Backdrop │ │ │ └── UserBackdrop.tsx │ └── common │ │ ├── BigHeadline │ │ └── index.tsx │ │ ├── Buttons │ │ ├── Button.tsx │ │ └── IconLink.tsx │ │ ├── Card │ │ └── Card.tsx │ │ ├── Info │ │ ├── ErrorHandler.tsx │ │ ├── Info.tsx │ │ └── InfoStore.ts │ │ ├── Input │ │ └── InputField.tsx │ │ ├── Menu │ │ └── Menu.tsx │ │ └── SubHeadline │ │ └── index.tsx ├── global.d.ts ├── index.tsx ├── pages │ ├── Enter │ │ └── Enter.tsx │ ├── Home │ │ ├── Home.tsx │ │ └── elements │ │ │ ├── NameInputContainer.tsx │ │ │ └── NameInputForm.tsx │ └── Session │ │ └── Session.tsx ├── react-app-env.d.ts ├── reportWebVitals.ts ├── serverConfig-example.ts ├── serverConfig.ts ├── setupTests.ts ├── store │ ├── ConferenceStore.tsx │ ├── ConnectionStore.tsx │ ├── LocalStore.test.tsx │ ├── LocalStore.tsx │ ├── LocalStoreLogic.tsx │ ├── createConnectionSlice.ts │ └── index.d.ts ├── stories │ ├── Button.tsx │ └── button.css ├── styled.d.ts ├── theme.d.ts ├── theme │ ├── GlobalStyles │ │ ├── GlobalStyles.tsx │ │ └── fonts.ts │ └── theme.tsx └── utils │ ├── LookupTable.ts │ ├── Portal.tsx │ ├── TypeDefinitions.ts │ ├── VectorHelpers.ts │ ├── hooks │ └── useDrag.ts │ ├── secureConferenceName.spec.ts │ └── secureConferenceName.ts └── tsconfig.json /.env.development: -------------------------------------------------------------------------------- 1 | # REACT_APP_SERVICE_URL="your_jitsi_server_url" 2 | # REACT_APP_USE_WEBSOCKET="true" 3 | 4 | HTTPS=true 5 | # PORT=8080 6 | SSL_CRT_FILE=./.cert/cert.pem 7 | SSL_KEY_FILE=./.cert/key.pem -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: chatmosphere 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/addToProject.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign to Project(s) 2 | 3 | on: 4 | issues: 5 | types: [opened, labeled] 6 | pull_request: 7 | types: [opened, labeled] 8 | issue_comment: 9 | types: [created] 10 | env: 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | 13 | jobs: 14 | assign_one_project: 15 | runs-on: ubuntu-latest 16 | name: Assign to One Project 17 | steps: 18 | - name: Assign NEW issues and NEW pull requests to Chatmosphere App Board 19 | uses: srggrs/assign-one-project-github-action@1.2.1 20 | if: github.event.action == 'opened' 21 | with: 22 | project: 'https://github.com/Chatmosphere/chatmosphere-app/projects/1' 23 | column_name: 'Backlog' 24 | -------------------------------------------------------------------------------- /.github/workflows/build-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Manual Chatmosphere Deploy 2 | on: 3 | # Manual Trigger for Stage Server 4 | workflow_dispatch: 5 | inputs: 6 | environment: 7 | type: environment 8 | description: Select the environment 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | build: 13 | # The type of runner that the job will run on 14 | runs-on: ubuntu-latest 15 | environment: 16 | name: ${{ github.event.inputs.environment }} 17 | 18 | strategy: 19 | matrix: 20 | node-version: [16.x] 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 25 | - uses: actions/checkout@v2 26 | - name: Use Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v2 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | - name: npm ci, build and test 31 | run: | 32 | npm install 33 | npm test 34 | PUBLIC_URL="https://${{ secrets.PUBLIC_URL }}" REACT_APP_SERVICE_URL=${{ secrets.SERVICE_URL }} REACT_APP_USE_WEBSOCKET="true" npm run build 35 | - name: rsync deploy 36 | uses: burnett01/rsync-deployments@4.1 37 | with: 38 | switches: -avzr --delete 39 | path: ./build/ 40 | remote_path: /var/www/virtual/${{ secrets.UBERSPACE_USER }}/${{ secrets.PUBLIC_URL }}/ 41 | remote_host: ${{ secrets.UBERSPACE_HOST }} 42 | remote_user: ${{ secrets.UBERSPACE_USER }} 43 | remote_key: ${{ secrets.DEPLOY_KEY_PRIVATE }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | *.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | .cert 22 | .env 23 | .eslintcache 24 | app 25 | .env.demo 26 | .env.app 27 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma":"all", 3 | "semi": false 4 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [unreleased] 8 | 9 | ### Added 10 | ### Changed 11 | - adding environmental variables to serverConfig.ts for jitsi server url (serviceUrl) to be either set in .env.local files by yourself or passed on `npm run start` or `npm run build` directly. So either add `SERVICE_URL = "url-of-your-jitsi-installation"` to your .env files (also to .env.development to be run during dev) or pass it like `SERVICE_URL=url-of-your-jitsi-installation npm run start`; 12 | ### Fixed 13 | ### Removed 14 | 15 | ## [0.6] 2021-03-04 16 | 17 | ### Added 18 | - Doc Folder for Documentation 19 | - Added CONTRIBUTION.md 20 | - Added INSTALL.md 21 | 22 | ### Changed 23 | - Adjusted Readme.md to be an Overview 24 | - Added gif of chatmosphere in use 25 | 26 | ### Fixed 27 | - Updated Storybook to last version; they are using an old immer install but since we're not using storybook in our app itself this is no problem. 28 | - Fixed Bug where Session Name with capital letters caused error 29 | 30 | ### Removed 31 | - openmoji npm package because it's huuuge; we added used icons in /assets folder 32 | 33 | ## [0.5.9] 2021-02-06 34 | 35 | ### Added 36 | - Added meta description in index.html (thx @bumi) 37 | 38 | ### Changed 39 | - Certificates are now linked in .env.development so it should also run on windows (Thanks @XristophD) 40 | - 41 | 42 | ### Fixed 43 | - 44 | ### Removed 45 | 46 | ## [0.5.8] - 2021-01-28 47 | 48 | ### Added 49 | - preferred Codec to be H265 in Options; it should switch the videoCodec to H265 instead of VP8, not sure if this is used though; maybe it needs to be set on server 50 | 51 | ### Changed 52 | - AudioRadius is now 650px; if its more it feels quite weird :) 53 | 54 | 55 | ## [0.5.7] - 2021-01.28 56 | 57 | ### Added 58 | - Kitty Icons for Mute 59 | - finished Color Variables for Theming 60 | - commented way to throttle pan and drag updates; thus its not updating the store that fast but with an delay of 200ms; should be checked and uncommented if we like it 61 | 62 | ### Changed 63 | - Input Field under users is now more consistent 64 | - Social Icons have theme colors 65 | 66 | ### Fixed 67 | - Button Story in Storyboard 68 | 69 | ### Removed 70 | 71 | ## [0.5.6] - 2021-01-27 72 | 73 | ### Added 74 | - Added a changelog to track our releases :) 75 | 76 | ### Changed 77 | - bumped version to 0.5.6 78 | - Interim Enter Screen is omitted for now since it's currently more confusing than helpful; 79 | so after Welcome one is direcly in the session/call; 80 | we know some people are irritated that they are directly visible with video; we will target that with the enter screen in future releases 81 | 82 | 83 | ### Fixed 84 | - No Videos where shown after camera permission request; hopefully fixed for now. 85 | - Potential Vulnerability in immer lib - updated to 8.0.1 86 | - Dragging Position, meaning the position of the current user, is now truncated to integer; (float values cannot be displayed by browsers anyways) 87 | 88 | ### Removed 89 | - uncommented the "Video" Button/Control in Footer because its not implemented yet 90 | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

😽 Chatmosphere

3 | The Open Source Videochat for Cozy Talks 4 |
5 | 6 | 7 | ![Chatmosphere Demo](docs/chatmosphere.gif) 8 | 9 | **Chatmosphere is an open source project that aims to make video calls informal and natural**. We missed the dynamics of a self-organizing crowd hanging out at one big table together. The big table in a bar, where so many discussions, jokes, comforting talks, utopias and ideas happen. With chatmosphere you can move and zoom in the area and hear people that are located near by louder and have dynamic talks. To learn more about the Chatmosphere project and ideas have a look in our [ABOUT.md](docs/ABOUT.md) 10 | 11 | 12 | ### Helpful Links 13 | * [How to run Chatmosphere](docs/INSTALL.md) 14 | * [Contribution Guideline](docs/CONTRIBUTION.md) 15 | * [Find Support here](https://github.com/Chatmosphere/chatmosphere-app/discussions) 16 | * [Code of Conduct for Excellent Humans](docs/CODE_OF_CONDUCT.md) 17 | * [Roadmap](https://github.com/orgs/Chatmosphere/projects/4) 18 | * [Code License](LICENSE.md) 19 | * [Donations to help us maintaining Chatmospere and run our demo server](https://opencollective.com/chatmosphere) 20 | 21 | 25 | 26 | 27 | ### Funded from September 2020 until February 2021 and September 2021 until February 2021 by 28 | 29 |

30 | Logo of the German Ministry for Education and Research            Logo of the Prototype Fund            Logo of the Open Knowledge Foundation Germany 31 |

32 | -------------------------------------------------------------------------------- /docs/ABOUT.md: -------------------------------------------------------------------------------- 1 | # Chatmosphere 2 | 3 | We enable informal conversation dynamics in the digital space. 4 | 5 | ## What it is? 6 | 7 | Chatmosphere is an open source project that aims to improve video call experiences. 8 | 9 | ## Why is it useful? 10 | 11 | Haven't you had this problem, when you and your friends met in a video call and just couldn't figure out who would talk next? Haven't you been bothered when everybody talks at once and you couldn't understand a thing? Or when you wanted to tell your best buddy something and had to wait for the conversation to end or use whatsapp in order to tell her?! We're trying to help you with a focus not just on functionality (code), but also on a pleasing user experience. And hey, it's open source… 12 | 13 | ## Why are we doing this? 14 | 15 | The idea came up during the first SARS-CoV-2 shutdown, when some of us started a [virtual bar](https://www.notion.so/How-it-started-e201ac34fbec4429b5dabe7de9699d1e). We found informal group conversations restricting and hierarchical, as the majority of tools is created to enable (corporate) meeting culture. We missed the dynamics of a self-organizing crowd hanging out at one big table together. The big table in a bar, on which so many discussions, jokes, comforting talks, utopias and ideas happened. 16 | 17 | It's our goal to find interaction patterns that enable informal, unmoderated video calls. It's our hypothesis, that we can prototype these interactions based on spatialized sound. 18 | 19 | ## Code of Conduct 20 | 21 | We're working with the [Citizen Code of Conduct](CODE_OF_CONDUCT.md) in order to make this project a respectful and happy space. 22 | 23 | ## Technologies 24 | 25 | We're using the [jitsi API](https://github.com/jitsi/lib-jitsi-meet/blob/master/doc/API.md), [react](https://reactjs.org/) and [type-script](https://www.typescriptlang.org/). 26 | 27 | ## How you can get started with the project? 28 | 29 | Check out our [README](README.md). 30 | 31 | ## Extended documantation 32 | 33 | For our research and other notes, check out our website: [https://chatmosphere.github.io/](https://chatmosphere.github.io/) 34 | 35 | ## Licensing 36 | 37 | We want everybody to be able to participate in this project. Since jitsi is using Apache 2.0, we're also using this for our code. Until we have a better idea, we use the Beer Ware License for all designs. We know that it would be nice if design-files itself were open-source files – thats an issue still to be tackled since most design software is not open source. Still you're welcome to use the assets and adjust to your likings. 38 | 39 | SPDX-License-Identifier: [Apache-2.0 OR GPL-2.0-or-later](LICENSE.md) 40 |
41 | SPDX-License-Identifier: Beer Ware License for Design Files 42 | 43 |
44 | ----------------------------------------------------------------------------
45 | "THE BEER-WARE LICENSE" (Revision 42):
46 | info@chatmosphere.cc wrote this file. As long as you retain this notice you
47 | can do whatever you want with this stuff. If we meet some day, and you think
48 | this stuff is worth it, you can buy us a beer in return.
49 | ---------------------------------------------------------------------------- 50 |
51 | 52 | ## Project status & where do we want to go with this? 53 | 54 | We're currently funded by the [Prototype Fund](https://prototypefund.de/) in order to develop a first prototype of our idea and we'd love to carry on working on this. We hope to find additional funding. So if you know something or someone or have any idea, drop us a line. We'd highly appreciate it. 55 | 56 | ## How can i get in touch? 57 | 58 | If you have any questions or issues regarding this project, feel free to contact us anytime and we'll try to answer as quick as life allows us to. [info@chatmosphere.cc](mailto:info@chatmosphere.cc) 59 | 60 | ## Funded from September 2020 until February 2021 and from September 2021 until February 2022 by 61 | 62 |

63 | Logo of the German Ministry for Education and Research            Logo of the Prototype Fund            Logo of the Open Knowledge Foundation Germany 64 |

65 | -------------------------------------------------------------------------------- /docs/BMBF_Logo_new.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/BMBF_Logo_new.jpg -------------------------------------------------------------------------------- /docs/BMBF_sponsored_by_en.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/BMBF_sponsored_by_en.jpg -------------------------------------------------------------------------------- /docs/Chatmosphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/Chatmosphere.png -------------------------------------------------------------------------------- /docs/Chatmosphere_PrivateConversation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/Chatmosphere_PrivateConversation.gif -------------------------------------------------------------------------------- /docs/INSTALL.md: -------------------------------------------------------------------------------- 1 | # 😽 Install Chatmosphere 2 | 3 | 4 | -> If you want to run **your own Jitsi Server** checkout [these instructions](https://www.notion.so/universalinteraction/Documentation-87a86705ea564ead942d3c81d2bb06e6) 5 | 6 | ## ☝️ Download Files 7 | * go to our github repository: [Chatmosphere/chatmosphere-app](https://github.com/Chatmosphere/chatmosphere-app) 8 | * Download Zip or clone repo 9 | 10 | ![](github-screenshot.png) 11 | 12 | * Either download the ZIP file and unpack it or clone with git 13 | * Then open the folder in your favourite IDE 14 | 15 | ## ✌️ Install the App 16 | 17 | ```shell 18 | # run yarn install or npm install 19 | npm install 20 | ``` 21 | 22 | #### Certificates 23 | 24 | WebRTC - the basic technology Jitsi and Chatmosphere are running on, needs HTTPS to run. So we need a local certificate to start the app.; 25 | a nice description to add one can be found here 26 | [https://www.freecodecamp.org/news/how-to-set-up-https-locally-with-create-react-app/](https://www.freecodecamp.org/news/how-to-set-up-https-locally-with-create-react-app/) 27 | 28 | So if you have not done so already for some other project, make your local machine a certification authority to create valid certificates. We're using 🍺 [homebrew](https://brew.sh/) for that. 29 | 30 | ```bash 31 | # Install mkcert tool with homebrew 32 | brew install mkcert 33 | 34 | # Install nss (only needed if you use Firefox) 35 | brew install nss 36 | 37 | # Setup mkcert on your machine (creates a CA) 38 | mkcert -install 39 | ``` 40 | 41 | #### Create a Certificate 42 | Then, from the root folder of your downloaded chatmosphere directory that you just downloaded, run: 43 | 44 | ```bash 45 | # Create .cert directory if it doesn't exist 46 | mkdir -p .cert 47 | 48 | # Generate the certificate (ran from the root of this project) 49 | mkcert -key-file ./.cert/key.pem -cert-file ./.cert/cert.pem "localhost" 50 | ``` 51 | 52 | ## 😻 Run Chatmosphere 53 | 54 | ```bash 55 | # to start chatmosphere, type 56 | npm start 57 | # or if you prefer yarn 58 | yarn start 59 | ``` 60 | Runs the app in the development mode.\ 61 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 62 | 63 | The page will reload if you make edits.\ 64 | You will also see any lint errors in the console. 65 | 66 | The default setup uses a test session on [https://meet.jit.si/](https://meet.jit.si/) by default; the public jitsi-meet should only be used for testing, not for production. They now offer [hosted services](https://jaas.8x8.vc/) and since they're awesome we hope to make chatmosphere compatible soon. 67 | 68 | To use your very own Jitsi Server, copy `serverConfig-example.ts` and rename it to `serverConfig.ts` 69 | Replace the configuration in the file with your very own Jitsi Server paths: 70 | 71 | ```bash 72 | export const connectionOptions = { 73 | serviceUrl: '//your.domain.com/http-bind', 74 | hosts: { 75 | domain: "your.domain.com", 76 | muc: 'conference.your.domain.com', 77 | // anonymousdomain: '' 78 | }, 79 | bosh: '//your.domain.com/http-bind', 80 | 81 | clientNode: 'http://jitsi.org/jitsimeet' 82 | } 83 | ``` 84 | 85 | Restart your app *(ctrl+c),* then `npm start` aaaaaand you're done - test it and please tell us how it worked 🖖 😽 🎉 86 | 87 | --- 88 | 89 | ## Other Available Scripts 90 | 91 | ### `npm test` `yarn test` 92 | 93 | Launches the test runner in the interactive watch mode.\ 94 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 95 | -------------------------------------------------------------------------------- /docs/OpenDesignChatmosphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/OpenDesignChatmosphere.png -------------------------------------------------------------------------------- /docs/chatmosphere.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/chatmosphere.gif -------------------------------------------------------------------------------- /docs/github-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/github-screenshot.png -------------------------------------------------------------------------------- /docs/playwave_chatmosphere.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/docs/playwave_chatmosphere.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.6.1", 3 | "homepage": "https://app.chatmosphere.cc", 4 | "name": "chatmosphere", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.14.1", 8 | "@testing-library/react": "^12.0.0", 9 | "@testing-library/user-event": "^13.2.1", 10 | "@types/jest": "^27.0.1", 11 | "@types/node": "^16.7.13", 12 | "@types/react": "^17.0.20", 13 | "@types/react-dom": "^17.0.9", 14 | "@types/styled-components": "^5.1.21", 15 | "env-cmd": "^10.1.0", 16 | "immer": "^9.0.12", 17 | "lodash": "^4.17.20", 18 | "polished": "^4.1.4", 19 | "react": "^17.0.2", 20 | "react-dom": "^17.0.2", 21 | "react-feather": "^2.0.9", 22 | "react-icons": "^4.1.0", 23 | "react-router-dom": "^5.2.0", 24 | "react-scripts": "^5.0.0", 25 | "react-zoom-pan-pinch": "^1.6.1", 26 | "simple-zustand-devtools": "^1.0.0", 27 | "styled-components": "^5.3.3", 28 | "typescript": "^4.4.2", 29 | "web-vitals": "^2.1.0", 30 | "zustand": "^3.6.9" 31 | }, 32 | "scripts": { 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "build:demo": "env-cmd -f .env.demo.local react-scripts build && rm -rf demo && rm -rf app && mv build demo", 36 | "build:app": "env-cmd -f .env.app.local react-scripts build && rm -rf app && rm -rf demo && mv build app", 37 | "test": "react-scripts test", 38 | "eject": "react-scripts eject" 39 | }, 40 | "eslintConfig": { 41 | "extends": [ 42 | "react-app", 43 | "react-app/jest" 44 | ] 45 | }, 46 | "browserslist": { 47 | "production": [ 48 | ">0.2%", 49 | "not dead", 50 | "not op_mini all" 51 | ], 52 | "development": [ 53 | "last 1 chrome version", 54 | "last 1 firefox version", 55 | "last 1 safari version" 56 | ] 57 | }, 58 | "devDependencies": { 59 | "@testing-library/react-hooks": "^7.0.2", 60 | "prettier": "2.2.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | Options -MultiViews 2 | RewriteEngine On 3 | RewriteBase / 4 | RewriteCond %{HTTP_HOST} ^chatmosphere.cc/build 5 | RewriteRule (.*) https://stage.chatmosphere.cc/$1 [R=301,L] 6 | RewriteCond %{HTTP_HOST} ^chatmosphere.cc/demo 7 | RewriteRule (.*) https://demo.chatmosphere.cc/$1 [R=301,L] 8 | RewriteCond %{REQUEST_FILENAME} !-f 9 | RewriteRule ^ index.html [QSA,L] -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fossasia/chatmosphere-app/2dd8e5e7c2f2541bef18ad3fbc77730f930f4ec4/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 27 | Chatmosphere 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/libs/strophe/strophe.disco.min.js: -------------------------------------------------------------------------------- 1 | Strophe.addConnectionPlugin("disco",{_connection:null,_identities:[],_features:[],_items:[],init:function(conn){this._connection=conn;this._identities=[];this._features=[];this._items=[];conn.addHandler(this._onDiscoInfo.bind(this),Strophe.NS.DISCO_INFO,"iq","get",null,null);conn.addHandler(this._onDiscoItems.bind(this),Strophe.NS.DISCO_ITEMS,"iq","get",null,null)},addIdentity:function(category,type,name,lang){for(var i=0;i { 6 | 7 | }); 8 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { Header } from './components/Header/Header' 3 | import styled from 'styled-components' 4 | 5 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom" 6 | import { Home } from "./pages/Home/Home" 7 | import { Enter } from "./pages/Enter/Enter" 8 | import { Session } from "./pages/Session/Session" 9 | 10 | const AppContainer = styled.div` 11 | text-align: center; 12 | position: fixed; 13 | width: 100%; 14 | height: 100%; 15 | cursor: default; 16 | ` 17 | 18 | 19 | function App() { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {/* TODO: redirect to "/enter" if this the first time the user in this conference */} 30 | 31 | 32 | 33 | 34 |
Chatmosphere
35 | 36 |
37 |
38 |
39 |
40 | ) 41 | } 42 | 43 | export default App 44 | -------------------------------------------------------------------------------- /src/addons/Screenshare/ScreenshareButton/ScreenshareButton.tsx: -------------------------------------------------------------------------------- 1 | import { useConferenceStore } from "../../../store/ConferenceStore" 2 | import { useConnectionStore } from "../../../store/ConnectionStore" 3 | import { useLocalStore } from "../../../store/LocalStore" 4 | import { IconButton } from "../../../components/common/Buttons/Button" 5 | import { useCallback, useState } from "react" 6 | import ScreenShareIcon from "../../../assets/icons/ScreenShare" 7 | 8 | // TODO Error when alone in call - not sure why - replaceTrack has some empty object 9 | export const ScreenshareButton = (props) => { 10 | const jsMeet = useConnectionStore((state) => state.jsMeet) 11 | const setLocalTracks = useLocalStore( 12 | useCallback((store) => store.setLocalTracks, []), 13 | ) 14 | const conferenceObject = useConferenceStore((state) => state.conferenceObject) 15 | const [isSharing, setIsSharing] = useState(false) 16 | 17 | 18 | 19 | const setNewTracks = (tracks, oldTrack) => { 20 | const newTrack = tracks[0] 21 | // const oldTrack = conferenceObject?.getLocalVideoTrack() 22 | // oldTrack?.dispose() 23 | 24 | let isDesktopTrack = newTrack.videoType === "desktop" 25 | if (isDesktopTrack) { 26 | // add stop listener to automatically activate camera again if stopped by browser controls 27 | // -> but thats why the video track is called two times, manual stop calls it also 28 | newTrack.addEventListener(jsMeet?.events.track.LOCAL_TRACK_STOPPED, () => createVideoTrack()) 29 | } 30 | if (oldTrack) { 31 | conferenceObject?.replaceTrack(oldTrack, newTrack).then(() => { 32 | setLocalTracks(tracks) 33 | setIsSharing(isDesktopTrack) 34 | }) 35 | .catch(err => console.error(err)) 36 | } //else add new Track? 37 | } 38 | 39 | const createVideoTrack = () => { 40 | if (!jsMeet) return // not needed ? 41 | 42 | //delete old Track 43 | const oldTrack = conferenceObject?.getLocalVideoTrack() 44 | const type = oldTrack?.videoType === "desktop" ? "video" : "desktop" 45 | oldTrack?.dispose() 46 | 47 | // create tracks 48 | jsMeet 49 | .createLocalTracks({ devices: [type] }) 50 | .then((tracks) => setNewTracks(tracks, oldTrack)) 51 | .catch((error) => { 52 | console.log(error) 53 | }) 54 | } 55 | 56 | return ( 57 | } 62 | label="Screenshare" 63 | /> 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /src/addons/Screenshare/components/LocalDesktop.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useEffect, useRef } from "react" 2 | import styled from "styled-components" 3 | import { useConferenceStore } from "../../../store/ConferenceStore" 4 | 5 | const StyledVideo = styled.video` 6 | width: auto; 7 | height: 200px; 8 | display: block; 9 | ` 10 | 11 | const LocalDesktop:React.FC<{track:IVideoTrack}> = memo(({track}) => { 12 | const myRef:any = useRef() 13 | const room = useConferenceStore(store => store.conferenceObject) 14 | 15 | useEffect(() => { 16 | room?.addTrack(track) //TODO should be done in store I think? 17 | .catch(error => console.log(error))//the track might have been added already, handle the promise error 18 | return() => { 19 | // room?.removeTrack(track) // we're replacing, not deleting & adding new one; 20 | } 21 | },[room, track]) 22 | 23 | useEffect(()=> { 24 | const el = myRef.current 25 | if(track?.containers?.length === 0) track.attach(el) 26 | return (() => { 27 | track.detach(el) 28 | }) 29 | },[track]) 30 | 31 | 32 | return 33 | }) 34 | 35 | export default LocalDesktop -------------------------------------------------------------------------------- /src/addons/Stage/Stage.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect } from "react" 2 | import styled from "styled-components" 3 | import { useConferenceStore } from "../../store/ConferenceStore" 4 | import { useConnectionStore } from "../../store/ConnectionStore" 5 | import { useLocalStore } from "../../store/LocalStore" 6 | import { LocalUser } from "./components/StageUser" 7 | import { StageUsers } from "./components/StageUsers" 8 | import { GhostButton} from "../../components/common/Buttons/Button" 9 | import { EyeOff, VolumeX } from "react-feather" 10 | 11 | const Container = styled.div` 12 | position: relative; 13 | display: flex; 14 | flex-direction: column; 15 | gap: 20px; 16 | width: auto; 17 | height: auto; 18 | /* top: 24px; 19 | left: 24px; */ 20 | ` 21 | const ScrollContainer = styled.div` 22 | position: absolute; 23 | top: 24px; 24 | left: 0; 25 | bottom: 24px; 26 | padding-right: 60px; 27 | padding-left: 24px; 28 | width: 12%; 29 | overflow-y: auto; 30 | overflow-x: visible; 31 | ` 32 | 33 | //TODO cleaner way would be to handle the whole stage management here and refactor calculateUsersOnScreen 34 | // Actually that would be much better - no need to loop all users in localstore on each user movement to recalculate who is on stage 35 | // in set last-n we would just set calculated visible user array and stage user array that is set here only on participantpropertychange 36 | // const onParticipantPropertyChange = (e) => { 37 | // useLocalStore.getState().calculateUsersOnScreen() 38 | // } 39 | 40 | const userOnScreenSelector = (store) => store.calculateUsersOnScreen 41 | const ids_OnStageSelector = (store) => store.usersOnStage 42 | 43 | const ConnectedStage = () => { 44 | const stageVisible = useLocalStore( 45 | useCallback((store) => store.stageVisible, []), 46 | ) 47 | const stageMute = useLocalStore(useCallback((store) => store.stageMute, [])) 48 | const localUserOnStage = useLocalStore((store) => store.onStage) 49 | const conference = useConferenceStore((store) => store.conferenceObject) 50 | const JSMeet = useConnectionStore((store) => store.jsMeet) 51 | const calculateUsersOnScreen = useLocalStore(userOnScreenSelector) 52 | const usersOnStage = useLocalStore(ids_OnStageSelector) 53 | 54 | useEffect(() => { 55 | conference?.on( 56 | JSMeet?.events.conference.PARTICIPANT_PROPERTY_CHANGED, 57 | calculateUsersOnScreen, 58 | ) 59 | 60 | return () => { 61 | conference?.off( 62 | JSMeet?.events.conference.PARTICIPANT_PROPERTY_CHANGED, 63 | calculateUsersOnScreen, 64 | ) 65 | } 66 | }, [conference, JSMeet, calculateUsersOnScreen]) 67 | 68 | return ( 69 | <> 70 | {(usersOnStage.length > 0 || localUserOnStage) && ( 71 | 72 | {localUserOnStage && } 73 | {stageVisible && } 74 | 75 | )} 76 | 77 | ) 78 | } 79 | 80 | export const Stage = ({ children }) => ( 81 | 82 | 83 | 84 | {children} 85 | 86 | 87 | ) 88 | 89 | 90 | 91 | const StageControls = () => { 92 | const toggleMute = useLocalStore((store) => store.toggleStageMute) 93 | const toggleStage = useLocalStore((store) => store.toggleStage) 94 | const mute = useLocalStore((store) => store.stageMute) 95 | const visible = useLocalStore((store) => store.stageVisible) 96 | 97 | return ( 98 | 99 | {!visible && ( 100 | } 108 | /> 109 | )} 110 | {visible && ( 111 | } 118 | /> 119 | )} 120 | {visible && mute && ( 121 | } 129 | /> 130 | )} 131 | {visible && !mute && ( 132 | } 139 | /> 140 | )} 141 | 142 | ) 143 | } 144 | 145 | const StageControlsBox = styled.div` 146 | display: flex; 147 | flex-direction: row; 148 | ` 149 | 150 | const MuteStageButton = styled(GhostButton)`` 151 | 152 | const HideStageButton = styled(GhostButton)`` 153 | 154 | export default ConnectedStage 155 | -------------------------------------------------------------------------------- /src/addons/Stage/components/ResizeControl.tsx: -------------------------------------------------------------------------------- 1 | import { MouseEventHandler } from "react" 2 | import { Maximize2 } from "react-feather" 3 | import styled from "styled-components" 4 | 5 | const ResizeContainer = styled.div` 6 | position: absolute; 7 | left: 0; 8 | top: 0; 9 | right: 0; 10 | bottom: 0; 11 | & div { 12 | display: none; 13 | } 14 | &:hover { 15 | background-color: rgba(255, 255, 255, 0.2); 16 | & div { 17 | display: block; 18 | } 19 | } 20 | ` 21 | 22 | const ResizeElement = styled.div` 23 | position: absolute; 24 | top: 50%; 25 | left: 50%; 26 | transform: translate(-50%, -50%); 27 | background-color: rgba(0, 0, 0, 0.5); 28 | padding: 15px; 29 | border-radius: 50%; 30 | & svg { 31 | stroke:white; 32 | } 33 | ` 34 | 35 | export const ResizeControl = ({callback=()=>null}:{callback:MouseEventHandler|undefined}) => { 36 | return ( 37 | 38 | 39 | 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/addons/Stage/components/StageAudio.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | //Could be a general Audio Element for all other components 4 | export const AudioComponent:React.FunctionComponent<{audio:IAudioTrack | undefined, volume:number, className?:string, id?:string}> = ({ audio, volume, className, id }) => { 5 | 6 | const myRef: any = useRef(); 7 | 8 | useEffect(() => { 9 | if (myRef.current && myRef.current.volume !== undefined) 10 | myRef.current.volume = volume; 11 | }, [volume]); 12 | 13 | useEffect(() => { 14 | const currentEl = myRef.current; 15 | let tmpEl = undefined; 16 | if (audio?.containers && audio?.containers.length > 0) { 17 | tmpEl = audio.containers[0]; 18 | if(tmpEl) audio.detach(tmpEl); 19 | } 20 | if (audio?.containers.length === 0) 21 | audio?.attach(currentEl); 22 | return (() => { 23 | audio?.detach(currentEl); 24 | if (tmpEl) 25 | audio?.attach(tmpEl); 26 | }); 27 | },[audio]); 28 | 29 | return