Merge branch 'livekit' into defaut-server-config
@@ -1,54 +0,0 @@
|
|||||||
const COPYRIGHT_HEADER = `/*
|
|
||||||
Copyright %%CURRENT_YEAR%% New Vector Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
plugins: ["matrix-org", "rxjs"],
|
|
||||||
extends: [
|
|
||||||
"plugin:matrix-org/react",
|
|
||||||
"plugin:matrix-org/a11y",
|
|
||||||
"plugin:matrix-org/typescript",
|
|
||||||
"prettier",
|
|
||||||
"plugin:rxjs/recommended",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module",
|
|
||||||
project: ["./tsconfig.json"],
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
"matrix-org/require-copyright-header": ["error", COPYRIGHT_HEADER],
|
|
||||||
"jsx-a11y/media-has-caption": "off",
|
|
||||||
// We should use the js-sdk logger, never console directly.
|
|
||||||
"no-console": ["error"],
|
|
||||||
"react/display-name": "error",
|
|
||||||
// Encourage proper usage of Promises:
|
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
|
||||||
"@typescript-eslint/promise-function-async": "error",
|
|
||||||
"@typescript-eslint/require-await": "error",
|
|
||||||
"@typescript-eslint/await-thenable": "error",
|
|
||||||
// To help ensure that we get proper vite/rollup lazy loading (e.g. for matrix-js-sdk):
|
|
||||||
"@typescript-eslint/consistent-type-imports": [
|
|
||||||
"error",
|
|
||||||
{ fixStyle: "inline-type-imports" },
|
|
||||||
],
|
|
||||||
// To encourage good usage of RxJS:
|
|
||||||
"rxjs/no-exposed-subjects": "error",
|
|
||||||
"rxjs/finnish": "error",
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
react: {
|
|
||||||
version: "detect",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/sh
|
|
||||||
|
|
||||||
FILE=.links.disabled.yaml
|
|
||||||
if test -f "$FILE"; then
|
|
||||||
# echo "$FILE exists. -> moving to .links.disabled.yaml"
|
|
||||||
mv .links.disabled.yaml .links.yaml
|
|
||||||
# echo "running yarn"
|
|
||||||
yarnLog=$(yarn)
|
|
||||||
echo "[yarn-linker] The post-commit hook has re-enabled .links.yaml."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
#!/usr/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
FILE=".links.yaml"
|
# Checks if there currently is linking configured. Informs the user to disable linking before committing.
|
||||||
if test -f "$FILE"; then
|
|
||||||
# echo "$FILE exists. -> moving to .links.disabled.yaml"
|
PNPMFILE=.pnpmfile.cjs
|
||||||
mv .links.yaml .links.disabled.yaml
|
if test -f "$PNPMFILE"; then
|
||||||
# echo "running yarn"
|
echo "[pnpm-linker] The pre-commit hook detected $PNPMFILE which implies you have linked packages in your pnpm-lock.yaml. Run pnpm links:off and commit again. See also linking.md."
|
||||||
x=$(yarn)
|
|
||||||
y=$(git add yarn.lock)
|
|
||||||
echo "[yarn-linker] The pre-commit hook has disabled .links.yaml and MODIFIED the yarn.lock file. Review the staged changes (the hook added yarn.lock, was this desired?) and run \`git commit \` again if they look okay. The post-commit hook will re-enable your links."
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
46
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<!-- Thanks for submitting a PR! Please read CONTRIBUTING.md before you start. -->
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **Features and UI changes require a pre-approved issue.**
|
||||||
|
> Every PR must have a linked issue
|
||||||
|
> that a maintainer has reviewed and approved **before you started writing code**.
|
||||||
|
> PRs that don't meet this requirement will not be reviewed.
|
||||||
|
> See [CONTRIBUTING.md](https://github.com/element-hq/element-call/blob/livekit/CONTRIBUTING.md) for ElementCall decided for this approach.
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
<!-- Describe shortly what has been changed -->
|
||||||
|
|
||||||
|
## Motivation and context
|
||||||
|
|
||||||
|
<!-- Provide a link to the pre-approved issue, or explain the context for a bug fix -->
|
||||||
|
|
||||||
|
## Screenshots / GIFs
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
You can use a table like this to show a before/after comparison.
|
||||||
|
Uncomment the markdown table below and fill in the last line:
|
||||||
|
|
||||||
|
|Before|After|
|
||||||
|
|-|-|
|
||||||
|
|||
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
<!-- Explain how you tested your changes -->
|
||||||
|
|
||||||
|
- Step 1
|
||||||
|
- Step 2
|
||||||
|
- Step ...
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] A linked, pre-approved issue exists for this feature or UI change.
|
||||||
|
- [ ] I have read [CONTRIBUTING.md](https://github.com/element-hq/element-call/blob/livekit/CONTRIBUTING.md) in full.
|
||||||
|
- [ ] Pull request includes screenshots or videos for any UI changes.
|
||||||
|
- [ ] Tests written for new code (and existing touched code where feasible).
|
||||||
|
- [ ] Linter and other CI checks pass.
|
||||||
|
- [ ] I have licensed the changes to Element by completing the [Contributor License Agreement (CLA)](https://cla-assistant.io/element-hq/element-call)
|
||||||
253
.github/labels.yml
vendored
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
- name: "A-1:1"
|
||||||
|
description: "Calls between two people"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Big-Grid"
|
||||||
|
description: "The freedom layout system used for >12 participants"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Developer-Experience"
|
||||||
|
description: "Workflow of developing: building, linting, debugging, profiling, etc."
|
||||||
|
color: "c5def5"
|
||||||
|
- name: "A-E2EE"
|
||||||
|
description: "End-to-end encryption"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Embedded"
|
||||||
|
description: "Using the app embedded within other Matrix clients (as a widget)"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Feedback-Reporting"
|
||||||
|
description: "Reporting process for bugs, debug logs (rageshakes), suggestions"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Freedom"
|
||||||
|
description: "Freedom layout, where participants can be rearranged and resized"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Handset"
|
||||||
|
description: "Audio playback through the earpiece of a phone. Also known as 'earpiece mode' or 'handset mode'."
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Huddle"
|
||||||
|
description: "Ad-hoc calls in a room notifying others"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Lobby"
|
||||||
|
description: "The page before joining a call"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Login"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Matrix2.0"
|
||||||
|
description: "Issues relating to the Matrix 2.0 / MSC4143 work, such as sticky events and multi-sfu"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Media-Devices"
|
||||||
|
color: "BFD4F2"
|
||||||
|
- name: "A-Media-Quality"
|
||||||
|
description: "Distortions or glitches in audio/video"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Meeting"
|
||||||
|
description: "Scheduled call on the calendar"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Mobile"
|
||||||
|
description: "Using the app on a mobile device"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Moderation"
|
||||||
|
description: "Access to calls and powers within calls"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Performance"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Reactions"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Registration"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Screen-Sharing"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-SDK"
|
||||||
|
description: "SDK for building MatrixRTC + LiveKit widgets"
|
||||||
|
color: "c5def5"
|
||||||
|
- name: "A-Settings"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-SFU"
|
||||||
|
description: "Routing calls through a selective forwarding unit"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Signaling"
|
||||||
|
description: "Call signaling"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Simulcast"
|
||||||
|
description: "Automatic selection of variable video resolutions"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-SPA"
|
||||||
|
description: "Standalone application accessed via call links"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Spatial-Audio"
|
||||||
|
description: "Directional audio based on where a speaker appears on screen"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Speech-Enhancement"
|
||||||
|
description: "Techniques to enhance the intelligibility of speech in calls"
|
||||||
|
color: "c5def5"
|
||||||
|
- name: "A-Split-Grid"
|
||||||
|
description: "The freedom layout system used for ≤12 participants"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Spotlight"
|
||||||
|
description: "Spotlight layout, where the active speaker is foregrounded"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Telemetry-Posthog"
|
||||||
|
description: "Share opt in usage data for optimizing the app via posthog"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Testing"
|
||||||
|
description: "Integration tests, unit tests, etc."
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Video-Rooms"
|
||||||
|
description: "Rooms reserved exclusively for calling"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A-Walkie-Talkie"
|
||||||
|
description: "Walkie-talkie / PTT (push-to-talk) mode"
|
||||||
|
color: "bfd4f2"
|
||||||
|
- name: "A11y"
|
||||||
|
description: "Accessibility"
|
||||||
|
color: "4ADEC0"
|
||||||
|
- name: "backport-candidate"
|
||||||
|
description: "Something that is a candidate for backport to a particular release branch"
|
||||||
|
color: "0B8D85"
|
||||||
|
- name: "customer-retainer"
|
||||||
|
color: "F44A5F"
|
||||||
|
- name: "dependencies"
|
||||||
|
description: "Pull requests that update a dependency file"
|
||||||
|
color: "0366d6"
|
||||||
|
- name: "development build"
|
||||||
|
description: "runs yarn build process in development mode"
|
||||||
|
color: "1d76db"
|
||||||
|
- name: "Discord"
|
||||||
|
description: "Use case familiar to Discord users"
|
||||||
|
color: "3670d2"
|
||||||
|
- name: "docker build"
|
||||||
|
description: "Creates a docker image for this PR"
|
||||||
|
color: "0e8a16"
|
||||||
|
- name: "EPIC"
|
||||||
|
color: "5319E7"
|
||||||
|
- name: "good first issue"
|
||||||
|
description: "Good for newcomers"
|
||||||
|
color: "7057ff"
|
||||||
|
- name: "Help Wanted"
|
||||||
|
description: "Community contributions are welcome!"
|
||||||
|
color: "159818"
|
||||||
|
- name: "I18n"
|
||||||
|
description: "Internationalisation"
|
||||||
|
color: "d4c5f9"
|
||||||
|
- name: "O-Frequent"
|
||||||
|
description: "Affects or can be seen by most users regularly or impacts most users' first experience"
|
||||||
|
color: "0052CC"
|
||||||
|
- name: "O-Occasional"
|
||||||
|
description: "Affects or can be seen by some users regularly or most users rarely"
|
||||||
|
color: "1D76DB"
|
||||||
|
- name: "O-Uncommon"
|
||||||
|
description: "Most users are unlikely to come across this or unexpected workflow"
|
||||||
|
color: "C5DEF5"
|
||||||
|
- name: "p1"
|
||||||
|
description: "Must fix/implement before this is usable as a product"
|
||||||
|
color: "D93F0B"
|
||||||
|
- name: "p2"
|
||||||
|
description: "Should fix/implement, but not at the expense of p1s"
|
||||||
|
color: "FBCA04"
|
||||||
|
- name: "p3"
|
||||||
|
description: "Could fix/implement when time allows"
|
||||||
|
color: "0E8A16"
|
||||||
|
- name: "PR-Breaking-Change"
|
||||||
|
description: "A Pull request that changes EC in a way that is incompatible to the previous version."
|
||||||
|
color: "D93F0B"
|
||||||
|
- name: "PR-Bug-Fix"
|
||||||
|
description: "Release note category. A PR that fixes a bug."
|
||||||
|
color: "C2E0C6"
|
||||||
|
- name: "PR-Developer-Experience"
|
||||||
|
description: "Release note category. A PR that does not change EC but improves working with the repository."
|
||||||
|
color: "C2E0C6"
|
||||||
|
- name: "PR-Documentation"
|
||||||
|
description: "Release note category. A PR that improves the documentation."
|
||||||
|
color: "C2E0C6"
|
||||||
|
- name: "PR-Feature"
|
||||||
|
description: "Release note category. A PR that introduces a new user facing feature."
|
||||||
|
color: "C2E0C6"
|
||||||
|
- name: "PR-Improvement"
|
||||||
|
description: "Release note category. A PR that improves EC's performance or stability."
|
||||||
|
color: "C2E0C6"
|
||||||
|
- name: "PR-Task"
|
||||||
|
description: "Release note category. A PR that is hidden from release note."
|
||||||
|
color: "C2E0C6"
|
||||||
|
- name: "Privacy"
|
||||||
|
color: "f41192"
|
||||||
|
- name: "Roadmap"
|
||||||
|
color: "57457E"
|
||||||
|
- name: "S-Critical"
|
||||||
|
description: "Prevents work, causes data loss and/or has no workaround"
|
||||||
|
color: "bd0026"
|
||||||
|
- name: "S-Major"
|
||||||
|
description: "Severely degrades major functionality or product features, with no satisfactory workaround"
|
||||||
|
color: "fc4e2a"
|
||||||
|
- name: "S-Minor"
|
||||||
|
description: "Impairs non-critical functionality or suitable workarounds exist"
|
||||||
|
color: "feb24c"
|
||||||
|
- name: "S-Tolerable"
|
||||||
|
description: "Low/no impact on users"
|
||||||
|
color: "ffeda0"
|
||||||
|
- name: "Security"
|
||||||
|
color: "b3e5fc"
|
||||||
|
- name: "storybook build"
|
||||||
|
description: "Build and deploy the storybook frontend to netlify."
|
||||||
|
color: "45cd61"
|
||||||
|
- name: "T-Defect"
|
||||||
|
description: "Something isn't working: bugs, crashes, hangs, vulnerabilities, or other reported problems"
|
||||||
|
color: "98e6ae"
|
||||||
|
- name: "T-Enhancement"
|
||||||
|
description: "New features, changes in functionality, performance boosts, user-facing improvements"
|
||||||
|
color: "98e6ae"
|
||||||
|
- name: "T-Other"
|
||||||
|
description: "Questions, user support, anything else"
|
||||||
|
color: "98e6ae"
|
||||||
|
- name: "T-Task"
|
||||||
|
description: "Refactoring, enabling or disabling functionality, other engineering tasks"
|
||||||
|
color: "98e6ae"
|
||||||
|
- name: "X-Blocked"
|
||||||
|
description: "Cannot be merged due to external dependencies"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Cannot-Reproduce"
|
||||||
|
description: "Needs reproduction steps"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Needs-Design"
|
||||||
|
description: "May require input from the design team"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Needs-Info"
|
||||||
|
description: "This issue is blocked awaiting information from the reporter"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Needs-Investigation"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Needs-Product"
|
||||||
|
description: "More input needed from the Product team"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Regression"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Release-Blocker"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Spec-Changes"
|
||||||
|
description: "May require spec changes"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "X-Won't-Fix"
|
||||||
|
description: "This will not be worked on"
|
||||||
|
color: "ff7979"
|
||||||
|
- name: "Z-Community-Testing"
|
||||||
|
description: "Issues found during the community testing sessions"
|
||||||
|
color: "efefef"
|
||||||
|
- name: "Z-Could"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-Design"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-Flaky-Test"
|
||||||
|
color: "aaaaaa"
|
||||||
|
- name: "Z-Media-Failure"
|
||||||
|
description: "Someone's audio or video isn't coming through"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-Must"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-Platform-Specific"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-Power-Users"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-ProductPolish"
|
||||||
|
color: "aaaaaa"
|
||||||
|
- name: "Z-Should"
|
||||||
|
color: "ededed"
|
||||||
|
- name: "Z-Splitbrain"
|
||||||
|
description: "Someone who should be on the call isn't showing up"
|
||||||
|
color: "ededed"
|
||||||
13
.github/workflows/blocked.yaml
vendored
@@ -1,7 +1,16 @@
|
|||||||
name: Prevent blocked
|
name: Prevent blocked
|
||||||
on:
|
on:
|
||||||
|
# zizmor: ignore[dangerous-triggers]
|
||||||
|
# Reason: This workflow does not checkout code or use secrets.
|
||||||
|
# It only reads labels to set a failure status on the PR.
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, labeled, unlabeled]
|
types: [opened, labeled, unlabeled, synchronize]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
# Required to fail the check on the PR
|
||||||
|
statuses: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prevent-blocked:
|
prevent-blocked:
|
||||||
name: Prevent blocked
|
name: Prevent blocked
|
||||||
@@ -10,7 +19,7 @@ jobs:
|
|||||||
pull-requests: read
|
pull-requests: read
|
||||||
steps:
|
steps:
|
||||||
- name: Add notice
|
- name: Add notice
|
||||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
|
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
|
||||||
if: contains(github.event.pull_request.labels.*.name, 'X-Blocked')
|
if: contains(github.event.pull_request.labels.*.name, 'X-Blocked')
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
61
.github/workflows/build-and-publish-docker.yaml
vendored
@@ -20,13 +20,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # required to upload release asset
|
contents: write # required to upload release asset
|
||||||
packages: write
|
packages: write # needed for publishing packages to GHCR
|
||||||
|
id-token: write # needed for login into tailscale with GitHub OIDC Token
|
||||||
steps:
|
steps:
|
||||||
- name: Check it out
|
- name: Check it out
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ inputs.artifact_run_id }}
|
run-id: ${{ inputs.artifact_run_id }}
|
||||||
@@ -34,24 +37,64 @@ jobs:
|
|||||||
path: dist
|
path: dist
|
||||||
|
|
||||||
- name: Log in to container registry
|
- name: Log in to container registry
|
||||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Connect to Tailscale
|
||||||
|
uses: tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
with:
|
||||||
|
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||||
|
audience: ${{ secrets.TS_AUDIENCE }}
|
||||||
|
tags: tag:github-actions
|
||||||
|
|
||||||
|
- name: Compute vault jwt role name
|
||||||
|
id: vault-jwt-role
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
echo "role_name=github_service_management_$( echo "${{ github.repository }}" | sed -r 's|[/-]|_|g')" | tee -a "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Get team registry token
|
||||||
|
id: import-secrets
|
||||||
|
uses: hashicorp/vault-action@4c06c5ccf5c0761b6029f56cfb1dcf5565918a3b # v3
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
with:
|
||||||
|
url: https://vault.infra.ci.i.element.dev
|
||||||
|
role: ${{ steps.vault-jwt-role.outputs.role_name }}
|
||||||
|
path: service-management/github-actions
|
||||||
|
jwtGithubAudience: https://vault.infra.ci.i.element.dev
|
||||||
|
method: jwt
|
||||||
|
secrets: |
|
||||||
|
services/voip-repositories/secret/data/oci.element.io username | OCI_USERNAME ;
|
||||||
|
services/voip-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
|
||||||
|
|
||||||
|
- name: Login to oci.element.io Registry
|
||||||
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
with:
|
||||||
|
registry: oci-push.vpn.infra.element.io
|
||||||
|
username: ${{ steps.import-secrets.outputs.OCI_USERNAME }}
|
||||||
|
password: ${{ steps.import-secrets.outputs.OCI_PASSWORD }}
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
||||||
with:
|
with:
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: |
|
||||||
tags: ${{ inputs.docker_tags}}
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
oci-push.vpn.infra.element.io/element-call
|
||||||
|
tags: ${{ inputs.docker_tags }}
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.licenses=AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0
|
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|||||||
38
.github/workflows/build-element-call.yaml
vendored
@@ -7,8 +7,13 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
package:
|
package:
|
||||||
type: string # This would ideally be a `choice` type, but that isn't supported yet
|
type: string # This would ideally be a `choice` type, but that isn't supported yet
|
||||||
description: The package type to be built. Must be one of 'full' or 'embedded'
|
description: The package type to be built. Must be one of 'full', 'embedded', or 'sdk'
|
||||||
required: true
|
required: true
|
||||||
|
build_mode:
|
||||||
|
type: string # This would ideally be a `choice` type, but that isn't supported yet
|
||||||
|
description: The build mode for vite. Must be either 'development' or 'production'
|
||||||
|
required: false
|
||||||
|
default: production
|
||||||
secrets:
|
secrets:
|
||||||
SENTRY_ORG:
|
SENTRY_ORG:
|
||||||
required: true
|
required: true
|
||||||
@@ -27,30 +32,21 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable
|
run: corepack enable
|
||||||
- name: Yarn cache
|
- name: pnpm cache
|
||||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "pnpm"
|
||||||
node-version-file: ".node-version"
|
node-version-file: ".node-version"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: "yarn install --immutable"
|
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
|
||||||
- name: Build full version
|
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
|
||||||
if: ${{ inputs.package == 'full' }}
|
- name: Build Element Call
|
||||||
run: "yarn run build:full"
|
run: pnpm run build:"$PACKAGE":"$BUILD_MODE"
|
||||||
env:
|
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
|
||||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
|
||||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
VITE_APP_VERSION: ${{ inputs.vite_app_version }}
|
|
||||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
|
||||||
- name: Build embedded
|
|
||||||
if: ${{ inputs.package == 'embedded' }}
|
|
||||||
run: "yarn run build:embedded"
|
|
||||||
env:
|
env:
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
@@ -59,6 +55,8 @@ jobs:
|
|||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
VITE_APP_VERSION: ${{ inputs.vite_app_version }}
|
VITE_APP_VERSION: ${{ inputs.vite_app_version }}
|
||||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||||
|
PACKAGE: ${{ inputs.package }}
|
||||||
|
BUILD_MODE: ${{ inputs.build_mode }}
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
46
.github/workflows/build.yaml
vendored
@@ -14,6 +14,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
package: full
|
package: full
|
||||||
vite_app_version: ${{ github.event.release.tag_name || github.sha }}
|
vite_app_version: ${{ github.event.release.tag_name || github.sha }}
|
||||||
|
build_mode: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'development build') && 'development' || 'production' }}
|
||||||
secrets:
|
secrets:
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
@@ -48,7 +49,9 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write
|
||||||
uses: ./.github/workflows/build-and-publish-docker.yaml
|
uses: ./.github/workflows/build-and-publish-docker.yaml
|
||||||
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
artifact_run_id: ${{ github.run_id }}
|
artifact_run_id: ${{ github.run_id }}
|
||||||
docker_tags: |
|
docker_tags: |
|
||||||
@@ -61,9 +64,52 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
package: embedded
|
package: embedded
|
||||||
vite_app_version: ${{ github.event.release.tag_name || github.sha }}
|
vite_app_version: ${{ github.event.release.tag_name || github.sha }}
|
||||||
|
build_mode: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'development build') && 'development' || 'production' }}
|
||||||
secrets:
|
secrets:
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
build_sdk_element_call:
|
||||||
|
# Use the embedded package vite build
|
||||||
|
uses: ./.github/workflows/build-element-call.yaml
|
||||||
|
with:
|
||||||
|
package: sdk
|
||||||
|
vite_app_version: ${{ github.event.release.tag_name || github.sha }}
|
||||||
|
build_mode: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'development build') && 'development' || 'production' }}
|
||||||
|
secrets:
|
||||||
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
|
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||||
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
build_storybook:
|
||||||
|
name: Build Storybook
|
||||||
|
if: contains(github.event.pull_request.labels.*.name, 'storybook build')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
|
- name: pnpm cache
|
||||||
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
|
with:
|
||||||
|
cache: "pnpm"
|
||||||
|
node-version-file: ".node-version"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
|
||||||
|
- name: Build Storybook
|
||||||
|
run: pnpm run build-storybook
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||||
|
with:
|
||||||
|
name: build-output-storybook
|
||||||
|
path: storybook-static
|
||||||
|
# We'll only use this in a triggered job, then we're done with it
|
||||||
|
retention-days: 1
|
||||||
|
|||||||
22
.github/workflows/changelog-label.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: PR changelog label
|
||||||
|
|
||||||
|
on:
|
||||||
|
# zizmor: ignore[dangerous-triggers]
|
||||||
|
# This is safe because we do not use actions/checkout or execute untrusted code.
|
||||||
|
# Using pull_request_target is necessary to allow status writes for PRs from forks.
|
||||||
|
pull_request_target:
|
||||||
|
types: [labeled, unlabeled, opened, synchronize]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
statuses: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pr-changelog-label:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2
|
||||||
|
with:
|
||||||
|
REQUIRED_LABELS_ANY: "PR-Bug-Fix,PR-Documentation,PR-Task,PR-Feature,PR-Improvement,PR-Developer-Experience,dependencies,PR-Breaking-Change"
|
||||||
|
REQUIRED_LABELS_ANY_DESCRIPTION: "Select at least one 'PR-' label"
|
||||||
|
BANNED_LABELS: "banned"
|
||||||
28
.github/workflows/deploy-to-netlify.yaml
vendored
@@ -14,6 +14,10 @@ on:
|
|||||||
deployment_ref:
|
deployment_ref:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
package:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: Which package to deploy - 'full', 'embedded', 'sdk', or 'storybook'
|
||||||
artifact_run_id:
|
artifact_run_id:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
@@ -39,34 +43,46 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
step: start
|
step: start
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
env: Netlify
|
env: ${{ inputs.package}}
|
||||||
ref: ${{ inputs.deployment_ref }}
|
ref: ${{ inputs.deployment_ref }}
|
||||||
desc: |
|
desc: |
|
||||||
Do you trust the author of this PR? Maybe this build will steal your keys or give you malware.
|
Do you trust the author of this PR? Maybe this build will steal your keys or give you malware.
|
||||||
Exercise caution. Use test accounts.
|
Exercise caution. Use test accounts.
|
||||||
|
|
||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
run-id: ${{ inputs.artifact_run_id }}
|
run-id: ${{ inputs.artifact_run_id }}
|
||||||
name: build-output-full
|
name: build-output-${{ inputs.package }}
|
||||||
path: webapp
|
path: webapp
|
||||||
|
|
||||||
- name: Add redirects file
|
- name: Add redirects file
|
||||||
# We fetch from github directly as we don't bother checking out the repo
|
# We fetch from github directly as we don't bother checking out the repo
|
||||||
|
# Not needed for storybook deployments
|
||||||
|
if: inputs.package != 'storybook'
|
||||||
run: curl -s https://raw.githubusercontent.com/element-hq/element-call/main/config/netlify_redirects > webapp/_redirects
|
run: curl -s https://raw.githubusercontent.com/element-hq/element-call/main/config/netlify_redirects > webapp/_redirects
|
||||||
|
|
||||||
- name: Add config file
|
- name: Add config file
|
||||||
run: curl -s "https://raw.githubusercontent.com/${{ inputs.pr_head_full_name }}/${{ inputs.pr_head_ref }}/config/config_netlify_preview.json" > webapp/config.json
|
# Not needed for storybook deployments
|
||||||
|
if: inputs.package != 'storybook'
|
||||||
|
run: |
|
||||||
|
if [ "${INPUTS_PACKAGE}" = "full" ]; then
|
||||||
|
curl -s "https://raw.githubusercontent.com/${INPUTS_PR_HEAD_FULL_NAME}/${INPUTS_PR_HEAD_REF}/config/config_netlify_preview.json" > webapp/config.json
|
||||||
|
else
|
||||||
|
curl -s "https://raw.githubusercontent.com/${INPUTS_PR_HEAD_FULL_NAME}/${INPUTS_PR_HEAD_REF}/config/config_netlify_preview_sdk.json" > webapp/config.json
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
INPUTS_PACKAGE: ${{ inputs.package }}
|
||||||
|
INPUTS_PR_HEAD_FULL_NAME: ${{ inputs.pr_head_full_name }}
|
||||||
|
INPUTS_PR_HEAD_REF: ${{ inputs.pr_head_ref }}
|
||||||
- name: ☁️ Deploy to Netlify
|
- name: ☁️ Deploy to Netlify
|
||||||
id: netlify
|
id: netlify
|
||||||
uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0
|
uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0
|
||||||
with:
|
with:
|
||||||
publish-dir: webapp
|
publish-dir: webapp
|
||||||
deploy-message: "Deploy from GitHub Actions"
|
deploy-message: "Deploy from GitHub Actions"
|
||||||
alias: pr${{ inputs.pr_number }}
|
alias: ${{ inputs.package == 'sdk' && format('pr{0}-sdk', inputs.pr_number) || inputs.package == 'storybook' && format('pr{0}-storybook', inputs.pr_number) || format('pr{0}', inputs.pr_number) }}
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
|
|||||||
29
.github/workflows/lint.yaml
vendored
@@ -2,28 +2,31 @@ name: Lint, format & type check
|
|||||||
on:
|
on:
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
jobs:
|
jobs:
|
||||||
prettier:
|
lint:
|
||||||
name: Lint, format & type check
|
name: Lint, format & type check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable
|
run: corepack enable
|
||||||
- name: Yarn cache
|
- name: pnpm cache
|
||||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "pnpm"
|
||||||
node-version-file: ".node-version"
|
node-version-file: ".node-version"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: "yarn install --immutable"
|
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
|
||||||
- name: Prettier
|
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
|
||||||
run: "yarn run prettier:check"
|
- name: Formatting
|
||||||
|
run: "pnpm run format:check"
|
||||||
- name: i18n
|
- name: i18n
|
||||||
run: "yarn run i18n:check"
|
run: "pnpm run i18n:check"
|
||||||
- name: ESLint
|
- name: Lint
|
||||||
run: "yarn run lint:eslint"
|
run: "pnpm run lint:oxlint"
|
||||||
- name: Type check
|
- name: Type check
|
||||||
run: "yarn run lint:types"
|
run: "pnpm run lint:types"
|
||||||
- name: Dead code analysis
|
- name: Dead code analysis
|
||||||
run: "yarn run lint:knip"
|
run: "pnpm run lint:knip"
|
||||||
|
|||||||
51
.github/workflows/pr-deploy.yaml
vendored
@@ -1,5 +1,7 @@
|
|||||||
name: Deploy previews for PRs
|
name: Deploy previews for PRs
|
||||||
on:
|
on:
|
||||||
|
# zizmor: ignore[dangerous-triggers]
|
||||||
|
# Reason: This is now restricted to internal PRs only using the 'if' condition below.
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["Build"]
|
workflows: ["Build"]
|
||||||
types:
|
types:
|
||||||
@@ -7,7 +9,14 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prdetails:
|
prdetails:
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
|
# Logic:
|
||||||
|
# 1. Build must be successful
|
||||||
|
# 2. Event must be a pull_request
|
||||||
|
# 3. Head repository must be the SAME as the base repository (No Forks!)
|
||||||
|
if: >
|
||||||
|
github.event.workflow_run.conclusion == 'success' &&
|
||||||
|
github.event.workflow_run.event == 'pull_request' &&
|
||||||
|
github.event.workflow_run.head_repository.full_name == github.repository
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
pr_number: ${{ steps.prdetails.outputs.pr_id }}
|
pr_number: ${{ steps.prdetails.outputs.pr_id }}
|
||||||
@@ -20,7 +29,7 @@ jobs:
|
|||||||
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
||||||
branch: ${{ github.event.workflow_run.head_branch }}
|
branch: ${{ github.event.workflow_run.head_branch }}
|
||||||
|
|
||||||
netlify:
|
netlify-full:
|
||||||
needs: prdetails
|
needs: prdetails
|
||||||
permissions:
|
permissions:
|
||||||
deployments: write
|
deployments: write
|
||||||
@@ -31,6 +40,42 @@ jobs:
|
|||||||
pr_head_full_name: ${{ github.event.workflow_run.head_repository.full_name }}
|
pr_head_full_name: ${{ github.event.workflow_run.head_repository.full_name }}
|
||||||
pr_head_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.ref }}
|
pr_head_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.ref }}
|
||||||
deployment_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.sha || github.ref || github.head_ref }}
|
deployment_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.sha || github.ref || github.head_ref }}
|
||||||
|
package: full
|
||||||
|
secrets:
|
||||||
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
|
|
||||||
|
netlify-sdk:
|
||||||
|
needs: prdetails
|
||||||
|
permissions:
|
||||||
|
deployments: write
|
||||||
|
uses: ./.github/workflows/deploy-to-netlify.yaml
|
||||||
|
with:
|
||||||
|
artifact_run_id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
|
pr_number: ${{ needs.prdetails.outputs.pr_number }}
|
||||||
|
pr_head_full_name: ${{ github.event.workflow_run.head_repository.full_name }}
|
||||||
|
pr_head_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.ref }}
|
||||||
|
deployment_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.sha || github.ref || github.head_ref }}
|
||||||
|
package: sdk
|
||||||
|
secrets:
|
||||||
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
|
|
||||||
|
netlify-storybook:
|
||||||
|
needs: prdetails
|
||||||
|
if: ${{ needs.prdetails.outputs.pr_data_json && contains(fromJSON(needs.prdetails.outputs.pr_data_json).labels.*.name, 'storybook build') }}
|
||||||
|
permissions:
|
||||||
|
deployments: write
|
||||||
|
uses: ./.github/workflows/deploy-to-netlify.yaml
|
||||||
|
with:
|
||||||
|
artifact_run_id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
|
pr_number: ${{ needs.prdetails.outputs.pr_number }}
|
||||||
|
pr_head_full_name: ${{ github.event.workflow_run.head_repository.full_name }}
|
||||||
|
pr_head_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.ref }}
|
||||||
|
deployment_ref: ${{ needs.prdetails.outputs.pr_data_json && fromJSON(needs.prdetails.outputs.pr_data_json).head.sha || github.ref || github.head_ref }}
|
||||||
|
package: storybook
|
||||||
secrets:
|
secrets:
|
||||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
@@ -42,7 +87,9 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write
|
||||||
uses: ./.github/workflows/build-and-publish-docker.yaml
|
uses: ./.github/workflows/build-and-publish-docker.yaml
|
||||||
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
artifact_run_id: ${{ github.event.workflow_run.id || github.run_id }}
|
artifact_run_id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
docker_tags: |
|
docker_tags: |
|
||||||
|
|||||||
192
.github/workflows/publish-embedded-packages.yaml
vendored
@@ -11,19 +11,60 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [livekit]
|
branches: [livekit]
|
||||||
|
|
||||||
env:
|
|
||||||
# We perform a dry run for all events except releases.
|
|
||||||
# This is to help make sure that we notice if the packaging process has become
|
|
||||||
# broken ahead of a release.
|
|
||||||
DRY_RUN: ${{ github.event_name != 'release' }}
|
|
||||||
# We should only use the hard coded test value for a dry run
|
|
||||||
VERSION: ${{ github.event_name == 'release' && github.event.release.tag_name || 'v0.0.0-pre.0' }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
versioning:
|
||||||
|
name: Versioning
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
DRY_RUN: ${{ steps.dry_run.outputs.DRY_RUN }}
|
||||||
|
PREFIXED_VERSION: ${{ steps.prefixed_version.outputs.PREFIXED_VERSION }}
|
||||||
|
UNPREFIXED_VERSION: ${{ steps.unprefixed_version.outputs.UNPREFIXED_VERSION }}
|
||||||
|
TAG: ${{ steps.tag.outputs.TAG }}
|
||||||
|
steps:
|
||||||
|
- name: Calculate VERSION
|
||||||
|
# Safely store dynamic values in environment variables
|
||||||
|
# to prevent shell injection (template-injection)
|
||||||
|
run: |
|
||||||
|
# The logic is executed within the shell using the env variables
|
||||||
|
if [ "$EVENT_NAME" = "release" ]; then
|
||||||
|
echo "VERSION=$RELEASE_TAG" >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
echo "VERSION=v0.0.0-pre.0" >> "$GITHUB_ENV"
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||||
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
|
- id: dry_run
|
||||||
|
name: Set DRY_RUN
|
||||||
|
# We perform a dry run for all events except releases.
|
||||||
|
# This is to help make sure that we notice if the packaging process has become
|
||||||
|
# broken ahead of a release.
|
||||||
|
run: echo "DRY_RUN=${{ github.event_name != 'release' }}" >> "$GITHUB_OUTPUT"
|
||||||
|
- id: prefixed_version
|
||||||
|
name: Set PREFIXED_VERSION
|
||||||
|
run: echo "PREFIXED_VERSION=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
- id: unprefixed_version
|
||||||
|
name: Set UNPREFIXED_VERSION
|
||||||
|
# This just strips the leading character
|
||||||
|
run: echo "UNPREFIXED_VERSION=${VERSION:1}" >> "$GITHUB_OUTPUT"
|
||||||
|
- id: tag
|
||||||
|
# latest = a proper release
|
||||||
|
# other = anything else
|
||||||
|
name: Set tag
|
||||||
|
run: |
|
||||||
|
if [[ "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "TAG=latest" >> "$GITHUB_OUTPUT"
|
||||||
|
elif [[ "${VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+\-rc\.[0-9]+$ ]]; then
|
||||||
|
echo "TAG=rc" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "TAG=other" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
build_element_call:
|
build_element_call:
|
||||||
|
needs: versioning
|
||||||
uses: ./.github/workflows/build-element-call.yaml
|
uses: ./.github/workflows/build-element-call.yaml
|
||||||
with:
|
with:
|
||||||
vite_app_version: embedded-${{ github.event.release.tag_name || 'v0.0.0-pre.0' }} # Using ${{ env.VERSION }} here doesn't work
|
vite_app_version: embedded-${{ needs.versioning.outputs.PREFIXED_VERSION }}
|
||||||
package: embedded
|
package: embedded
|
||||||
secrets:
|
secrets:
|
||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
@@ -32,7 +73,7 @@ jobs:
|
|||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
|
|
||||||
publish_tarball:
|
publish_tarball:
|
||||||
needs: build_element_call
|
needs: [build_element_call, versioning]
|
||||||
if: always()
|
if: always()
|
||||||
name: Publish tarball
|
name: Publish tarball
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -40,40 +81,46 @@ jobs:
|
|||||||
contents: write # required to upload release asset
|
contents: write # required to upload release asset
|
||||||
steps:
|
steps:
|
||||||
- name: Determine filename
|
- name: Determine filename
|
||||||
run: echo "FILENAME_PREFIX=element-call-embedded-${VERSION:1}" >> "$GITHUB_ENV"
|
run: echo "FILENAME_PREFIX=element-call-embedded-${NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION}" >> "$GITHUB_ENV"
|
||||||
|
env:
|
||||||
|
NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION: ${{ needs.versioning.outputs.UNPREFIXED_VERSION }}
|
||||||
- name: 📥 Download built element-call artifact
|
- name: 📥 Download built element-call artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
name: build-output-embedded
|
name: build-output-embedded
|
||||||
path: ${{ env.FILENAME_PREFIX}}
|
path: ${{ env.FILENAME_PREFIX}}
|
||||||
- name: Create Tarball
|
- name: Create Tarball
|
||||||
run: tar --numeric-owner -cvzf ${{ env.FILENAME_PREFIX }}.tar.gz ${{ env.FILENAME_PREFIX }}
|
run: tar --numeric-owner -cvzf ${FILENAME_PREFIX}.tar.gz ${FILENAME_PREFIX}
|
||||||
- name: Create Checksum
|
- name: Create Checksum
|
||||||
run: find ${{ env.FILENAME_PREFIX }} -type f -print0 | sort -z | xargs -0 sha256sum | tee ${{ env.FILENAME_PREFIX }}.sha256
|
run: find ${FILENAME_PREFIX} -type f -print0 | sort -z | xargs -0 sha256sum | tee ${FILENAME_PREFIX}.sha256
|
||||||
- name: Upload
|
- name: Upload
|
||||||
if: ${{ env.DRY_RUN == 'false' }}
|
if: ${{ needs.versioning.outputs.DRY_RUN == 'false' }}
|
||||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
${{ env.FILENAME_PREFIX }}.tar.gz
|
${{ env.FILENAME_PREFIX }}.tar.gz
|
||||||
${{ env.FILENAME_PREFIX }}.sha256
|
${{ env.FILENAME_PREFIX }}.sha256
|
||||||
|
|
||||||
publish_npm:
|
publish_npm:
|
||||||
needs: build_element_call
|
needs: [build_element_call, versioning]
|
||||||
if: always()
|
if: always()
|
||||||
name: Publish NPM
|
name: Publish NPM
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
ARTIFACT_VERSION: ${{ steps.artifact_version.outputs.ARTIFACT_VERSION }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write # required for the provenance flag on npm publish
|
id-token: write # Allow npm to authenticate as a trusted publisher
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: 📥 Download built element-call artifact
|
- name: 📥 Download built element-call artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
@@ -82,7 +129,7 @@ jobs:
|
|||||||
|
|
||||||
# n.b. We don't enable corepack here because we are using plain npm
|
# n.b. We don't enable corepack here because we are using plain npm
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
node-version-file: .node-version
|
node-version-file: .node-version
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
@@ -90,25 +137,34 @@ jobs:
|
|||||||
- name: Publish npm
|
- name: Publish npm
|
||||||
working-directory: embedded/web
|
working-directory: embedded/web
|
||||||
run: |
|
run: |
|
||||||
npm version ${{ env.VERSION }} --no-git-tag-version
|
npm version ${NEEDS_VERSIONING_OUTPUTS_PREFIXED_VERSION} --no-git-tag-version
|
||||||
echo "ARTIFACT_VERSION=$(jq '.version' --raw-output package.json)" >> "$GITHUB_ENV"
|
echo "ARTIFACT_VERSION=$(jq '.version' --raw-output package.json)" >> "$GITHUB_ENV"
|
||||||
npm publish --provenance --access public ${{ env.DRY_RUN == 'true' && '--dry-run' || '' }}
|
npm publish --provenance --access public --tag ${NEEDS_VERSIONING_OUTPUTS_TAG} ${{ needs.versioning.outputs.DRY_RUN == 'true' && '--dry-run' || '' }}
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_RELEASE_TOKEN }}
|
NEEDS_VERSIONING_OUTPUTS_PREFIXED_VERSION: ${{ needs.versioning.outputs.PREFIXED_VERSION }}
|
||||||
|
NEEDS_VERSIONING_OUTPUTS_TAG: ${{ needs.versioning.outputs.TAG }}
|
||||||
|
|
||||||
|
- id: artifact_version
|
||||||
|
name: Output artifact version
|
||||||
|
run: echo "ARTIFACT_VERSION=${ARTIFACT_VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
publish_android:
|
publish_android:
|
||||||
needs: build_element_call
|
needs: [build_element_call, versioning]
|
||||||
if: always()
|
if: always()
|
||||||
name: Publish Android AAR
|
name: Publish Android AAR
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
ARTIFACT_VERSION: ${{ steps.artifact_version.outputs.ARTIFACT_VERSION }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: 📥 Download built element-call artifact
|
- name: 📥 Download built element-call artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
@@ -116,16 +172,27 @@ jobs:
|
|||||||
path: embedded/android/lib/src/main/assets/element-call
|
path: embedded/android/lib/src/main/assets/element-call
|
||||||
|
|
||||||
- name: ☕️ Setup Java
|
- name: ☕️ Setup Java
|
||||||
uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 # v4
|
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
|
||||||
with:
|
with:
|
||||||
distribution: "temurin"
|
distribution: "temurin"
|
||||||
java-version: "17"
|
java-version: "17"
|
||||||
|
|
||||||
- name: Get artifact version
|
- name: Get artifact version
|
||||||
run: echo "ARTIFACT_VERSION=${VERSION:1}" >> "$GITHUB_ENV"
|
# Anything that is not a final release will be tagged as a snapshot
|
||||||
|
run: |
|
||||||
|
if [[ "${NEEDS_VERSIONING_OUTPUTS_TAG}" == "latest" ]]; then
|
||||||
|
echo "ARTIFACT_VERSION=${NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION}" >> "$GITHUB_ENV"
|
||||||
|
elif [[ "${NEEDS_VERSIONING_OUTPUTS_TAG}" == "rc" ]]; then
|
||||||
|
echo "ARTIFACT_VERSION=${NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION}" >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
echo "ARTIFACT_VERSION=${NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION}-SNAPSHOT" >> "$GITHUB_ENV"
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
NEEDS_VERSIONING_OUTPUTS_TAG: ${{ needs.versioning.outputs.TAG }}
|
||||||
|
NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION: ${{ needs.versioning.outputs.UNPREFIXED_VERSION }}
|
||||||
|
|
||||||
- name: Set version string
|
- name: Set version string
|
||||||
run: sed -i "s/0.0.0/${{ env.ARTIFACT_VERSION }}/g" embedded/android/lib/src/main/kotlin/io/element/android/call/embedded/Version.kt
|
run: sed -i "s/0.0.0/${ARTIFACT_VERSION}/g" embedded/android/lib/src/main/kotlin/io/element/android/call/embedded/Version.kt
|
||||||
|
|
||||||
- name: Publish AAR
|
- name: Publish AAR
|
||||||
working-directory: embedded/android
|
working-directory: embedded/android
|
||||||
@@ -135,23 +202,30 @@ jobs:
|
|||||||
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_RELEASE_PASSWORD }}
|
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_RELEASE_PASSWORD }}
|
||||||
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SIGNING_KEY }}
|
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SIGNING_KEY }}
|
||||||
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SIGNING_KEY_PASSWORD }}
|
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SIGNING_KEY_PASSWORD }}
|
||||||
run: ./gradlew publishAndReleaseToMavenCentral --no-daemon ${{ env.DRY_RUN == 'true' && '--dry-run' || '' }}
|
run: ./gradlew publishToMavenCentral --no-daemon ${{ needs.versioning.outputs.DRY_RUN == 'true' && '--dry-run' || '' }}
|
||||||
|
|
||||||
|
- id: artifact_version
|
||||||
|
name: Output artifact version
|
||||||
|
run: echo "ARTIFACT_VERSION=${ARTIFACT_VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
publish_ios:
|
publish_ios:
|
||||||
needs: build_element_call
|
needs: [build_element_call, versioning]
|
||||||
if: always()
|
if: always()
|
||||||
name: Publish SwiftPM Library
|
name: Publish SwiftPM Library
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
ARTIFACT_VERSION: ${{ steps.artifact_version.outputs.ARTIFACT_VERSION }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
with:
|
with:
|
||||||
path: element-call
|
path: element-call
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: 📥 Download built element-call artifact
|
- name: 📥 Download built element-call artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
@@ -159,20 +233,22 @@ jobs:
|
|||||||
path: element-call/embedded/ios/Sources/dist
|
path: element-call/embedded/ios/Sources/dist
|
||||||
|
|
||||||
- name: Checkout element-call-swift
|
- name: Checkout element-call-swift
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
with:
|
with:
|
||||||
repository: element-hq/element-call-swift
|
repository: element-hq/element-call-swift
|
||||||
path: element-call-swift
|
path: element-call-swift
|
||||||
token: ${{ secrets.SWIFT_RELEASE_TOKEN }}
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Copy files
|
- name: Copy files
|
||||||
run: rsync -a --delete --exclude .git element-call/embedded/ios/ element-call-swift
|
run: rsync -a --delete --exclude .git element-call/embedded/ios/ element-call-swift
|
||||||
|
|
||||||
- name: Get artifact version
|
- name: Get artifact version
|
||||||
run: echo "ARTIFACT_VERSION=${VERSION:1}" >> "$GITHUB_ENV"
|
run: echo "ARTIFACT_VERSION=${NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION}" >> "$GITHUB_ENV"
|
||||||
|
env:
|
||||||
|
NEEDS_VERSIONING_OUTPUTS_UNPREFIXED_VERSION: ${{ needs.versioning.outputs.UNPREFIXED_VERSION }}
|
||||||
|
|
||||||
- name: Set version string
|
- name: Set version string
|
||||||
run: sed -i "s/0.0.0/${{ env.ARTIFACT_VERSION }}/g" element-call-swift/Sources/EmbeddedElementCall/EmbeddedElementCall.swift
|
run: sed -i "s/0.0.0/${ARTIFACT_VERSION}/g" element-call-swift/Sources/EmbeddedElementCall/EmbeddedElementCall.swift
|
||||||
|
|
||||||
- name: Test build
|
- name: Test build
|
||||||
working-directory: element-call-swift
|
working-directory: element-call-swift
|
||||||
@@ -184,28 +260,44 @@ jobs:
|
|||||||
git config --global user.email "ci@element.io"
|
git config --global user.email "ci@element.io"
|
||||||
git config --global user.name "Element CI"
|
git config --global user.name "Element CI"
|
||||||
git add -A
|
git add -A
|
||||||
git commit -am "Release ${{ env.VERSION }}"
|
git commit -am "Release ${NEEDS_VERSIONING_OUTPUTS_PREFIXED_VERSION}"
|
||||||
git tag -a ${{ env.ARTIFACT_VERSION }} -m "${{ github.event.release.html_url }}"
|
git tag -a ${ARTIFACT_VERSION} -m "${GITHUB_EVENT_RELEASE_HTML_URL}"
|
||||||
|
env:
|
||||||
|
NEEDS_VERSIONING_OUTPUTS_PREFIXED_VERSION: ${{ needs.versioning.outputs.PREFIXED_VERSION }}
|
||||||
|
GITHUB_EVENT_RELEASE_HTML_URL: ${{ github.event.release.html_url }}
|
||||||
|
|
||||||
- name: Push
|
- name: Push
|
||||||
|
if: ${{ needs.versioning.outputs.DRY_RUN == 'false' }}
|
||||||
working-directory: element-call-swift
|
working-directory: element-call-swift
|
||||||
run: |
|
run: |
|
||||||
git push --tags ${{ env.DRY_RUN == 'true' && '--dry-run' || '' }}
|
git push "https://x-access-token:${SWIFT_RELEASE_TOKEN}@github.com/element-hq/element-call-swift.git" --tags
|
||||||
|
env:
|
||||||
|
SWIFT_RELEASE_TOKEN: ${{ secrets.SWIFT_RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
- id: artifact_version
|
||||||
|
name: Output artifact version
|
||||||
|
run: echo "ARTIFACT_VERSION=${ARTIFACT_VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
release_notes:
|
release_notes:
|
||||||
needs: [publish_npm, publish_android, publish_ios]
|
needs: [versioning, publish_npm, publish_android, publish_ios]
|
||||||
if: always()
|
if: always()
|
||||||
name: Update release notes
|
name: Update release notes
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # to update release notes
|
contents: write # to update release notes
|
||||||
steps:
|
steps:
|
||||||
- name: Get artifact version
|
- name: Log versions
|
||||||
run: echo "ARTIFACT_VERSION=${VERSION:1}" >> "$GITHUB_ENV"
|
run: |
|
||||||
|
echo "NPM: ${NEEDS_PUBLISH_NPM_OUTPUTS_ARTIFACT_VERSION}"
|
||||||
|
echo "Android: ${NEEDS_PUBLISH_ANDROID_OUTPUTS_ARTIFACT_VERSION}"
|
||||||
|
echo "iOS: ${NEEDS_PUBLISH_IOS_OUTPUTS_ARTIFACT_VERSION}"
|
||||||
|
env:
|
||||||
|
NEEDS_PUBLISH_NPM_OUTPUTS_ARTIFACT_VERSION: ${{ needs.publish_npm.outputs.ARTIFACT_VERSION }}
|
||||||
|
NEEDS_PUBLISH_ANDROID_OUTPUTS_ARTIFACT_VERSION: ${{ needs.publish_android.outputs.ARTIFACT_VERSION }}
|
||||||
|
NEEDS_PUBLISH_IOS_OUTPUTS_ARTIFACT_VERSION: ${{ needs.publish_ios.outputs.ARTIFACT_VERSION }}
|
||||||
- name: Add release notes
|
- name: Add release notes
|
||||||
if: ${{ env.DRY_RUN == 'false' }}
|
if: ${{ needs.versioning.outputs.DRY_RUN == 'false' }}
|
||||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
||||||
with:
|
with:
|
||||||
append_body: true
|
append_body: true
|
||||||
body: |
|
body: |
|
||||||
@@ -218,19 +310,19 @@ jobs:
|
|||||||
### NPM
|
### NPM
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install @element-hq/element-call-embedded@${{ env.ARTIFACT_VERSION }}
|
npm install @element-hq/element-call-embedded@${{ needs.publish_npm.outputs.ARTIFACT_VERSION }}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Android AAR
|
### Android AAR
|
||||||
|
|
||||||
```
|
```
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'io.element.android:element-call-embedded:${{ env.ARTIFACT_VERSION }}'
|
implementation 'io.element.android:element-call-embedded:${{ needs.publish_android.outputs.ARTIFACT_VERSION }}'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### SwiftPM
|
### SwiftPM
|
||||||
|
|
||||||
```
|
```
|
||||||
.package(url: "https://github.com/element-hq/element-call-swift.git", from: "${{ env.ARTIFACT_VERSION }}")
|
.package(url: "https://github.com/element-hq/element-call-swift.git", from: "${{ needs.publish_ios.outputs.ARTIFACT_VERSION }}")
|
||||||
```
|
```
|
||||||
|
|||||||
20
.github/workflows/publish.yaml
vendored
@@ -31,18 +31,18 @@ jobs:
|
|||||||
- name: Determine filename
|
- name: Determine filename
|
||||||
run: echo "FILENAME_PREFIX=element-call-${VERSION:1}" >> "$GITHUB_ENV"
|
run: echo "FILENAME_PREFIX=element-call-${VERSION:1}" >> "$GITHUB_ENV"
|
||||||
- name: 📥 Download built element-call artifact
|
- name: 📥 Download built element-call artifact
|
||||||
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
run-id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
name: build-output-full
|
name: build-output-full
|
||||||
path: ${{ env.FILENAME_PREFIX }}
|
path: ${{ env.FILENAME_PREFIX }}
|
||||||
- name: Create Tarball
|
- name: Create Tarball
|
||||||
run: tar --numeric-owner --transform "s/dist/${{ env.FILENAME_PREFIX }}/" -cvzf ${{ env.FILENAME_PREFIX }}.tar.gz ${{ env.FILENAME_PREFIX }}
|
run: tar --numeric-owner --transform "s/dist/${FILENAME_PREFIX}/" -cvzf ${FILENAME_PREFIX}.tar.gz ${FILENAME_PREFIX}
|
||||||
- name: Create Checksum
|
- name: Create Checksum
|
||||||
run: find ${{ env.FILENAME_PREFIX }} -type f -print0 | sort -z | xargs -0 sha256sum | tee ${{ env.FILENAME_PREFIX }}.sha256
|
run: find ${FILENAME_PREFIX} -type f -print0 | sort -z | xargs -0 sha256sum | tee ${FILENAME_PREFIX}.sha256
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
${{ env.FILENAME_PREFIX }}.tar.gz
|
${{ env.FILENAME_PREFIX }}.tar.gz
|
||||||
@@ -55,21 +55,23 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write
|
||||||
uses: ./.github/workflows/build-and-publish-docker.yaml
|
uses: ./.github/workflows/build-and-publish-docker.yaml
|
||||||
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
artifact_run_id: ${{ github.event.workflow_run.id || github.run_id }}
|
artifact_run_id: ${{ github.event.workflow_run.id || github.run_id }}
|
||||||
docker_tags: |
|
docker_tags: |
|
||||||
type=sha,format=short,event=branch
|
type=sha,format=short,event=branch
|
||||||
type=semver,pattern=v{{version}}
|
type=raw,value=${{ github.event.release.tag_name }}
|
||||||
|
type=raw,value=latest
|
||||||
|
# Like before, using ${{ env.VERSION }} above doesn't work
|
||||||
add_docker_release_note:
|
add_docker_release_note:
|
||||||
needs: publish_docker
|
needs: publish_docker
|
||||||
name: Add docker release note
|
name: Add docker release note
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Get artifact version
|
|
||||||
run: echo "ARTIFACT_VERSION=${VERSION:1}" >> "$GITHUB_ENV"
|
|
||||||
- name: Add release note
|
- name: Add release note
|
||||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
|
||||||
with:
|
with:
|
||||||
append_body: true
|
append_body: true
|
||||||
body: |
|
body: |
|
||||||
@@ -79,5 +81,5 @@ jobs:
|
|||||||
The image provides a full build of Element Call that can be used both in standalone and as a widget (on a remote URL).
|
The image provides a full build of Element Call that can be used both in standalone and as a widget (on a remote URL).
|
||||||
|
|
||||||
```
|
```
|
||||||
docker pull ghcr.io/element-hq/element-call:${{ env.ARTIFACT_VERSION }}
|
docker pull ghcr.io/element-hq/element-call:${{ env.VERSION }}
|
||||||
```
|
```
|
||||||
|
|||||||
23
.github/workflows/sync-labels.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Sync labels
|
||||||
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- livekit
|
||||||
|
paths:
|
||||||
|
- .github/labels.yml
|
||||||
|
- .github/workflows/sync-labels.yml
|
||||||
|
|
||||||
|
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-labels:
|
||||||
|
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@7f2f93fb9b52ece7a0998f60e64862aa203c1746
|
||||||
|
with:
|
||||||
|
LABELS: |
|
||||||
|
.github/labels.yml
|
||||||
|
DELETE: true
|
||||||
|
WET: true
|
||||||
|
secrets:
|
||||||
|
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
48
.github/workflows/test-netlify.yaml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Triggers after the playwright tests have finished,
|
||||||
|
# taking the artifact and uploading it to Netlify for easier viewing
|
||||||
|
name: Upload End to End Test report to Netlify
|
||||||
|
on:
|
||||||
|
# Privilege escalation necessary to publish to Netlify
|
||||||
|
# 🚨 We must not execute any checked out code here.
|
||||||
|
workflow_run: # zizmor: ignore[dangerous-triggers]
|
||||||
|
workflows: ["Test"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }}
|
||||||
|
cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
report:
|
||||||
|
if: github.event.workflow_run.conclusion != 'cancelled'
|
||||||
|
name: Report results
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
environment: Netlify
|
||||||
|
permissions:
|
||||||
|
statuses: write
|
||||||
|
deployments: write
|
||||||
|
actions: read
|
||||||
|
steps:
|
||||||
|
- name: Download HTML report
|
||||||
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
name: html-report
|
||||||
|
path: playwright-report
|
||||||
|
|
||||||
|
- name: 📤 Deploy to Netlify
|
||||||
|
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
|
||||||
|
with:
|
||||||
|
path: playwright-report
|
||||||
|
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
||||||
|
branch: ${{ github.event.workflow_run.head_branch }}
|
||||||
|
revision: ${{ github.event.workflow_run.head_sha }}
|
||||||
|
token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
site_id: ${{ secrets.NETLIFY_SITE_ID }}
|
||||||
|
desc: Playwright Report
|
||||||
|
deployment_env: EndToEndTests
|
||||||
|
prefix: "e2e-"
|
||||||
56
.github/workflows/test.yaml
vendored
@@ -2,27 +2,34 @@ name: Test
|
|||||||
on:
|
on:
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
push:
|
push:
|
||||||
branches: [livekit, full-mesh]
|
branches: [livekit]
|
||||||
jobs:
|
jobs:
|
||||||
vitest:
|
vitest:
|
||||||
name: Run unit tests
|
name: Run unit tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
# Make sure to grab the latest version of the Playwright image
|
||||||
|
# https://playwright.dev/docs/docker#pull-the-image
|
||||||
|
image: mcr.microsoft.com/playwright:v1.61.1-noble
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable
|
run: corepack enable
|
||||||
- name: Yarn cache
|
- name: pnpm cache
|
||||||
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "pnpm"
|
||||||
node-version-file: ".node-version"
|
node-version-file: ".node-version"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: "yarn install --immutable"
|
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
|
||||||
|
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
|
||||||
- name: Vitest
|
- name: Vitest
|
||||||
run: "yarn run test:coverage"
|
run: "pnpm run test:coverage"
|
||||||
- name: Upload to codecov
|
- name: Upload to codecov
|
||||||
uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5
|
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
with:
|
with:
|
||||||
@@ -30,31 +37,36 @@ jobs:
|
|||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
playwright:
|
playwright:
|
||||||
name: Run end-to-end tests
|
name: Run end-to-end tests
|
||||||
timeout-minutes: 10
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable
|
run: corepack enable
|
||||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "pnpm"
|
||||||
node-version-file: ".node-version"
|
node-version-file: ".node-version"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install --immutable
|
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
|
||||||
|
run: pnpm install --frozen-lockfile --ignore-pnpmfile
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: yarn playwright install --with-deps
|
run: pnpm exec playwright install --with-deps
|
||||||
- name: Run backend components
|
- name: Run backend components
|
||||||
run: |
|
run: |
|
||||||
docker compose -f playwright-backend-docker-compose.yml up -d
|
docker compose -f docker-compose-dev.yml -f docker-compose-playwright.yml pull
|
||||||
|
docker compose -f docker-compose-dev.yml -f docker-compose-playwright.yml up -d
|
||||||
docker ps
|
docker ps
|
||||||
- name: Copy config file
|
|
||||||
run: cp config/config.devenv.json public/config.json
|
|
||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
run: yarn playwright test
|
env:
|
||||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
USE_DOCKER: 1
|
||||||
|
run: pnpm exec playwright test
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report
|
name: html-report
|
||||||
path: playwright-report/
|
path: playwright-report
|
||||||
retention-days: 3
|
if-no-files-found: error
|
||||||
|
retention-days: 4
|
||||||
|
|||||||
24
.github/workflows/translations-download.yaml
vendored
@@ -13,18 +13,21 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the code
|
- name: Checkout the code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Enable Corepack
|
- name: Enable Corepack
|
||||||
run: corepack enable
|
run: corepack enable
|
||||||
|
|
||||||
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
cache: "yarn"
|
cache: "pnpm"
|
||||||
node-version-file: ".node-version"
|
node-version-file: ".node-version"
|
||||||
|
|
||||||
- name: Install Deps
|
- name: Install Deps
|
||||||
run: "yarn install --immutable"
|
# ignore-pnpmfile should never be commited. Make CI crash if it happened (`pnpmfileChecksum` is present)
|
||||||
|
run: "pnpm install --frozen-lockfile --ignore-pnpmfile"
|
||||||
|
|
||||||
- name: Prune i18n
|
- name: Prune i18n
|
||||||
run: "rm -R locales"
|
run: "rm -R locales"
|
||||||
@@ -37,12 +40,17 @@ jobs:
|
|||||||
- name: Fix the owner of the downloaded files
|
- name: Fix the owner of the downloaded files
|
||||||
run: "sudo chown runner:docker -R locales"
|
run: "sudo chown runner:docker -R locales"
|
||||||
|
|
||||||
- name: Prettier
|
# Localazy doesn't write file contents in the same order that i18next-cli's
|
||||||
run: yarn prettier:format
|
# extractor uses. We re-run the extractor after downloading to fix the order.
|
||||||
|
- name: i18n
|
||||||
|
run: pnpm i18n
|
||||||
|
|
||||||
|
- name: Formatting
|
||||||
|
run: pnpm format
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||||
branch: actions/localazy-download
|
branch: actions/localazy-download
|
||||||
@@ -50,7 +58,7 @@ jobs:
|
|||||||
title: Localazy Download
|
title: Localazy Download
|
||||||
commit-message: Translations updates
|
commit-message: Translations updates
|
||||||
labels: |
|
labels: |
|
||||||
T-Task
|
PR-Task
|
||||||
|
|
||||||
- name: Enable automerge
|
- name: Enable automerge
|
||||||
run: gh pr merge --merge --auto "$PR_NUMBER"
|
run: gh pr merge --merge --auto "$PR_NUMBER"
|
||||||
|
|||||||
4
.github/workflows/translations-upload.yaml
vendored
@@ -14,7 +14,9 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the code
|
- name: Checkout the code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Upload
|
- name: Upload
|
||||||
uses: localazy/upload@27e6b5c0fddf4551596b42226b1c24124335d24a # v1
|
uses: localazy/upload@27e6b5c0fddf4551596b42226b1c24124335d24a # v1
|
||||||
|
|||||||
23
.github/workflows/zizmor.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: GitHub Actions Security Analysis with zizmor 🌈
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["livekit", "full-mesh"]
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
zizmor:
|
||||||
|
name: Run zizmor 🌈
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Run zizmor 🌈
|
||||||
|
uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
|
||||||
11
.gitignore
vendored
@@ -8,7 +8,9 @@ dist-ssr
|
|||||||
.idea/
|
.idea/
|
||||||
public/config.json
|
public/config.json
|
||||||
backend/synapse_tmp/*
|
backend/synapse_tmp/*
|
||||||
|
backend/synapse_tmp_othersite/*
|
||||||
/coverage
|
/coverage
|
||||||
|
config.json
|
||||||
|
|
||||||
# Yarn
|
# Yarn
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
@@ -19,11 +21,20 @@ yarn-error.log
|
|||||||
!/.yarn/releases
|
!/.yarn/releases
|
||||||
!/.yarn/sdks
|
!/.yarn/sdks
|
||||||
!/.yarn/versions
|
!/.yarn/versions
|
||||||
|
# old yarn based linking
|
||||||
/.links.yaml
|
/.links.yaml
|
||||||
/.links.disabled.yaml
|
/.links.disabled.yaml
|
||||||
|
/.links.temp-disabled.yaml
|
||||||
|
# pnpm based linking
|
||||||
|
/.links.cjs
|
||||||
|
/.links.disabled.cjs
|
||||||
|
/.links.temp-disabled.cjs
|
||||||
|
|
||||||
# Playwright
|
# Playwright
|
||||||
/test-results/
|
/test-results/
|
||||||
/playwright-report/
|
/playwright-report/
|
||||||
/blob-report/
|
/blob-report/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
|
|
||||||
|
*storybook.log
|
||||||
|
storybook-static
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22
|
24
|
||||||
|
|||||||
6
.oxfmtrc.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
||||||
|
"printWidth": 80,
|
||||||
|
"sortPackageJson": false,
|
||||||
|
"ignorePatterns": ["pnpm-lock.yaml", "node_modules", "dist"]
|
||||||
|
}
|
||||||
154
.oxlintrc.json
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
|
"plugins": [
|
||||||
|
"eslint",
|
||||||
|
"import",
|
||||||
|
"jsdoc",
|
||||||
|
"jsx-a11y",
|
||||||
|
"promise",
|
||||||
|
"react",
|
||||||
|
"typescript",
|
||||||
|
"unicorn",
|
||||||
|
"vitest"
|
||||||
|
],
|
||||||
|
"jsPlugins": [
|
||||||
|
"eslint-plugin-storybook",
|
||||||
|
"eslint-plugin-element-call"
|
||||||
|
// TODO: Re-enable once oxlint supports lint rules that rely on TypeScript type-awareness.
|
||||||
|
// "eslint-plugin-rxjs"
|
||||||
|
],
|
||||||
|
"categories": {
|
||||||
|
"correctness": "error",
|
||||||
|
"perf": "error"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"denyWarnings": true,
|
||||||
|
"typeAware": true
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"builtin": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"element-call/copyright-header": [
|
||||||
|
"error",
|
||||||
|
"/*\nCopyright %%CURRENT_YEAR%% Element Creations Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE in the repository root for full details.\n*/\n\n"
|
||||||
|
],
|
||||||
|
"element-call/no-observablescope-leak": "error",
|
||||||
|
"element-call/no-top-level-logger-get-child": "error",
|
||||||
|
"jsdoc/empty-tags": "error",
|
||||||
|
"jsdoc/check-property-names": "error",
|
||||||
|
"jsdoc/require-param-description": "warn",
|
||||||
|
"react/display-name": "error",
|
||||||
|
// TODO: Re-enable once oxlint supports lint rules that rely on TypeScript type-awareness.
|
||||||
|
// "rxjs/no-exposed-subjects": "error",
|
||||||
|
// "rxjs/finnish": [
|
||||||
|
// "error",
|
||||||
|
// {
|
||||||
|
// "names": {
|
||||||
|
// "^this$": false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"group": [
|
||||||
|
"matrix-widget-api/src",
|
||||||
|
"matrix-widget-api/src/",
|
||||||
|
"matrix-widget-api/src/**",
|
||||||
|
"matrix-widget-api/lib",
|
||||||
|
"matrix-widget-api/lib/",
|
||||||
|
"matrix-widget-api/lib/**"
|
||||||
|
],
|
||||||
|
"message": "Please use matrix-widget-api instead"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": [
|
||||||
|
"matrix-js-sdk/src",
|
||||||
|
"matrix-js-sdk/src/",
|
||||||
|
"matrix-js-sdk/src/**",
|
||||||
|
"matrix-js-sdk/lib",
|
||||||
|
"matrix-js-sdk/lib/",
|
||||||
|
"matrix-js-sdk/lib/index"
|
||||||
|
],
|
||||||
|
"message": "Please use matrix-js-sdk instead"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typescript/no-floating-promises": "error",
|
||||||
|
"typescript/no-misused-promises": "error",
|
||||||
|
"typescript/promise-function-async": "error",
|
||||||
|
"typescript/require-await": "error",
|
||||||
|
"typescript/await-thenable": "error",
|
||||||
|
// To help ensure that we get proper vite/rollup lazy loading (e.g. for matrix-js-sdk).
|
||||||
|
"typescript/consistent-type-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"fixStyle": "inline-type-imports"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// TODO: These had to be disabled in the eslint -> oxlint migration. Would be nice to
|
||||||
|
// enable them in future or at least document why we're disabling them.
|
||||||
|
"eslint/no-await-in-loop": "off",
|
||||||
|
"eslint/no-unused-vars": ["error", { "args": "none" }],
|
||||||
|
"import/default": "off",
|
||||||
|
"jsdoc/check-tag-names": "off",
|
||||||
|
"jsx-a11y/prefer-tag-over-role": "off",
|
||||||
|
"promise/no-callback-in-promise": "off",
|
||||||
|
"react/jsx-key": "off",
|
||||||
|
"react/jsx-no-constructed-context-values": "off",
|
||||||
|
"react/no-array-index-key": "off",
|
||||||
|
"react/no-children-prop": "off",
|
||||||
|
"react/no-object-type-as-default-prop": "off",
|
||||||
|
"typescript/no-misused-spread": "off",
|
||||||
|
"typescript/no-useless-default-assignment": "off",
|
||||||
|
"typescript/restrict-template-expressions": "off",
|
||||||
|
"typescript/unbound-method": "off",
|
||||||
|
"vitest/expect-expect": "off",
|
||||||
|
"vitest/no-conditional-expect": "off",
|
||||||
|
"vitest/no-disabled-tests": "off",
|
||||||
|
"vitest/require-mock-type-parameters": "off",
|
||||||
|
"vitest/require-to-throw-message": "off"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["src/*/**"],
|
||||||
|
"rules": {
|
||||||
|
// In application code we should use the js-sdk logger, never console directly.
|
||||||
|
"no-console": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.test.ts",
|
||||||
|
"**/*.test.tsx",
|
||||||
|
"**/test.ts",
|
||||||
|
"**/test.tsx",
|
||||||
|
"**/test-**"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
// Tests often initialize an ObservableScope in an outer scope in
|
||||||
|
// beforeEach, which is not actually a problem
|
||||||
|
"element-call/no-observablescope-leak": "off",
|
||||||
|
"jsdoc/empty-tags": "off",
|
||||||
|
"jsdoc/check-property-names": "off",
|
||||||
|
"jsdoc/require-param-description": "off",
|
||||||
|
"jsx-a11y/media-has-caption": "off"
|
||||||
|
// TODO: Enable once oxlint supports them.
|
||||||
|
// "jsdoc/check-values": "off",
|
||||||
|
// "jsdoc/check-param-names": "off",
|
||||||
|
// "jsdoc/no-types": "off",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["playwright/**"],
|
||||||
|
"rules": {
|
||||||
|
// Playwright as a `use` function that has nothing to do with React hooks.
|
||||||
|
"react-hooks/rules-of-hooks": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
36
.storybook/main.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { StorybookConfig } from "@storybook/react-vite";
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||||
|
addons: ["@storybook/addon-docs", "@storybook/addon-vitest"],
|
||||||
|
framework: "@storybook/react-vite",
|
||||||
|
// THIS IS IMPORTANT
|
||||||
|
// vitest runs without Vite's normal dependency optimization, so we need to manually include the polyfills for the stories to work.
|
||||||
|
// otherwise we will get: new dependencies optimized: ...
|
||||||
|
// and
|
||||||
|
// ```
|
||||||
|
// [vitest] Vite unexpectedly reloaded a test. This may cause tests to fail, lead to flaky behaviour or duplicated test runs.
|
||||||
|
// For a stable experience, please add mentioned dependencies to your config's `optimizeDeps.include` field manually.
|
||||||
|
// ```
|
||||||
|
// which breaks the storybook ci on the first and only run.
|
||||||
|
viteFinal(config) {
|
||||||
|
config.optimizeDeps = {
|
||||||
|
...config.optimizeDeps,
|
||||||
|
include: [
|
||||||
|
...(config.optimizeDeps?.include ?? []),
|
||||||
|
"vite-plugin-node-polyfills/shims/buffer",
|
||||||
|
"vite-plugin-node-polyfills/shims/global",
|
||||||
|
"vite-plugin-node-polyfills/shims/process",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
31
.storybook/manager.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { create } from "storybook/theming";
|
||||||
|
import { addons } from "storybook/manager-api";
|
||||||
|
|
||||||
|
addons.setConfig({
|
||||||
|
theme: create({
|
||||||
|
base: "light",
|
||||||
|
colorPrimary: "#1b1d22",
|
||||||
|
colorSecondary: "#0467dd",
|
||||||
|
|
||||||
|
// Typography
|
||||||
|
fontBase: '"Inter", sans-serif',
|
||||||
|
fontCode: '"Inconsolata", monospace',
|
||||||
|
|
||||||
|
// Text colors
|
||||||
|
textColor: "#1b1d22",
|
||||||
|
appBg: "#ffffff",
|
||||||
|
barBg: "#ffffff",
|
||||||
|
|
||||||
|
brandTitle: "Element Call",
|
||||||
|
brandUrl: "https://element.io/",
|
||||||
|
brandImage: "/src/icons/Logo.svg",
|
||||||
|
brandTarget: "_self",
|
||||||
|
}),
|
||||||
|
});
|
||||||
56
.storybook/preview.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Preview } from "@storybook/react-vite";
|
||||||
|
import { TooltipProvider } from "@vector-im/compound-web";
|
||||||
|
import i18n from "i18next";
|
||||||
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
|
import EN from "../locales/en/app.json";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
import "../src/index.css";
|
||||||
|
|
||||||
|
// Bare-minimum i18n config
|
||||||
|
i18n
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
lng: "en",
|
||||||
|
fallbackLng: "en",
|
||||||
|
supportedLngs: ["en"],
|
||||||
|
// We embed the translations, so that it never needs to fetch
|
||||||
|
resources: {
|
||||||
|
en: {
|
||||||
|
translation: EN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false, // React has built-in XSS protections
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((e) => logger.warn("Failed to init i18n for stories", e));
|
||||||
|
|
||||||
|
const preview: Preview = {
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<TooltipProvider>
|
||||||
|
<Story />
|
||||||
|
</TooltipProvider>
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
91
.yarn/plugins/linker.cjs
vendored
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2025 New Vector Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: "linker",
|
|
||||||
factory: (require) => ({
|
|
||||||
hooks: {
|
|
||||||
// Yarn's plugin system is very light on documentation. The best we have
|
|
||||||
// for this hook is simply the type definition in
|
|
||||||
// https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-core/sources/Plugin.ts
|
|
||||||
registerPackageExtensions: async (config, registerPackageExtension) => {
|
|
||||||
const { structUtils } = require("@yarnpkg/core");
|
|
||||||
const { parseSyml } = require("@yarnpkg/parsers");
|
|
||||||
const path = require("path");
|
|
||||||
const fs = require("fs");
|
|
||||||
const process = require("process");
|
|
||||||
|
|
||||||
// Create a descriptor that we can use to target our direct dependencies
|
|
||||||
const projectPath = config.projectCwd
|
|
||||||
.replace(/\\/g, "/")
|
|
||||||
.replace("/C:/", "C:/");
|
|
||||||
const manifestPath = path.join(projectPath, "package.json");
|
|
||||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
||||||
const selfDescriptor = structUtils.parseDescriptor(
|
|
||||||
`${manifest.name}@*`,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load the list of linked packages
|
|
||||||
const linksPath = path.join(projectPath, ".links.yaml");
|
|
||||||
let linksFile;
|
|
||||||
try {
|
|
||||||
linksFile = fs.readFileSync(linksPath, "utf8");
|
|
||||||
} catch (e) {
|
|
||||||
return; // File doesn't exist, there's nothing to link
|
|
||||||
}
|
|
||||||
let links;
|
|
||||||
try {
|
|
||||||
links = parseSyml(linksFile);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(".links.yaml has invalid syntax", e);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve paths and turn them into a Yarn package extension
|
|
||||||
const overrides = Object.fromEntries(
|
|
||||||
Object.entries(links).map(([name, link]) => [
|
|
||||||
name,
|
|
||||||
`portal:${path.resolve(config.projectCwd, link)}`,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
const overrideIdentHashes = new Set();
|
|
||||||
for (const name of Object.keys(overrides))
|
|
||||||
overrideIdentHashes.add(
|
|
||||||
structUtils.parseDescriptor(`${name}@*`, true).identHash,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extend our own package's dependencies with these local overrides
|
|
||||||
registerPackageExtension(selfDescriptor, { dependencies: overrides });
|
|
||||||
|
|
||||||
// Filter out the original dependencies from the package spec so Yarn
|
|
||||||
// actually respects the overrides
|
|
||||||
const filterDependencies = (original) => {
|
|
||||||
const pkg = structUtils.copyPackage(original);
|
|
||||||
pkg.dependencies = new Map(
|
|
||||||
Array.from(pkg.dependencies.entries()).filter(
|
|
||||||
([, value]) => !overrideIdentHashes.has(value.identHash),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return pkg;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Patch Yarn's own normalizePackage method to use the above filter
|
|
||||||
const originalNormalizePackage = config.normalizePackage;
|
|
||||||
config.normalizePackage = function (pkg, extensions) {
|
|
||||||
return originalNormalizePackage.call(
|
|
||||||
this,
|
|
||||||
pkg.identHash === selfDescriptor.identHash
|
|
||||||
? filterDependencies(pkg)
|
|
||||||
: pkg,
|
|
||||||
extensions,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
nodeLinker: node-modules
|
|
||||||
plugins:
|
|
||||||
- .yarn/plugins/linker.cjs
|
|
||||||
@@ -1,3 +1,71 @@
|
|||||||
# Contributing code to Element
|
# Contributing code to Element
|
||||||
|
|
||||||
Element follows the same pattern as the [matrix-js-sdk](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md).
|
Element follows the same pattern as the [matrix-js-sdk](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
# Contributing to Element Call
|
||||||
|
|
||||||
|
Element Call is a native Matrix video conferencing application built on
|
||||||
|
[MatrixRTC (MSC4143)](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)
|
||||||
|
and [LiveKit](https://livekit.io/). It runs in multiple deployment contexts — as a
|
||||||
|
standalone web app and as a widget embedded in Element Web, Element X iOS, and
|
||||||
|
Element X Android. It is also the primary R&D foundation for MatrixRTC, which means
|
||||||
|
its architecture, maintainability, and flexibility are held to a high standard.
|
||||||
|
|
||||||
|
We welcome contributions from the community. This document explains how to
|
||||||
|
contribute effectively so that both you and the maintainers get the best outcome.
|
||||||
|
|
||||||
|
## Issue First Policy
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Before writing a single line of code for a new feature or UI change, you **must**
|
||||||
|
> open an issue and have the approach agreed with the maintainers.
|
||||||
|
>
|
||||||
|
> **We will not review or merge feature or UI pull requests that arrive without a
|
||||||
|
> corresponding, pre-approved issue.**
|
||||||
|
|
||||||
|
This is not gatekeeping — it's how we prevent wasted effort on both sides. Element
|
||||||
|
Call must work correctly across multiple deployment contexts and meet specific product
|
||||||
|
and design requirements. It is also a fast-moving codebase that underpins ongoing
|
||||||
|
MatrixRTC development. A PR that looks reasonable in isolation can easily conflict
|
||||||
|
with in-progress work, planned architecture changes, or design decisions that haven't
|
||||||
|
been publicly documented yet.
|
||||||
|
|
||||||
|
The issue is where we resolve all of that **before** anyone writes code.
|
||||||
|
|
||||||
|
**Bug fixes** are no exception — most confirmed bugs should already have an issue anyways, existing issues that are marked as bugs have an implicit maintainer approval. If the solution for the bug is controversial it is highly recommended to discuss the approach in the issue before opening a PR.
|
||||||
|
|
||||||
|
## Contribution Workflow
|
||||||
|
|
||||||
|
1. **Open an issue** using the [Enhancement request](https://github.com/element-hq/element-call/issues/new?template=enhancement.yml) template.
|
||||||
|
2. **Wait for feedback.** A maintainer will comment on the issue **within two weeks**. The use case and approach will get dicussed.
|
||||||
|
This may involve questions, suggestions, or a request to adjust scope.
|
||||||
|
This also allows to bring design and product into the loop before code gets created.
|
||||||
|
3. **Get a green light.** Wait for explicit approval from a maintainer before starting
|
||||||
|
implementation.
|
||||||
|
4. **Implement.** Write the code against the agreed approach.
|
||||||
|
5. **Open a PR.** Link to the issue in your PR description and satisfy the checklist
|
||||||
|
in the PR template.
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
Element Call moves fast and the codebase must stay clean and maintainable.
|
||||||
|
|
||||||
|
- **Take responsibility for AI-generated code.** AI tools can be a useful aid, but we expect all the generated code to be understood and reasoned about by the contributor. Questions by the maintainers should be answered without just forwarding them to AI. The maintainers also have access to AI tools. If your contribution is just transporting messages between LLM <-> maintaines all our time is better used if the maintainers decide to interact with AI for this specific problem by themselves.
|
||||||
|
- **Think across deployment contexts.** Changes must work correctly in both standalone
|
||||||
|
and widget modes. Consider how your change interacts with Element Web, Element X
|
||||||
|
iOS, and Element X Android.
|
||||||
|
- **Write tests.** New functionality should be covered by tests. Where it is feasible,
|
||||||
|
existing uncovered code touched by your PR should also gain tests.
|
||||||
|
|
||||||
|
## Contributor License Agreement
|
||||||
|
|
||||||
|
All contributors must sign the
|
||||||
|
[Element Contributor License Agreement](https://cla-assistant.io/element-hq/element-call)
|
||||||
|
before their contribution can be merged. The CLA assistant bot will prompt you
|
||||||
|
automatically when you open a PR.
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
The best place to ask questions about Element Call development is the MatrixRTC room:
|
||||||
|
|
||||||
|
**[#matrixRtc:matrix.org](https://matrix.to/#/#matrixrtc:matrix.org)**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ COPY ./dist /dist
|
|||||||
WORKDIR /dist/assets
|
WORKDIR /dist/assets
|
||||||
RUN gzip -k ../index.html *.js *.map *.css *.wasm *-app-*.json
|
RUN gzip -k ../index.html *.js *.map *.css *.wasm *-app-*.json
|
||||||
|
|
||||||
FROM nginxinc/nginx-unprivileged:alpine
|
FROM nginxinc/nginx-unprivileged:alpine-slim
|
||||||
|
|
||||||
COPY --from=builder ./dist /app
|
COPY --from=builder ./dist /app
|
||||||
|
|
||||||
|
|||||||
213
README.md
@@ -3,6 +3,7 @@
|
|||||||
[](https://matrix.to/#/#webrtc:matrix.org)
|
[](https://matrix.to/#/#webrtc:matrix.org)
|
||||||
[](https://localazy.com/p/element-call)
|
[](https://localazy.com/p/element-call)
|
||||||
[](LICENSE-AGPL-3.0)
|
[](LICENSE-AGPL-3.0)
|
||||||
|
[](https://app.codecov.io/gh/element-hq/element-call)
|
||||||
|
|
||||||
[🎬 Live Demo 🎬](https://call.element.io)
|
[🎬 Live Demo 🎬](https://call.element.io)
|
||||||
|
|
||||||
@@ -45,30 +46,35 @@ organize the flow of the meeting.
|
|||||||
✅ **Emoji Reactions** – Users can react with emojis 👍️ 🎉 👏 🤘, adding
|
✅ **Emoji Reactions** – Users can react with emojis 👍️ 🎉 👏 🤘, adding
|
||||||
engagement and interactivity to the conversation.
|
engagement and interactivity to the conversation.
|
||||||
|
|
||||||
## 🚀 Deployment Options
|
## 🚀 Deployment & Packaging Options
|
||||||
|
|
||||||
Element Call can be packaged in two ways:
|
Element Call is developed using the
|
||||||
|
[Matrix js-sdk](https://github.com/matrix-org/matrix-js-sdk) with Matroska mode.
|
||||||
|
This allows the app to run either as a Standalone App directly connected to a
|
||||||
|
homeserver with login interfaces or it can be used as a widget within a Matrix
|
||||||
|
client.
|
||||||
|
|
||||||
**Full Package** – Supports both **Standalone** and **Widget** mode. Hosted as
|
### 🖥️ Standalone Mode
|
||||||
a static web page and accessed via a URL when used as a widget.
|
|
||||||
|
|
||||||
**Embedded Package** – Designed for **Widget mode** only. Bundled with a
|
<p align="center">
|
||||||
messenger app for seamless integration. This is the recommended method for
|
<img src="./docs/element_call_standalone.drawio.png" alt="Element Call in Standalone Mode">
|
||||||
embedding Element Call into a messenger app.
|
</p>
|
||||||
|
|
||||||
See the [here](./docs/embedded-standalone.md) for more information on the packages.
|
In Standalone mode, Element Call operates as an independent, full-featured video
|
||||||
|
conferencing web application, enabling users to join or host calls without
|
||||||
### Standalone mode
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
In Standalone mode Element Call operates as an independent, full-featured video
|
|
||||||
conferencing web application, allowing users to join or host calls without
|
|
||||||
requiring a separate Matrix client.
|
requiring a separate Matrix client.
|
||||||
|
|
||||||
### Widget mode embedded in Messenger Apps
|
### 📲 In-App Calling (Widget Mode in Messenger Apps)
|
||||||
|
|
||||||

|
When used as a widget 🧩, Element Call is solely responsible for the core calling
|
||||||
|
functionality (MatrixRTC). Authentication, event handling, and room state
|
||||||
|
updates (via the Client-Server API) are handled by the hosting client.
|
||||||
|
Communication between Element Call and the client is managed through the widget
|
||||||
|
API.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./docs/element_call_widget.drawio.png" alt="Element Call in Widget Mode">
|
||||||
|
</p>
|
||||||
|
|
||||||
Element Call can be embedded as a widget inside apps like
|
Element Call can be embedded as a widget inside apps like
|
||||||
[**Element Web**](https://github.com/element-hq/element-web) or **Element X
|
[**Element Web**](https://github.com/element-hq/element-web) or **Element X
|
||||||
@@ -80,24 +86,50 @@ and voice calls within Matrix rooms.
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Embedded packaging is recommended for Element Call in widget mode!
|
> Embedded packaging is recommended for Element Call in widget mode!
|
||||||
|
|
||||||
|
### 📦 Element Call Packaging
|
||||||
|
|
||||||
|
Element Call offers two packaging options: one for standalone or widget
|
||||||
|
deployment, and another for seamless widget-based integration into messenger
|
||||||
|
apps. Below is an overview of each option.
|
||||||
|
|
||||||
|
**Full Package** – Supports both **Standalone** and **Widget** mode. It is
|
||||||
|
hosted as a static web page and can be accessed via a URL when used as a widget.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./docs/full_package.drawio.png" alt="Element Call Full Package">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
**Embedded Package** – Designed specifically for **Widget mode** only. It is
|
||||||
|
bundled with a messenger app for seamless integration and this is the
|
||||||
|
recommended method for embedding Element Call.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./docs/embedded_package.drawio.png" alt="Element Call Embedded Package">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
For more details on the packages, see the
|
||||||
|
[Embedded vs. Standalone Guide](./docs/embedded_standalone.md).
|
||||||
|
|
||||||
## 🛠️ Self-Hosting
|
## 🛠️ Self-Hosting
|
||||||
|
|
||||||
For operating and deploying Element Call on your own server, refer to the
|
For operating and deploying Element Call on your own server, refer to the
|
||||||
[**Self-Hosting Guide**](./docs/self-hosting.md).
|
[**Self-Hosting Guide**](./docs/self_hosting.md).
|
||||||
|
|
||||||
## 🧭 MatrixRTC Backend Discovery and Selection
|
## 🧭 MatrixRTC Backend Discovery and Selection
|
||||||
|
|
||||||
For proper Element Call operation each site deployment needs a MatrixRTC backend
|
For proper Element Call operation each site deployment needs a MatrixRTC backend
|
||||||
setup as outlined in the [Self-Hosting](#self-hosting). A typical federated site
|
setup as outlined in the [Self-Hosting Guide](./docs/self_hosting.md). A typical
|
||||||
deployment for three different sites A, B and C is depicted below.
|
federated site deployment for three different sites A, B and C is depicted below.
|
||||||
|
|
||||||

|
<p align="center">
|
||||||
|
<img src="./docs/Federated_Setup.drawio.png" alt="Element Call federated setup">
|
||||||
|
</p>
|
||||||
|
|
||||||
### Backend Discovery
|
### Backend Discovery
|
||||||
|
|
||||||
MatrixRTC backend (according to
|
The MatrixRTC backend (according to
|
||||||
[MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143))
|
[MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)) is
|
||||||
is announced by the Matrix site's `.well-known/matrix/client` file and discovered
|
announced by the Matrix site's `.well-known/matrix/client` file and discovered
|
||||||
via the `org.matrix.msc4143.rtc_foci` key, e.g.:
|
via the `org.matrix.msc4143.rtc_foci` key, e.g.:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -112,26 +144,29 @@ via the `org.matrix.msc4143.rtc_foci` key, e.g.:
|
|||||||
where the format for MatrixRTC using LiveKit backend is defined in
|
where the format for MatrixRTC using LiveKit backend is defined in
|
||||||
[MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md).
|
[MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md).
|
||||||
In the example above Matrix clients do discover a focus of type `livekit` which
|
In the example above Matrix clients do discover a focus of type `livekit` which
|
||||||
points them to a Matrix LiveKit JWT Auth Service via `livekit_service_url`.
|
points them to a [MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)
|
||||||
|
via `livekit_service_url`.
|
||||||
|
|
||||||
### Backend Selection
|
### Backend Selection
|
||||||
|
|
||||||
- Each call participant proposes their discovered MatrixRTC backend from
|
- Each call participant proposes their discovered MatrixRTC backend from
|
||||||
`org.matrix.msc4143.rtc_foci` in their `org.matrix.msc3401.call.member` state event.
|
`org.matrix.msc4143.rtc_foci` in their `org.matrix.msc3401.call.member` state event.
|
||||||
- For **LiveKit** MatrixRTC backend
|
- For the **LiveKit** MatrixRTC backend
|
||||||
([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)),
|
([MSC4195](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md)),
|
||||||
the **first participant who joined the call** defines via the `foci_preferred`
|
the **first participant who joined the call** defines which backend will be used for this call via
|
||||||
key in their `org.matrix.msc3401.call.member` which actual MatrixRTC backend
|
the `foci_preferred` key in their `org.matrix.msc3401.call.member` state event.
|
||||||
will be used for this call.
|
- During the actual call join flow, the **[MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)**
|
||||||
- During the actual call join flow, the **LiveKit JWT Auth Service** provides
|
provides the client with the **LiveKit SFU WebSocket URL** and an
|
||||||
the client with the **LiveKit SFU WebSocket URL** and an **access JWT token**
|
**access JWT token** in order to exchange media via WebRTC.
|
||||||
in order to exchange media via WebRTC.
|
|
||||||
|
|
||||||
The example below illustrates how backend selection works across **Matrix
|
The example below illustrates how backend selection works across **Matrix
|
||||||
federation**, using the setup from sites A, B, and C. It demonstrates backend
|
federation**, using the setup from sites A, B, and C. It demonstrates backend
|
||||||
selection for **Matrix rooms 123 and 456**, which include users from different
|
selection for **Matrix rooms 123 and 456**, which include users from different
|
||||||
homeservers.
|
homeservers.
|
||||||

|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./docs/SFU_selection.drawio.png" alt="Element Call SFU selection over Matrix federation">
|
||||||
|
</p>
|
||||||
|
|
||||||
## 🌍 Translation
|
## 🌍 Translation
|
||||||
|
|
||||||
@@ -142,6 +177,13 @@ discuss and coordinate translation efforts.
|
|||||||
|
|
||||||
## 🛠️ Development
|
## 🛠️ Development
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Node.js (e.g. via [nvm](https://github.com/nvm-sh/nvm))
|
||||||
|
- [Corepack](https://github.com/nodejs/corepack) (not bundled with Node.js anymore starting from 25.0.0)
|
||||||
|
- Docker client and runtime + Docker Compose (for the backend)
|
||||||
|
- On macOS you can install everything with `brew install colima docker docker-compose`
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|
||||||
To get started clone and set up this project:
|
To get started clone and set up this project:
|
||||||
@@ -150,7 +192,7 @@ To get started clone and set up this project:
|
|||||||
git clone https://github.com/element-hq/element-call.git
|
git clone https://github.com/element-hq/element-call.git
|
||||||
cd element-call
|
cd element-call
|
||||||
corepack enable
|
corepack enable
|
||||||
yarn
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
To use it, create a local config by, e.g.,
|
To use it, create a local config by, e.g.,
|
||||||
@@ -158,64 +200,88 @@ To use it, create a local config by, e.g.,
|
|||||||
The `config.devenv.json` config should work with the backend development
|
The `config.devenv.json` config should work with the backend development
|
||||||
environment as outlined in the next section out of box.
|
environment as outlined in the next section out of box.
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Be aware, that this `config.devenv.json` is exposing a deprecated fallback
|
|
||||||
> LiveKit config key. If the homeserver advertises SFU backend via
|
|
||||||
> `.well-known/matrix/client` this has precedence.
|
|
||||||
|
|
||||||
You're now ready to launch the development server:
|
You're now ready to launch the development server:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
|
|
||||||
- [Developing with linked packages](./linking.md)
|
- [Developing with linked packages](./docs/linking.md)
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
|
|
||||||
A docker compose file `dev-backend-docker-compose.yml` is provided to start the
|
A docker compose file `docker-compose-dev.yml` is provided to start the
|
||||||
whole stack of components which is required for a local development environment:
|
whole stack of components which is required for a local development environment
|
||||||
|
including federation:
|
||||||
|
|
||||||
- Minimum Synapse Setup (servername: `synapse.localhost`)
|
- Minimum Synapse Setup (servernames: `synapse.m.localhost`, `synapse.othersite.m.localhost`)
|
||||||
- LiveKit JWT Service (Note requires Federation API and hence a TLS reverse proxy)
|
- MatrixRTC Authorization Service (Note: requires Federation API and hence a TLS reverse proxy)
|
||||||
- Minimum TLS reverse proxy (servername: `synapse.localhost`) Note certificates
|
- Minimum LiveKit SFU setup using dev defaults for config
|
||||||
are valid for at least 10 years from now
|
- Minimum `localhost` Certificate Authority (CA) for Transport Layer Security (TLS)
|
||||||
- Minimum LiveKit SFU Setup using dev defaults for config
|
- Hostnames: `m.localhost`, `*.m.localhost`, `*.othersite.m.localhost`
|
||||||
- Redis db for completeness
|
- Add [./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt) to your web browser's trusted
|
||||||
|
certificates
|
||||||
|
- Minimum TLS reverse proxy for
|
||||||
|
- Synapse homeserver: `synapse.m.localhost` and `synapse.othersite.m.localhost`
|
||||||
|
- MatrixRTC backend: `matrix-rtc.m.localhost` and `matrix-rtc.othersite.m.localhost`
|
||||||
|
- Local Element Call development `call.m.localhost` via `pnpm dev --host `
|
||||||
|
- Element Web `app.m.localhost` and `app.othersite.m.localhost`
|
||||||
|
- Note certificates will expire on Thr, 20 September 2035 14:27:35 CEST
|
||||||
|
|
||||||
These use a test 'secret' published in this repository, so this must be used
|
These use a test 'secret' published in this repository, so this must be used
|
||||||
only for local development and **_never be exposed to the public Internet._**
|
only for local development and **_never be exposed to the public Internet._**
|
||||||
|
|
||||||
Run backend components:
|
Make sure your Docker runtime is running (e.g. via `colima start`) and then start
|
||||||
|
the backend components:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn backend
|
pnpm backend
|
||||||
# or for podman-compose
|
# or for podman-compose:
|
||||||
# podman-compose -f dev-backend-docker-compose.yml up
|
# podman-compose -f docker-compose-dev.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> To ensure your local development frontend functions properly, you’ll need to
|
||||||
|
> add certificate exceptions in your browser for `https://localhost:3000`,
|
||||||
|
> `https://matrix-rtc.m.localhost/livekit/jwt/healthz` and
|
||||||
|
> `https://synapse.m.localhost/.well-known/matrix/client`. This can be either
|
||||||
|
> done by adding the minimum localhost CA
|
||||||
|
> ([./backend/dev_tls_local-ca.crt](./backend/dev_tls_local-ca.crt)) to your web
|
||||||
|
> browser's trusted certificates or by simply copying and pasting each URL into
|
||||||
|
> your browser’s address bar and follow the prompts to add the exception.
|
||||||
|
|
||||||
|
### Updating snapshots
|
||||||
|
|
||||||
|
To update snapshots used in tests, use Vitest's `-u` flag, e.g.:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pnpm test DeveloperSettingsTab -u
|
||||||
```
|
```
|
||||||
|
|
||||||
### Playwright tests
|
### Playwright tests
|
||||||
|
|
||||||
Our Playwright tests run automatically as part of our CI along with our other tests,
|
Our Playwright tests run automatically as part of our CI along with our other
|
||||||
on every pull request.
|
tests, on every pull request.
|
||||||
|
|
||||||
You may need to follow instructions to set up your development environment for running
|
You may need to follow instructions to set up your development environment for
|
||||||
Playwright by following <https://playwright.dev/docs/browsers#install-browsers> and
|
running Playwright by following
|
||||||
|
<https://playwright.dev/docs/browsers#install-browsers> and
|
||||||
<https://playwright.dev/docs/browsers#install-system-dependencies>.
|
<https://playwright.dev/docs/browsers#install-system-dependencies>.
|
||||||
|
|
||||||
However the Playwright tests are run, an element-call instance must be running on
|
However the Playwright tests are run, an element-call instance must be running
|
||||||
https://localhost:3000 (this is configured in `playwright.config.ts`) - this is what will
|
on https://localhost:3000 (this is configured in `playwright.config.ts`) - this
|
||||||
be tested.
|
is what will be tested.
|
||||||
|
|
||||||
The local backend environment should be running for the test to work:
|
The local backend environment should be running for the test to work:
|
||||||
`yarn backend`
|
`pnpm backend`
|
||||||
|
|
||||||
There are a few different ways to run the tests yourself. The simplest is to run:
|
There are a few different ways to run the tests yourself. The simplest is to
|
||||||
|
run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
yarn run test:playwright
|
pnpm run test:playwright
|
||||||
```
|
```
|
||||||
|
|
||||||
This will run the Playwright tests once, non-interactively.
|
This will run the Playwright tests once, non-interactively.
|
||||||
@@ -223,7 +289,7 @@ This will run the Playwright tests once, non-interactively.
|
|||||||
There is a more user-friendly way to run the tests in interactive mode:
|
There is a more user-friendly way to run the tests in interactive mode:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
yarn run test:playwright:open
|
pnpm run test:playwright:open
|
||||||
```
|
```
|
||||||
|
|
||||||
The easiest way to develop new test is to use the codegen feature of Playwright:
|
The easiest way to develop new test is to use the codegen feature of Playwright:
|
||||||
@@ -237,8 +303,8 @@ to test visibility, text content and clicking.
|
|||||||
|
|
||||||
##### Investigate a failed test from the CI
|
##### Investigate a failed test from the CI
|
||||||
|
|
||||||
In the failed action page, click on the failed job, then scroll down to the `upload-artifact` step.
|
In the failed action page, click on the failed job, then scroll down to the
|
||||||
You will find a link to download the zip report, as per:
|
`upload-artifact` step. You will find a link to download the zip report, as per:
|
||||||
|
|
||||||
```
|
```
|
||||||
Artifact playwright-report has been successfully uploaded! Final size is 1360358 bytes. Artifact ID is 2746265841
|
Artifact playwright-report has been successfully uploaded! Final size is 1360358 bytes. Artifact ID is 2746265841
|
||||||
@@ -252,7 +318,8 @@ npx playwright show-report ~/Downloads/playwright-report/
|
|||||||
```
|
```
|
||||||
|
|
||||||
Under the failed test there is a small icon looking like "3 columns" (next to
|
Under the failed test there is a small icon looking like "3 columns" (next to
|
||||||
the test name file name), click on it to see the live screenshots/console output.
|
the test name file name), click on it to see the live screenshots/console
|
||||||
|
output.
|
||||||
|
|
||||||
### Test Coverage
|
### Test Coverage
|
||||||
|
|
||||||
@@ -264,7 +331,7 @@ To add a new translation key you can do these steps:
|
|||||||
|
|
||||||
1. Add the new key entry to the code where the new key is used:
|
1. Add the new key entry to the code where the new key is used:
|
||||||
`t("some_new_key")`
|
`t("some_new_key")`
|
||||||
1. Run `yarn i18n` to extract the new key and update the translation files. This
|
1. Run `pnpm i18n` to extract the new key and update the translation files. This
|
||||||
will add a skeleton entry to the `locales/en/app.json` file:
|
will add a skeleton entry to the `locales/en/app.json` file:
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
@@ -292,6 +359,16 @@ Usage and other technical details about the project can be found here:
|
|||||||
|
|
||||||
[**Docs**](./docs/README.md)
|
[**Docs**](./docs/README.md)
|
||||||
|
|
||||||
|
## GitHub Labels
|
||||||
|
|
||||||
|
GitHub labels in this repository are maintained in the [`labels.yml`](.github/labels.yml) file and
|
||||||
|
automatically synced to GitHub using the [`sync-labels` workflow](.github/workflows/sync-labels.yml).
|
||||||
|
We do this so that we can reuse the labels between repositories.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Do not manually edit labels in the GitHub UI. Any manual changes will be overridden by the
|
||||||
|
> workflow on its next invocation.
|
||||||
|
|
||||||
## 📝 Copyright & License
|
## 📝 Copyright & License
|
||||||
|
|
||||||
Copyright 2021-2025 New Vector Ltd
|
Copyright 2021-2025 New Vector Ltd
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Testing Element-Call in widget mode
|
# Testing Element-Call in widget mode
|
||||||
|
|
||||||
When running `yarn backend` the latest element-web develop will be deployed and served on `http://localhost:8081`.
|
When running `pnpm backend` the latest element-web develop will be deployed and served on `http://localhost:8081`.
|
||||||
In a development environment, you might prefer to just use the `element-web` repo directly, but this setup is useful for CI/CD testing.
|
In a development environment, you might prefer to just use the `element-web` repo directly, but this setup is useful for CI/CD testing.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
@@ -18,7 +18,7 @@ that uses
|
|||||||
It is part of the existing backend setup. To start the backend, run:
|
It is part of the existing backend setup. To start the backend, run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn backend
|
pnpm backend
|
||||||
```
|
```
|
||||||
|
|
||||||
Then open `http://localhost:8081` in your browser.
|
Then open `http://localhost:8081` in your browser.
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
"@babel/preset-env",
|
|
||||||
{
|
|
||||||
targets: {
|
|
||||||
node: "current",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@babel/preset-react",
|
|
||||||
{
|
|
||||||
runtime: "automatic",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"@babel/preset-typescript",
|
|
||||||
],
|
|
||||||
plugins: ["babel-plugin-transform-vite-meta-env"],
|
|
||||||
};
|
|
||||||
69
backend/dev_homeserver-othersite.yaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
server_name: "synapse.othersite.m.localhost"
|
||||||
|
public_baseurl: https://synapse.othersite.m.localhost/
|
||||||
|
|
||||||
|
pid_file: /data/homeserver.pid
|
||||||
|
|
||||||
|
listeners:
|
||||||
|
- port: 18008
|
||||||
|
tls: false
|
||||||
|
type: http
|
||||||
|
x_forwarded: true
|
||||||
|
resources:
|
||||||
|
- names: [client, federation, openid]
|
||||||
|
compress: false
|
||||||
|
|
||||||
|
database:
|
||||||
|
name: sqlite3
|
||||||
|
args:
|
||||||
|
database: /data/homeserver.db
|
||||||
|
|
||||||
|
media_store_path: /data/media_store
|
||||||
|
signing_key_path: "/data/SERVERNAME.signing.key"
|
||||||
|
|
||||||
|
# Due to custom TLS certificate with domains
|
||||||
|
# - m.localhost, localhost
|
||||||
|
# - *.m.localhost
|
||||||
|
# - *.othersite.m.localhost
|
||||||
|
# we disable certificate verification to allow for federation
|
||||||
|
# WARNING: DO NOT USE IN PRODUCTION!!!
|
||||||
|
federation_verify_certificates: false
|
||||||
|
ip_range_blacklist: []
|
||||||
|
trusted_key_servers:
|
||||||
|
- server_name: "synapse.m.localhost"
|
||||||
|
accept_keys_insecurely: true
|
||||||
|
|
||||||
|
experimental_features:
|
||||||
|
# MSC3266: Room summary API. Used for knocking over federation
|
||||||
|
msc3266_enabled: true
|
||||||
|
# MSC4222 needed for syncv2 state_after. This allow clients to
|
||||||
|
# correctly track the state of the room.
|
||||||
|
msc4222_enabled: true
|
||||||
|
# sticky events for MatrixRTC user state
|
||||||
|
msc4354_enabled: true
|
||||||
|
|
||||||
|
# The maximum allowed duration by which sent events can be delayed, as
|
||||||
|
# per MSC4140. Must be a positive value if set. Defaults to no
|
||||||
|
# duration (null), which disallows sending delayed events.
|
||||||
|
max_event_delay_duration: 24h
|
||||||
|
|
||||||
|
# Required for Element Call in Single Page Mode due to on-the-fly user registration
|
||||||
|
enable_registration: true
|
||||||
|
enable_registration_without_verification: true
|
||||||
|
|
||||||
|
# Shared secret for admin user registration via API (for testing only!)
|
||||||
|
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||||
|
|
||||||
|
report_stats: false
|
||||||
|
serve_server_wellknown: true
|
||||||
|
|
||||||
|
# Ratelimiting settings for client actions (registration, login, messaging).
|
||||||
|
#
|
||||||
|
# Each ratelimiting configuration is made of two parameters:
|
||||||
|
# - per_second: number of requests a client can send per second.
|
||||||
|
# - burst_count: number of requests a client can send before being throttled.
|
||||||
|
|
||||||
|
rc_message:
|
||||||
|
# This needs to match at least the heart-beat frequency plus a bit of headroom
|
||||||
|
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
|
||||||
|
per_second: 0.5
|
||||||
|
burst_count: 30
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
server_name: "synapse.localhost"
|
server_name: "synapse.m.localhost"
|
||||||
public_baseurl: http://synapse.localhost:8008/
|
public_baseurl: https://synapse.m.localhost/
|
||||||
|
|
||||||
pid_file: /data/homeserver.pid
|
pid_file: /data/homeserver.pid
|
||||||
|
|
||||||
@@ -19,8 +19,18 @@ database:
|
|||||||
|
|
||||||
media_store_path: /data/media_store
|
media_store_path: /data/media_store
|
||||||
signing_key_path: "/data/SERVERNAME.signing.key"
|
signing_key_path: "/data/SERVERNAME.signing.key"
|
||||||
|
|
||||||
|
# Due to custom TLS certificate with domains
|
||||||
|
# - m.localhost, localhost
|
||||||
|
# - *.m.localhost
|
||||||
|
# - *.othersite.m.localhost
|
||||||
|
# we disable certificate verification to allow for federation.
|
||||||
|
# WARNING: DO NOT USE IN PRODUCTION!!!
|
||||||
|
federation_verify_certificates: false
|
||||||
|
ip_range_blacklist: []
|
||||||
trusted_key_servers:
|
trusted_key_servers:
|
||||||
- server_name: "matrix.org"
|
- server_name: "synapse.othersite.m.localhost"
|
||||||
|
accept_keys_insecurely: true
|
||||||
|
|
||||||
experimental_features:
|
experimental_features:
|
||||||
# MSC3266: Room summary API. Used for knocking over federation
|
# MSC3266: Room summary API. Used for knocking over federation
|
||||||
@@ -28,12 +38,24 @@ experimental_features:
|
|||||||
# MSC4222 needed for syncv2 state_after. This allow clients to
|
# MSC4222 needed for syncv2 state_after. This allow clients to
|
||||||
# correctly track the state of the room.
|
# correctly track the state of the room.
|
||||||
msc4222_enabled: true
|
msc4222_enabled: true
|
||||||
|
# sticky events for MatrixRTC user state
|
||||||
|
msc4354_enabled: true
|
||||||
|
|
||||||
# The maximum allowed duration by which sent events can be delayed, as
|
# The maximum allowed duration by which sent events can be delayed, as
|
||||||
# per MSC4140. Must be a positive value if set. Defaults to no
|
# per MSC4140. Must be a positive value if set. Defaults to no
|
||||||
# duration (null), which disallows sending delayed events.
|
# duration (null), which disallows sending delayed events.
|
||||||
max_event_delay_duration: 24h
|
max_event_delay_duration: 24h
|
||||||
|
|
||||||
|
# Required for Element Call in Single Page Mode due to on-the-fly user registration
|
||||||
|
enable_registration: true
|
||||||
|
enable_registration_without_verification: true
|
||||||
|
|
||||||
|
# Shared secret for admin user registration via API (for testing only!)
|
||||||
|
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||||
|
|
||||||
|
report_stats: false
|
||||||
|
serve_server_wellknown: true
|
||||||
|
|
||||||
# Ratelimiting settings for client actions (registration, login, messaging).
|
# Ratelimiting settings for client actions (registration, login, messaging).
|
||||||
#
|
#
|
||||||
# Each ratelimiting configuration is made of two parameters:
|
# Each ratelimiting configuration is made of two parameters:
|
||||||
@@ -45,10 +67,3 @@ rc_message:
|
|||||||
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
|
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
|
||||||
per_second: 0.5
|
per_second: 0.5
|
||||||
burst_count: 30
|
burst_count: 30
|
||||||
|
|
||||||
# Required for Element Call in Single Page Mode due to on-the-fly user registration
|
|
||||||
enable_registration: true
|
|
||||||
enable_registration_without_verification: true
|
|
||||||
|
|
||||||
report_stats: false
|
|
||||||
serve_server_wellknown: true
|
|
||||||
|
|||||||
24
backend/dev_livekit-othersite.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
port: 17880
|
||||||
|
bind_addresses:
|
||||||
|
- "0.0.0.0"
|
||||||
|
rtc:
|
||||||
|
tcp_port: 17881
|
||||||
|
port_range_start: 50300
|
||||||
|
port_range_end: 50400
|
||||||
|
use_external_ip: false
|
||||||
|
turn:
|
||||||
|
enabled: false
|
||||||
|
domain: localhost
|
||||||
|
cert_file: ""
|
||||||
|
key_file: ""
|
||||||
|
tls_port: 5349
|
||||||
|
udp_port: 443
|
||||||
|
external_tls: true
|
||||||
|
keys:
|
||||||
|
devkey: secret
|
||||||
|
room:
|
||||||
|
auto_create: false
|
||||||
|
webhook:
|
||||||
|
api_key: devkey
|
||||||
|
urls:
|
||||||
|
- https://matrix-rtc.othersite.m.localhost/livekit/jwt/sfu_webhook
|
||||||
@@ -6,11 +6,6 @@ rtc:
|
|||||||
port_range_start: 50100
|
port_range_start: 50100
|
||||||
port_range_end: 50200
|
port_range_end: 50200
|
||||||
use_external_ip: false
|
use_external_ip: false
|
||||||
#redis:
|
|
||||||
# address: redis:6379
|
|
||||||
# username: ""
|
|
||||||
# password: ""
|
|
||||||
# db: 0
|
|
||||||
turn:
|
turn:
|
||||||
enabled: false
|
enabled: false
|
||||||
domain: localhost
|
domain: localhost
|
||||||
@@ -21,3 +16,9 @@ turn:
|
|||||||
external_tls: true
|
external_tls: true
|
||||||
keys:
|
keys:
|
||||||
devkey: secret
|
devkey: secret
|
||||||
|
room:
|
||||||
|
auto_create: false
|
||||||
|
webhook:
|
||||||
|
api_key: devkey
|
||||||
|
urls:
|
||||||
|
- https://matrix-rtc.m.localhost/livekit/jwt/sfu_webhook
|
||||||
|
|||||||
301
backend/dev_nginx.conf
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
# Synapse reverse proxy including .well-known/matrix/client
|
||||||
|
# domain synapse.m.localhost
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
listen 443 ssl;
|
||||||
|
listen 8448 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
listen [::]:8448 ssl;
|
||||||
|
server_name synapse.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
# well-known config adding rtc_foci backend
|
||||||
|
# Note well-known is currently not effective due to:
|
||||||
|
# https://spec.matrix.org/v1.12/client-server-api/#well-known-uri the spec
|
||||||
|
# says it must be at https://$server_name/... (implied port 443) Hence, we
|
||||||
|
# currently rely for local development environment on deprecated config.json
|
||||||
|
# setting for livekit_service_url
|
||||||
|
location /.well-known/matrix/client {
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
return 200 '{"m.homeserver": {"base_url": "https://synapse.m.localhost"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "https://matrix-rtc.m.localhost/livekit/jwt"}]}';
|
||||||
|
default_type application/json;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reverse proxy for Matrix Synapse Homeserver
|
||||||
|
# This is also required for development environment.
|
||||||
|
# Reason: the lk-jwt-service uses the federation API for the openid token
|
||||||
|
# verification, which requires TLS
|
||||||
|
location ~ ^(/_matrix|/_synapse/client) {
|
||||||
|
proxy_pass "http://homeserver:8008";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
location ~ ^(/_matrix|/_synapse/admin) {
|
||||||
|
proxy_pass "http://homeserver:8008";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Synapse reverse proxy including .well-known/matrix/client
|
||||||
|
# domain synapse.othersite.m.localhost
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
listen 443 ssl;
|
||||||
|
listen 8448 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
listen [::]:8448 ssl;
|
||||||
|
server_name synapse.othersite.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
# well-known config adding rtc_foci backend
|
||||||
|
# Note well-known is currently not effective due to:
|
||||||
|
# https://spec.matrix.org/v1.12/client-server-api/#well-known-uri the spec
|
||||||
|
# says it must be at https://$server_name/... (implied port 443) Hence, we
|
||||||
|
# currently rely for local development environment on deprecated config.json
|
||||||
|
# setting for livekit_service_url
|
||||||
|
location /.well-known/matrix/client {
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
return 200 '{"m.homeserver": {"base_url": "https://synapse.othersite.m.localhost"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "https://matrix-rtc.othersite.m.localhost/livekit/jwt"}]}';
|
||||||
|
default_type application/json;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reverse proxy for Matrix Synapse Homeserver
|
||||||
|
# This is also required for development environment.
|
||||||
|
# Reason: the lk-jwt-service uses the federation API for the openid token
|
||||||
|
# verification, which requires TLS
|
||||||
|
location ~ ^(/_matrix|/_synapse/client) {
|
||||||
|
proxy_pass "http://homeserver-1:18008";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^(/_matrix|/_synapse/admin) {
|
||||||
|
proxy_pass "http://homeserver-1:18008";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# MatrixRTC reverse proxy
|
||||||
|
# domain matrix-rtc.m.localhost
|
||||||
|
# - MatrixRTC Authorization Service
|
||||||
|
# - LiveKit SFU websocket signaling connection
|
||||||
|
upstream jwt-auth-services {
|
||||||
|
server auth-server:6080;
|
||||||
|
server host.docker.internal:6080;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name matrix-rtc.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
http2 on;
|
||||||
|
|
||||||
|
|
||||||
|
location ^~ /livekit/jwt/ {
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# JWT Service running at port 6080
|
||||||
|
proxy_pass http://jwt-auth-services/;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /livekit/sfu/ {
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_send_timeout 120;
|
||||||
|
proxy_read_timeout 120;
|
||||||
|
proxy_buffering off;
|
||||||
|
|
||||||
|
proxy_set_header Accept-Encoding gzip;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# LiveKit SFU websocket connection running at port 7880
|
||||||
|
proxy_pass http://livekit-sfu:7880/;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# MatrixRTC reverse proxy
|
||||||
|
# domain matrix-rtc.othersite.m.localhost
|
||||||
|
# - MatrixRTC Authorization Service
|
||||||
|
# - LiveKit SFU websocket signaling connection
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name matrix-rtc.othersite.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
http2 on;
|
||||||
|
|
||||||
|
|
||||||
|
location ^~ /livekit/jwt/ {
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# JWT Service running at port 16080
|
||||||
|
proxy_pass http://auth-service-1:16080/;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /livekit/sfu/ {
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_send_timeout 120;
|
||||||
|
proxy_read_timeout 120;
|
||||||
|
proxy_buffering off;
|
||||||
|
|
||||||
|
proxy_set_header Accept-Encoding gzip;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# LiveKit SFU websocket connection running at port 17880
|
||||||
|
proxy_pass http://livekit-sfu-1:17880/;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convenience reverse proxy for the call.m.localhost domain to element call
|
||||||
|
# running on the host either via
|
||||||
|
# - pnpm dev --host or
|
||||||
|
# - falling back to http (the element call docker container)
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name call.m.localhost;
|
||||||
|
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name call.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
# 1. Attempt HTTPS first
|
||||||
|
location ^~ / {
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_pass https://host.docker.internal:3000;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
# 2. Redirect specific errors (e.g., 502 Bad Gateway or 504 Timeout)
|
||||||
|
# to the named fallback location
|
||||||
|
error_page 502 503 504 = @http_fallback;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Fallback location using HTTP
|
||||||
|
location @http_fallback {
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_pass http://host.docker.internal:8080;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convenience reverse proxy app.m.localhost for element web
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name app.m.localhost;
|
||||||
|
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name app.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
|
||||||
|
location ^~ / {
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_pass http://element-web:8081;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convenience reverse proxy app.othersite.m.localhost for element web
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name app.othersite.m.localhost;
|
||||||
|
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
server_name app.othersite.m.localhost;
|
||||||
|
ssl_certificate /root/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /root/ssl/key.pem;
|
||||||
|
|
||||||
|
|
||||||
|
location ^~ / {
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_pass http://element-web-1:18081;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
backend/dev_tls_local-ca.crt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDGjCCAgKgAwIBAgIUOlA2wgQUGZkKqNDvvifFWEsJfvYwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwHjEcMBoGA1UEAwwTRWxlbWVudCBDYWxsIERldiBDQTAeFw0yNjA1MTgwOTM0
|
||||||
|
MzFaFw0yODA3MjYwOTM0MzFaMB4xHDAaBgNVBAMME0VsZW1lbnQgQ2FsbCBEZXYg
|
||||||
|
Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcImv3pStfIUo7PbOO
|
||||||
|
XRVUXuDlApBOrg2dCnvZQ1Jfaf4MftGHj/pURkF4eoBuyH4k4+NLzWD0VcU1cM74
|
||||||
|
RnxowJt4AceCGe5RK/rqal5fapXc2vYMM8P6xaQR86gkxohpufsLgTnSweh74yqN
|
||||||
|
B5WHUnCX00/X0bh1ho2BMUvGM9+dI4MdgKdaQDgWK4zg9hwp2Z6Yq7SkJ/D8+sGW
|
||||||
|
WGpn3osDakL8HTBqop+YVJgF40db50yFurfcfQ0gjVtT4JW8ejO9j8PS/S2oQ/s5
|
||||||
|
mA1B470XhLtT5qTjGm2bp3WpYkTi5widps8PDzBp5eNr0HrvJqcw7BGpbvBlLa+3
|
||||||
|
7dhLAgMBAAGjUDBOMB0GA1UdDgQWBBRDfyRM4yKUqW6vu/2KUSXGb8vswDAfBgNV
|
||||||
|
HSMEGDAWgBRDfyRM4yKUqW6vu/2KUSXGb8vswDAMBgNVHRMEBTADAQH/MA0GCSqG
|
||||||
|
SIb3DQEBCwUAA4IBAQBoAhD4W4Yi/VJ2pTKrzhstn1UF1rgQnRddnn97v5BaEV/X
|
||||||
|
uuBXbSO+/ewjQUupRjePZFp9FFe9co1OiduKcDExlvPU1eIqkWAwDWjMDpI+Lw5q
|
||||||
|
KI5yHzplmMrT/7jn9Tepl9atrIcfDeFkP1dGtdRPyU6ARJEEWJSKeH9ftmImAsbM
|
||||||
|
ykXAqSyRl8+bPx1ISG4cNihOxFd38VPDHIW53umaRBgRcN4GcvloKBGrVtRFNM//
|
||||||
|
H+md8HmNQMP+e7FamETxs28DxjsdpygxjiFNY/T2eD67dH50ZxC3qCxEG6TJsoAg
|
||||||
|
TYJafnqEcGDfiWQyNZRBypuaRsRmmTR27hCPVgi8
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
backend/dev_tls_local-ca.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcImv3pStfIUo7
|
||||||
|
PbOOXRVUXuDlApBOrg2dCnvZQ1Jfaf4MftGHj/pURkF4eoBuyH4k4+NLzWD0VcU1
|
||||||
|
cM74RnxowJt4AceCGe5RK/rqal5fapXc2vYMM8P6xaQR86gkxohpufsLgTnSweh7
|
||||||
|
4yqNB5WHUnCX00/X0bh1ho2BMUvGM9+dI4MdgKdaQDgWK4zg9hwp2Z6Yq7SkJ/D8
|
||||||
|
+sGWWGpn3osDakL8HTBqop+YVJgF40db50yFurfcfQ0gjVtT4JW8ejO9j8PS/S2o
|
||||||
|
Q/s5mA1B470XhLtT5qTjGm2bp3WpYkTi5widps8PDzBp5eNr0HrvJqcw7BGpbvBl
|
||||||
|
La+37dhLAgMBAAECggEAEsS4gc5jBk50I+bo3KYn2DqHgj/qpOqbTFNkS9uh3UJa
|
||||||
|
fZoJCeiuyM6hNCBVq/uB3mFeg1Au5XAiAqiK2KFwdw8gIS7lkqgXU76brO4YZhPj
|
||||||
|
6+aOSS03079KV7YYckNDRqJKoTlpgAI7Nhk6ljVhLiEk07tdD65wJACGpg8M8sg9
|
||||||
|
dyAz+ANs9gs65iF5LYjH61O/AFlLqCRQh5/z0mjGX6G9uN27nxeUY4+n4QMAcd9D
|
||||||
|
Gcygxjt+4nlQayNAlKMwVfps9bWNtI3Ye9knY4WGkrv5cJbW3bgjV6qrvQsbukbq
|
||||||
|
xEYzcIlUiWGO9Tv7MN6rk5uQOyoKT/KUnfRmdVd3YQKBgQDMhWm6Q+WuI7Pyn57R
|
||||||
|
tmY4rs+fSqTmv6xAOcozKJxffGaEwSUuNA15NvR/7iedcNqmH3XT9j90ZNVHe090
|
||||||
|
ocm1HDUvzC9G5GRrdO6JTTksRaIMZEhosWxqH3DIuBJPLGbF/4obGE7//PJtmDEp
|
||||||
|
QVL9Aa0WrcwAWhRzUdvCE+taMwKBgQDDbyZIvtlEr1w2V0bjXO536rRksBapc2ZJ
|
||||||
|
XRKtrXivuVtiZYNDB0I7CCJ52cka61n3kyZz2mhQmLq4cAZXyKYWE2i643O+kc3S
|
||||||
|
lpZEFSfDZ+3YlhxMxG9oEcgUSwVdbPlAhd/UR8V6n8o2Fm+gug9h6E2zY4fgHLJF
|
||||||
|
8hOWoD4hiQKBgA7YXD1F8mT6eHRS+78zIyZYIf/o9iE9pm4fA7tE5lzT9ckLD/zT
|
||||||
|
kGrM/2BN1BhMecJ3JCFXjXGQZB7FJ5ZKrA52VrH6ezAFIfjeyvWyYkUBZOrLWKoo
|
||||||
|
vrrRP2mCWuneSjNzAf5HfGx+WsZztpXNBQ4SUhMEWHtqDnP0bCQhOAMbAoGAPfLv
|
||||||
|
qcOFT3ZevoLv34ZHuQ9W20vOAyynUb4E+7SvOtSAmTIgZ5DXd6recs2MJ9JOlGG6
|
||||||
|
oKKsyk9/cJNiD1V1AC5q1kLfH5tMKOK/AxnJnvFEvZDnq5Xg0pZAW95j9vdiEwfc
|
||||||
|
qYeOm44nJPn7rHEOCzT93E1CdtHh2LYha2+kAjECgYEAh4qODleBi+2fnf2eq494
|
||||||
|
/tAot3szu2+gjyCN00vGjtzoAuDKTYgo0cbU1ILk0Pgpp1NcIvdHz/wQnG9RLX7e
|
||||||
|
Dfy1Q+UkyBK67SJUPvcYqBEaZ6ddyijJDunqh+U3nIBGP+IntKIKMIKiLF6wzTKz
|
||||||
|
NRpK1HNmllp+O692ZtxoNDU=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
21
backend/dev_tls_m.localhost.crt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDijCCAnKgAwIBAgIUWkx2ad/F7QIj1JDaYfbLhiRV+EswDQYJKoZIhvcNAQEL
|
||||||
|
BQAwHjEcMBoGA1UEAwwTRWxlbWVudCBDYWxsIERldiBDQTAeFw0yNjA1MTgwOTM0
|
||||||
|
MzFaFw0yODA3MjYwOTM0MzFaMBgxFjAUBgNVBAMMDSoubS5sb2NhbGhvc3QwggEi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0hora/UCYMtrLJc6BOjonPUPi
|
||||||
|
bYpbNiZYnvnqI4doKbV0LBT2TfokT7tpgdPCtHKV0RknsVSL8vhlXpkRqIiWPml8
|
||||||
|
sZaa0+5NDGCQxexS2WVBlsoNCmAaqi/HNSFop6xaxGpQ3bu0iV3oIkUihveXAl6H
|
||||||
|
C0VYyGifQ8D5onzepW2ayhemu47YRNSo8wETY5vIi0i/iajTTaw6JvwS+8Kv5/QV
|
||||||
|
5prdvcFlG/oBs12p0+KoRyxskyzcdBdyIarvfY+9nDZwym5GfN32xO/iqtDuDQzw
|
||||||
|
Q09h2OsfHJCw70IpHcgXLlEQF2DsFbmbVpWSU6HcMm6B7Yw1YeE64W4PRJp3AgMB
|
||||||
|
AAGjgcUwgcIwHwYDVR0jBBgwFoAUQ38kTOMilKlur7v9ilElxm/L7MAwCQYDVR0T
|
||||||
|
BAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwUwYDVR0RBEww
|
||||||
|
SoIJbG9jYWxob3N0ggttLmxvY2FsaG9zdIINKi5tLmxvY2FsaG9zdIIXKi5vdGhl
|
||||||
|
cnNpdGUubS5sb2NhbGhvc3SCCCoubmlwLmlvMB0GA1UdDgQWBBSd0sKIKmZzTnxT
|
||||||
|
gNHHjsJNnFcYaTANBgkqhkiG9w0BAQsFAAOCAQEAeffRTrD9o9PVRIoul5r2chwP
|
||||||
|
WF7JtvPdC5xWy9rlCfmIKRNzNRnpVw/mDF/jdhlWcENt3psN8Vb1NM3SECKve9KL
|
||||||
|
8bDD2rJEoLBHIFQPS+XpEPqVGLHQcfBtGgs2XdILKvgXJyBHY/pgNZkQmXxYDVoc
|
||||||
|
bH9PjJJ4V3t6+tiVWZ792739EU/pHaSz7tab+ycTiggs7mo18E5jpYILhWsDqIVs
|
||||||
|
Kz3uczK2OR8537Ix64Z9kmKiklVAqE53odV7Qx2B+7DoOD/7KBN7SMy1KvR1ae6I
|
||||||
|
p1ivtDKpBZWbb1ccFxp2cQ30qRHLJrt2YRwz268gx/A6rGXuW6UQPYf4ISNR4Q==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
backend/dev_tls_m.localhost.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0hora/UCYMtrL
|
||||||
|
Jc6BOjonPUPibYpbNiZYnvnqI4doKbV0LBT2TfokT7tpgdPCtHKV0RknsVSL8vhl
|
||||||
|
XpkRqIiWPml8sZaa0+5NDGCQxexS2WVBlsoNCmAaqi/HNSFop6xaxGpQ3bu0iV3o
|
||||||
|
IkUihveXAl6HC0VYyGifQ8D5onzepW2ayhemu47YRNSo8wETY5vIi0i/iajTTaw6
|
||||||
|
JvwS+8Kv5/QV5prdvcFlG/oBs12p0+KoRyxskyzcdBdyIarvfY+9nDZwym5GfN32
|
||||||
|
xO/iqtDuDQzwQ09h2OsfHJCw70IpHcgXLlEQF2DsFbmbVpWSU6HcMm6B7Yw1YeE6
|
||||||
|
4W4PRJp3AgMBAAECggEAIgdIbk4VmnrfjjKCsg5JPvNH9AsE7PuQj9zrq+xljkdq
|
||||||
|
aksS6ni5YZXb9F/iDE4aWU4waTB+iODUXLtPrCnyESwTk0sgYe/39/MQ0slUKivL
|
||||||
|
b+keDgY6JlyVI/5KXWFZ1kQ27CZXxwiruGGZWZBKZF8wdVE1Ea65Neg+HHA6DHee
|
||||||
|
Jck002gtgO/J1MMbB1MzdtGcsejYLrA+mO6YddQhA65xdQMljTEfyUwgTVv0pWde
|
||||||
|
biyKegGK77vlsOyoCkMpVYORG5NMV1Kxs+htA79yuIW71tWHqVbcRMyoM+BaHzPh
|
||||||
|
7uprs+8vYDFrO39LseczA8gURWwUsCgQ0yQ6Ix5W7QKBgQDnSK4AzjPpDEArdHuV
|
||||||
|
VGKyzrfPtzH0VV/yTH9hvByNG6i/x8sE/r2KPi5nRMi4PAjjqmxyO1G5qwDOfzvK
|
||||||
|
sBvwFrTRpmbnqGITVKPPivdoI9+RveN+FxhOXVA8NylAOv/dtSoakYwg3e507UsC
|
||||||
|
RuFW3Re0Oc+0XFq4C8rQyLkIOwKBgQDH0T9gww+XbwIGCtiNpnEziU9FXBKSVwXf
|
||||||
|
dCxYcTLPATq3BqHmP4OUA0v+sa3wPcnBkXF7q6eoB9+S6ZYQA/b2BXGU5/j9xYd4
|
||||||
|
29cF4DlPkhTwF9S8b+h1zhlGIn96Lw/vZuj7Bc3wuwxvB17d8dpyo8bZynIe7BvF
|
||||||
|
KSPJz+2O9QKBgFFyd8xS0VcFeGeVKpwozmUXhQWCBvZ7RkGGjOk3HHrYvbFjw2vr
|
||||||
|
5YWUZjT5tRGkGqFJ98y2dQ5EWRFfHwg+wmfnJyAZUG3OD1OtX86Lqpqi321siHtz
|
||||||
|
2JxoIgRCjKVQ4aAK11vp24YLgZjto5eWrG4xh9Jw9WMXjt73UCH8PaTXAoGBAIff
|
||||||
|
TY1qlmuO3H1nWqHXkBpPQEwVs7s22ZN817q8HqSMXXSfWe/LOJmpND/YakJ2gX7S
|
||||||
|
e6xwqOylje3EUHpLd98LDJUIuFM3wkr4klo4gkANQZeRXONV5WhV4PHD+5MF9XwB
|
||||||
|
KmOnKsaLKoVFKckZ8EUMAOePtdI5ExkaRG+yqAMRAoGAJyUFK+V9ST1N/6wYgqor
|
||||||
|
vywMSRE2cF2WvVIxdMvWffmpoj40bG6lAlaSWm29E2T8SVvAKsRid0wDgCQ4QTEn
|
||||||
|
ft7yUDjqVALCJVCrOFHDY0BPStkm6njMWagr/0lGr9zUWqbBOKJhNfDJlykv8gaF
|
||||||
|
8kWTgabrMCKmpTi7fBWbzZA=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
40
backend/dev_tls_setup
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Step 1: Create a Root CA key and cert
|
||||||
|
openssl genrsa -out dev_tls_local-ca.key 2048
|
||||||
|
openssl req -x509 -new -nodes \
|
||||||
|
-days 800 \
|
||||||
|
-subj "/CN=Element Call Dev CA" \
|
||||||
|
-key dev_tls_local-ca.key \
|
||||||
|
-out dev_tls_local-ca.crt \
|
||||||
|
-sha256 -addext "basicConstraints=CA:TRUE"
|
||||||
|
|
||||||
|
# Step 2: Create a private key and CSR for *.m.localhost
|
||||||
|
openssl req -new -nodes -newkey rsa:2048 \
|
||||||
|
-keyout dev_tls_m.localhost.key \
|
||||||
|
-out dev_tls_m.localhost.csr \
|
||||||
|
-subj "/CN=*.m.localhost"
|
||||||
|
|
||||||
|
# Step 3: Sign the CSR with your CA
|
||||||
|
openssl x509 \
|
||||||
|
-req -in dev_tls_m.localhost.csr \
|
||||||
|
-CA dev_tls_local-ca.crt -CAkey dev_tls_local-ca.key \
|
||||||
|
-CAcreateserial \
|
||||||
|
-out dev_tls_m.localhost.crt \
|
||||||
|
-days 800 \
|
||||||
|
-sha256 \
|
||||||
|
-extfile <( cat <<EOF
|
||||||
|
authorityKeyIdentifier=keyid,issuer
|
||||||
|
basicConstraints=CA:FALSE
|
||||||
|
keyUsage = digitalSignature, keyEncipherment
|
||||||
|
extendedKeyUsage = serverAuth
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
|
||||||
|
[alt_names]
|
||||||
|
DNS.1 = localhost
|
||||||
|
DNS.2 = m.localhost
|
||||||
|
DNS.3 = *.m.localhost
|
||||||
|
DNS.4 = *.othersite.m.localhost
|
||||||
|
DNS.5 = *.nip.io
|
||||||
|
EOF
|
||||||
|
)
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"default_server_config": {
|
"default_server_config": {
|
||||||
"m.homeserver": {
|
"m.homeserver": {
|
||||||
"base_url": "http://synapse.localhost:8008",
|
"base_url": "https://synapse.m.localhost",
|
||||||
"server_name": "synapse.localhost"
|
"server_name": "synapse.m.localhost"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"disable_custom_urls": false,
|
"disable_custom_urls": false,
|
||||||
|
|||||||
53
backend/ew.test.othersite.config.json
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"default_server_config": {
|
||||||
|
"m.homeserver": {
|
||||||
|
"base_url": "https://synapse.othersite.m.localhost",
|
||||||
|
"server_name": "synapse.othersite.m.localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disable_custom_urls": false,
|
||||||
|
"disable_guests": false,
|
||||||
|
"disable_login_language_selector": false,
|
||||||
|
"disable_3pid_login": false,
|
||||||
|
"force_verification": false,
|
||||||
|
"brand": "Element",
|
||||||
|
"integrations_ui_url": "https://scalar.vector.im/",
|
||||||
|
"integrations_rest_url": "https://scalar.vector.im/api",
|
||||||
|
"integrations_widgets_urls": [
|
||||||
|
"https://scalar.vector.im/_matrix/integrations/v1",
|
||||||
|
"https://scalar.vector.im/api",
|
||||||
|
"https://scalar-staging.vector.im/_matrix/integrations/v1",
|
||||||
|
"https://scalar-staging.vector.im/api",
|
||||||
|
"https://scalar-staging.riot.im/scalar/api"
|
||||||
|
],
|
||||||
|
"default_widget_container_height": 280,
|
||||||
|
"default_country_code": "GB",
|
||||||
|
"show_labs_settings": false,
|
||||||
|
"features": {
|
||||||
|
"feature_element_call_video_rooms": true,
|
||||||
|
"feature_video_rooms": true,
|
||||||
|
"feature_group_calls": true,
|
||||||
|
"feature_release_announcement": false
|
||||||
|
},
|
||||||
|
"default_federate": true,
|
||||||
|
"default_theme": "light",
|
||||||
|
"room_directory": {
|
||||||
|
"servers": ["matrix.org"]
|
||||||
|
},
|
||||||
|
"enable_presence_by_hs_url": {
|
||||||
|
"https://matrix.org": false,
|
||||||
|
"https://matrix-client.matrix.org": false
|
||||||
|
},
|
||||||
|
"setting_defaults": {
|
||||||
|
"breadcrumbs": true,
|
||||||
|
"feature_group_calls": true
|
||||||
|
},
|
||||||
|
"jitsi": {
|
||||||
|
"preferred_domain": "meet.element.io"
|
||||||
|
},
|
||||||
|
"element_call": {
|
||||||
|
"participant_limit": 8,
|
||||||
|
"brand": "Element Call"
|
||||||
|
},
|
||||||
|
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
|
||||||
|
}
|
||||||
86
backend/playwright_homeserver-othersite.yaml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
server_name: "synapse.othersite.m.localhost"
|
||||||
|
public_baseurl: https://synapse.othersite.m.localhost/
|
||||||
|
|
||||||
|
pid_file: /data/homeserver.pid
|
||||||
|
|
||||||
|
listeners:
|
||||||
|
- port: 18008
|
||||||
|
tls: false
|
||||||
|
type: http
|
||||||
|
x_forwarded: true
|
||||||
|
resources:
|
||||||
|
- names: [client, federation, openid]
|
||||||
|
compress: false
|
||||||
|
|
||||||
|
database:
|
||||||
|
name: sqlite3
|
||||||
|
args:
|
||||||
|
database: /data/homeserver.db
|
||||||
|
|
||||||
|
media_store_path: /data/media_store
|
||||||
|
signing_key_path: "/data/SERVERNAME.signing.key"
|
||||||
|
|
||||||
|
# Due to custom TLS certificate with domains
|
||||||
|
# - m.localhost, localhost
|
||||||
|
# - *.m.localhost
|
||||||
|
# - *.othersite.m.localhost
|
||||||
|
# we disable certificate verification to allow for federation.
|
||||||
|
# WARNING: DO NOT USE IN PRODUCTION!!!
|
||||||
|
federation_verify_certificates: false
|
||||||
|
ip_range_blacklist: []
|
||||||
|
trusted_key_servers:
|
||||||
|
- server_name: "synapse.m.localhost"
|
||||||
|
accept_keys_insecurely: true
|
||||||
|
|
||||||
|
experimental_features:
|
||||||
|
# MSC3266: Room summary API. Used for knocking over federation
|
||||||
|
msc3266_enabled: true
|
||||||
|
# MSC4222 needed for syncv2 state_after. This allow clients to
|
||||||
|
# correctly track the state of the room.
|
||||||
|
msc4222_enabled: true
|
||||||
|
# sticky events for MatrixRTC user state
|
||||||
|
msc4354_enabled: true
|
||||||
|
|
||||||
|
# The maximum allowed duration by which sent events can be delayed, as
|
||||||
|
# per MSC4140. Must be a positive value if set. Defaults to no
|
||||||
|
# duration (null), which disallows sending delayed events.
|
||||||
|
max_event_delay_duration: 24h
|
||||||
|
|
||||||
|
# Required for Element Call in Single Page Mode due to on-the-fly user registration
|
||||||
|
enable_registration: true
|
||||||
|
enable_registration_without_verification: true
|
||||||
|
|
||||||
|
# Shared secret for admin user registration via API (for testing only!)
|
||||||
|
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||||
|
|
||||||
|
report_stats: false
|
||||||
|
serve_server_wellknown: true
|
||||||
|
|
||||||
|
# Ratelimiting settings for client actions (registration, login, messaging).
|
||||||
|
#
|
||||||
|
# Each ratelimiting configuration is made of two parameters:
|
||||||
|
# - per_second: number of requests a client can send per second.
|
||||||
|
# - burst_count: number of requests a client can send before being throttled.
|
||||||
|
|
||||||
|
rc_message:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
|
|
||||||
|
rc_delayed_event_mgmt:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
|
|
||||||
|
rc_login:
|
||||||
|
address:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
|
account:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
|
failed_attempts:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
|
|
||||||
|
rc_registration:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
server_name: "synapse.localhost"
|
server_name: "synapse.m.localhost"
|
||||||
public_baseurl: http://synapse.localhost:8008/
|
public_baseurl: https://synapse.m.localhost/
|
||||||
|
|
||||||
pid_file: /data/homeserver.pid
|
pid_file: /data/homeserver.pid
|
||||||
|
|
||||||
@@ -19,8 +19,18 @@ database:
|
|||||||
|
|
||||||
media_store_path: /data/media_store
|
media_store_path: /data/media_store
|
||||||
signing_key_path: "/data/SERVERNAME.signing.key"
|
signing_key_path: "/data/SERVERNAME.signing.key"
|
||||||
|
|
||||||
|
# Due to custom TLS certificate with domains
|
||||||
|
# - m.localhost, localhost
|
||||||
|
# - *.m.localhost
|
||||||
|
# - *.othersite.m.localhost
|
||||||
|
# we disable certificate verification to allow for federation.
|
||||||
|
# WARNING: DO NOT USE IN PRODUCTION!!!
|
||||||
|
federation_verify_certificates: false
|
||||||
|
ip_range_blacklist: []
|
||||||
trusted_key_servers:
|
trusted_key_servers:
|
||||||
- server_name: "matrix.org"
|
- server_name: "synapse.othersite.m.localhost"
|
||||||
|
accept_keys_insecurely: true
|
||||||
|
|
||||||
experimental_features:
|
experimental_features:
|
||||||
# MSC3266: Room summary API. Used for knocking over federation
|
# MSC3266: Room summary API. Used for knocking over federation
|
||||||
@@ -28,12 +38,24 @@ experimental_features:
|
|||||||
# MSC4222 needed for syncv2 state_after. This allow clients to
|
# MSC4222 needed for syncv2 state_after. This allow clients to
|
||||||
# correctly track the state of the room.
|
# correctly track the state of the room.
|
||||||
msc4222_enabled: true
|
msc4222_enabled: true
|
||||||
|
# sticky events for MatrixRTC user state
|
||||||
|
msc4354_enabled: true
|
||||||
|
|
||||||
# The maximum allowed duration by which sent events can be delayed, as
|
# The maximum allowed duration by which sent events can be delayed, as
|
||||||
# per MSC4140. Must be a positive value if set. Defaults to no
|
# per MSC4140. Must be a positive value if set. Defaults to no
|
||||||
# duration (null), which disallows sending delayed events.
|
# duration (null), which disallows sending delayed events.
|
||||||
max_event_delay_duration: 24h
|
max_event_delay_duration: 24h
|
||||||
|
|
||||||
|
# Required for Element Call in Single Page Mode due to on-the-fly user registration
|
||||||
|
enable_registration: true
|
||||||
|
enable_registration_without_verification: true
|
||||||
|
|
||||||
|
# Shared secret for admin user registration via API (for testing only!)
|
||||||
|
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||||
|
|
||||||
|
report_stats: false
|
||||||
|
serve_server_wellknown: true
|
||||||
|
|
||||||
# Ratelimiting settings for client actions (registration, login, messaging).
|
# Ratelimiting settings for client actions (registration, login, messaging).
|
||||||
#
|
#
|
||||||
# Each ratelimiting configuration is made of two parameters:
|
# Each ratelimiting configuration is made of two parameters:
|
||||||
@@ -44,6 +66,10 @@ rc_message:
|
|||||||
per_second: 10000
|
per_second: 10000
|
||||||
burst_count: 10000
|
burst_count: 10000
|
||||||
|
|
||||||
|
rc_delayed_event_mgmt:
|
||||||
|
per_second: 10000
|
||||||
|
burst_count: 10000
|
||||||
|
|
||||||
rc_login:
|
rc_login:
|
||||||
address:
|
address:
|
||||||
per_second: 10000
|
per_second: 10000
|
||||||
@@ -58,10 +84,3 @@ rc_login:
|
|||||||
rc_registration:
|
rc_registration:
|
||||||
per_second: 10000
|
per_second: 10000
|
||||||
burst_count: 10000
|
burst_count: 10000
|
||||||
|
|
||||||
# Required for Element Call in Single Page Mode due to on-the-fly user registration
|
|
||||||
enable_registration: true
|
|
||||||
enable_registration_without_verification: true
|
|
||||||
|
|
||||||
report_stats: false
|
|
||||||
serve_server_wellknown: true
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
bind 0.0.0.0
|
|
||||||
protected-mode yes
|
|
||||||
port 6379
|
|
||||||
timeout 0
|
|
||||||
tcp-keepalive 300
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDtzCCAp+gAwIBAgIUCmJjl3HAeLmrPwRg+/OzikW6peQwDQYJKoZIhvcNAQEL
|
|
||||||
BQAwazELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwGTG9u
|
|
||||||
ZG9uMQ4wDAYDVQQKDAVBbHJvczEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDESMBAG
|
|
||||||
A1UEAwwJbG9jYWxob3N0MB4XDTI0MTEwNDIxNDcwMFoXDTM0MTEwMjIxNDcwMFow
|
|
||||||
azELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEPMA0GA1UEBwwGTG9uZG9u
|
|
||||||
MQ4wDAYDVQQKDAVBbHJvczEWMBQGA1UECwwNSVQgRGVwYXJ0bWVudDESMBAGA1UE
|
|
||||||
AwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs368
|
|
||||||
ExLSudP8luNoY5UfaPqBSVJUPYBi+JGyd36tyN75p5OI7xSfHTttQxuD4KrExBFP
|
|
||||||
C8mAhE1eoZPBVBOZJ4FYWBJfMaQnCjeqU+laP36td65kSJYbUYlKYH1WpxEpCdgx
|
|
||||||
wWOKkP/kPX5YXbYqODx9aBJXgoT3yAJW7AniIoL+eLFnS9Xo86TPqCDBTJU9ocwK
|
|
||||||
gPIDLhDv60724rhZT1kbGp7ECqRovndoDTQjuws2D3yNMfQ+4rrQGPXHGmP5PcaR
|
|
||||||
0R7uueB+6APyC7MJbuhbxxg/+DFHrRi3lJsgwxuh2hi/+vWw8zgKlgYIwHFA9X0l
|
|
||||||
cX0UlQdENMH3bgcGIwIDAQABo1MwUTAdBgNVHQ4EFgQUUFGxw7zoiHXGwRqtagjZ
|
|
||||||
RPYc85cwHwYDVR0jBBgwFoAUUFGxw7zoiHXGwRqtagjZRPYc85cwDwYDVR0TAQH/
|
|
||||||
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEALokb1z2lu3qW141b2wm14ilZQKCZ
|
|
||||||
reNNuUR95Uom96FXPH4QVEH+mYTXXJ5UrfNhQYKQFpdE+5S4HL/UqEOxtWvbAHpK
|
|
||||||
nsLQ62J8m+0+uwiJGqeQpWr03KJgXDAVE9X3XwMlp/+buxSLhc+GIHWuXW56itV2
|
|
||||||
jiZJYjhO5SnhhgTWNoVZk93qXuuWEN0yacw7c3Fr1IvFYYYWufbXTk70dbZihPDK
|
|
||||||
VD141o8tpp6FerSKHNYDqkVFDyTz3DVOhQQJ59zfMre7bFr+PpTTl4vIuGzXEY+E
|
|
||||||
HPjUSlOzwkCoh5fu7Fs3qG55rJt8akhTEoKpiBTaLucgAjVWNHeci1+Yxg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzfrwTEtK50/yW
|
|
||||||
42hjlR9o+oFJUlQ9gGL4kbJ3fq3I3vmnk4jvFJ8dO21DG4PgqsTEEU8LyYCETV6h
|
|
||||||
k8FUE5kngVhYEl8xpCcKN6pT6Vo/fq13rmRIlhtRiUpgfVanESkJ2DHBY4qQ/+Q9
|
|
||||||
flhdtio4PH1oEleChPfIAlbsCeIigv54sWdL1ejzpM+oIMFMlT2hzAqA8gMuEO/r
|
|
||||||
TvbiuFlPWRsansQKpGi+d2gNNCO7CzYPfI0x9D7iutAY9ccaY/k9xpHRHu654H7o
|
|
||||||
A/ILswlu6FvHGD/4MUetGLeUmyDDG6HaGL/69bDzOAqWBgjAcUD1fSVxfRSVB0Q0
|
|
||||||
wfduBwYjAgMBAAECggEACTqdSExxzJ+LX5ARFaWyOBSWly2GKqSyR14+aInOklhx
|
|
||||||
9QgkmfOxJrCf3TvJ8RWhXloW0Aqr8qGDxG0Ixgjn7rG7gskXCey1xn8MNppLS0kj
|
|
||||||
ztaG+NB3AR89ABm8XdoHsSY45geh3/Ni9I0i1VardGQafUJhgNLTZqjwIodzkBtJ
|
|
||||||
S/bi4uFk1lGNfuvWQvWqzGXUvd1l1YupV6iA4GfhXlUvrSBZwftLBD6xEvQaSqsA
|
|
||||||
pHvBxTfMXG4RMAkNPDIElkuQ8++CGi1gIRkJfmrv4OgbbitteMnxqqqGYV0zSNCg
|
|
||||||
R/5FG6umIV7lDLBHZCSCk7wmfmq2UUvzhHThHy4yMQKBgQDu4TwFJCIcVIj7Wj4r
|
|
||||||
DUBFvz6Lgbltqb+YAMUBtpiDcAQxDJWmedh6dK04ts5CFAFRlRjjuz2uFn7qlVBm
|
|
||||||
uye9R7tL+tOv5viqDXU78a4snFywoXub6yzpbxrW8B4W1pdIUvQmhwCcDwvO1V24
|
|
||||||
7Vj2vxcM5I9dsk1aCQSi3VY5yQKBgQDAW/VoTRwhU6OUc6sji5Z5dnkMjkP6NZK9
|
|
||||||
CSrTWLAMGaLPY+g6fFS7JMNSvfWm/okypD6rcN7p0cxMK3mfFKmMiyPRde0wdrci
|
|
||||||
sGFjGxM/2d2D7KTMC9iMYwA0K17UIna+UiYPfhR/muIg/dCyjlkKDFs9Z4jk//r1
|
|
||||||
91bmznt2iwKBgFdiYXhn/Wprqih4nKFXGZnqGdEixVhObl4GegrkZuo+AeqHdf8O
|
|
||||||
N5ikMfG7PbyCYPEdH5u/FRMn+4mI0X6jHChroyJqQSHp1jEu9yHUiSicknOyvusM
|
|
||||||
nsNN932FHRyxp2m3nsSxQhHUlzc0ajKJ8K9iu+XlfmSCIzW6cs25Nh+xAoGBAJro
|
|
||||||
M0wIdPPdsCj3sUVRvx8XqknTM6kGhaIYBNXoYPWNm5BaC4U15OJEq8sxUOdnqcMP
|
|
||||||
g6x6m/k+S8C3bh0O/a9Bydl/l0BlCfw0gGjYP/s2ju4Tn272xy/e9iYNGzPIgUmp
|
|
||||||
TB9D0GwmpZ4d6HgyrD+sTbm4bATGpCp6QhBjDggbAoGBAJVMMtZ4pF8D6mLMRZGR
|
|
||||||
pQjNPy+MH13XYmDRc/BSF8KJ4yKk3tohr9LSXzxR0SEB43NoL1bHkucZrNjGyL8x
|
|
||||||
jktnwkoIs96kO2mPrl1TqWkXs5RjGkkSTbAJovIcvkRU31SWap/WzN2kHpmRVcQc
|
|
||||||
KEFKXT5fUYZCLLWxhgZFlGPp
|
|
||||||
-----END PRIVATE KEY-----
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
listen 443 ssl;
|
|
||||||
listen 8448 ssl;
|
|
||||||
listen [::]:443 ssl;
|
|
||||||
listen [::]:8448 ssl;
|
|
||||||
server_name synapse.localhost;
|
|
||||||
ssl_certificate /root/ssl/cert.pem;
|
|
||||||
ssl_certificate_key /root/ssl/key.pem;
|
|
||||||
|
|
||||||
# well-known config adding rtc_foci backend
|
|
||||||
# Note well-known is currently not effective due to:
|
|
||||||
# https://spec.matrix.org/v1.12/client-server-api/#well-known-uri the spec
|
|
||||||
# says it must be at https://$server_name/... (implied port 443) Hence, we
|
|
||||||
# currently rely for local development environment on deprecated config.json
|
|
||||||
# setting for livekit_service_url
|
|
||||||
location /.well-known/matrix/client {
|
|
||||||
return 200 '{"m.homeserver": {"base_url": "http://synapse.localhost:8008"}, "org.matrix.msc4143.rtc_foci": [{"type": "livekit", "livekit_service_url": "http://localhost:8080"}]}';
|
|
||||||
default_type application/json;
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Reverse proxy for Matrix Synapse Homeserver
|
|
||||||
# This is also required for development environment.
|
|
||||||
# Reason: the lk-jwt-service uses the federation API for the openid token
|
|
||||||
# verification, which requires TLS
|
|
||||||
location / {
|
|
||||||
proxy_pass "http://homeserver:8008";
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ coverage:
|
|||||||
informational: true
|
informational: true
|
||||||
patch:
|
patch:
|
||||||
default:
|
default:
|
||||||
# Encourage (but don't enforce) 80% coverage on all lines that a PR
|
# Enforce 80% coverage on all lines that a PR
|
||||||
# touches
|
# touches
|
||||||
target: 80%
|
target: 80%
|
||||||
informational: true
|
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
{
|
{
|
||||||
"default_server_config": {
|
"default_server_config": {
|
||||||
"m.homeserver": {
|
"m.homeserver": {
|
||||||
"base_url": "http://synapse.localhost:8008",
|
"base_url": "https://synapse.m.localhost",
|
||||||
"server_name": "synapse.localhost"
|
"server_name": "synapse.m.localhost"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"livekit": {
|
|
||||||
"livekit_service_url": "http://localhost:8009"
|
|
||||||
},
|
|
||||||
"features": {
|
"features": {
|
||||||
"feature_use_device_session_member_events": true
|
"feature_use_device_session_member_events": true
|
||||||
},
|
},
|
||||||
"ssla": "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf"
|
"ssla": "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
|
||||||
|
"matrix_rtc_session": {
|
||||||
|
"wait_for_key_rotation_ms": 3000,
|
||||||
|
"membership_event_expiry_ms": 180000000,
|
||||||
|
"delayed_leave_event_delay_ms": 18000,
|
||||||
|
"delayed_leave_event_restart_ms": 4000,
|
||||||
|
"network_error_retry_ms": 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,13 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"feature_use_device_session_member_events": true
|
"feature_use_device_session_member_events": true
|
||||||
},
|
},
|
||||||
"ssla": "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf"
|
"ssla": "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
|
||||||
|
"matrix_rtc_mode": "compatibility",
|
||||||
|
"matrix_rtc_session": {
|
||||||
|
"wait_for_key_rotation_ms": 3000,
|
||||||
|
"membership_event_expiry_ms": 180000000,
|
||||||
|
"delayed_leave_event_delay_ms": 18000,
|
||||||
|
"delayed_leave_event_restart_ms": 4000,
|
||||||
|
"network_error_retry_ms": 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,20 @@
|
|||||||
"server_name": "call-unstable.ems.host"
|
"server_name": "call-unstable.ems.host"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ssla": "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
|
||||||
|
"matrix_rtc_session": {
|
||||||
|
"wait_for_key_rotation_ms": 3000,
|
||||||
|
"membership_event_expiry_ms": 180000000,
|
||||||
|
"delayed_leave_event_delay_ms": 18000,
|
||||||
|
"delayed_leave_event_restart_ms": 4000,
|
||||||
|
"network_error_retry_ms": 100
|
||||||
|
},
|
||||||
"posthog": {
|
"posthog": {
|
||||||
"api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU",
|
"api_key": "phc_rXGHx9vDmyEvyRxPziYtdVIv0ahEv8A9uLWFcCi1WcU",
|
||||||
"api_host": "https://posthog-element-call.element.io"
|
"api_host": "https://posthog-element-call.element.io"
|
||||||
},
|
},
|
||||||
"rageshake": {
|
"rageshake": {
|
||||||
"submit_url": "https://element.io/bugreports/submit"
|
"submit_url": "https://rageshakes.element.io/api/submit"
|
||||||
},
|
},
|
||||||
"sentry": {
|
"sentry": {
|
||||||
"environment": "netlify-pr-preview",
|
"environment": "netlify-pr-preview",
|
||||||
|
|||||||
16
config/config_netlify_preview_sdk.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"default_server_config": {
|
||||||
|
"m.homeserver": {
|
||||||
|
"base_url": "https://call-unstable.ems.host",
|
||||||
|
"server_name": "call-unstable.ems.host"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ssla": "https://static.element.io/legal/element-software-and-services-license-agreement-uk-1.pdf",
|
||||||
|
"matrix_rtc_session": {
|
||||||
|
"wait_for_key_rotation_ms": 3000,
|
||||||
|
"membership_event_expiry_ms": 180000000,
|
||||||
|
"delayed_leave_event_delay_ms": 18000,
|
||||||
|
"delayed_leave_event_restart_ms": 4000,
|
||||||
|
"network_error_retry_ms": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
# OpenTelemetry Collector for development
|
# OpenTelemetry Collector for development
|
||||||
|
|
||||||
|
## Edit:
|
||||||
|
|
||||||
|
Open telemetry has been removed in: https://github.com/element-hq/element-call/pull/3586
|
||||||
|
Check this PR to get back the implementation or to use it as reference to add it back.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
This directory contains a docker compose file that starts a jaeger all-in-one instance
|
This directory contains a docker compose file that starts a jaeger all-in-one instance
|
||||||
with an in-memory database, along with a standalone OpenTelemetry collector that forwards
|
with an in-memory database, along with a standalone OpenTelemetry collector that forwards
|
||||||
traces into the jaeger. Jaeger has a built-in OpenTelemetry collector, but it can't be
|
traces into the jaeger. Jaeger has a built-in OpenTelemetry collector, but it can't be
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
networks:
|
|
||||||
ecbackend:
|
|
||||||
|
|
||||||
services:
|
|
||||||
auth-service:
|
|
||||||
image: ghcr.io/element-hq/lk-jwt-service:latest-ci
|
|
||||||
hostname: auth-server
|
|
||||||
environment:
|
|
||||||
- LK_JWT_PORT=8080
|
|
||||||
- LIVEKIT_URL=ws://localhost:7880
|
|
||||||
- LIVEKIT_KEY=devkey
|
|
||||||
- LIVEKIT_SECRET=secret
|
|
||||||
# If the configured homeserver runs on localhost, it'll probably be using
|
|
||||||
# a self-signed certificate
|
|
||||||
- LIVEKIT_INSECURE_SKIP_VERIFY_TLS=YES_I_KNOW_WHAT_I_AM_DOING
|
|
||||||
deploy:
|
|
||||||
restart_policy:
|
|
||||||
condition: on-failure
|
|
||||||
ports:
|
|
||||||
# HOST_PORT:CONTAINER_PORT
|
|
||||||
- 8009:8080
|
|
||||||
networks:
|
|
||||||
- ecbackend
|
|
||||||
|
|
||||||
livekit:
|
|
||||||
image: livekit/livekit-server:latest
|
|
||||||
command: --dev --config /etc/livekit.yaml
|
|
||||||
restart: unless-stopped
|
|
||||||
# The SFU seems to work far more reliably when we let it share the host
|
|
||||||
# network rather than opening specific ports (but why?? we're not missing
|
|
||||||
# any…)
|
|
||||||
ports:
|
|
||||||
# HOST_PORT:CONTAINER_PORT
|
|
||||||
- 7880:7880/tcp
|
|
||||||
- 7881:7881/tcp
|
|
||||||
- 7882:7882/tcp
|
|
||||||
- 50100-50200:50100-50200/udp
|
|
||||||
volumes:
|
|
||||||
- ./backend/dev_livekit.yaml:/etc/livekit.yaml:Z
|
|
||||||
networks:
|
|
||||||
- ecbackend
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:6-alpine
|
|
||||||
command: redis-server /etc/redis.conf
|
|
||||||
ports:
|
|
||||||
# HOST_PORT:CONTAINER_PORT
|
|
||||||
- 6379:6379
|
|
||||||
volumes:
|
|
||||||
- ./backend/redis.conf:/etc/redis.conf:Z
|
|
||||||
networks:
|
|
||||||
- ecbackend
|
|
||||||
|
|
||||||
synapse:
|
|
||||||
hostname: homeserver
|
|
||||||
image: docker.io/matrixdotorg/synapse:latest
|
|
||||||
environment:
|
|
||||||
- SYNAPSE_CONFIG_PATH=/data/cfg/homeserver.yaml
|
|
||||||
# Needed for rootless podman-compose such that the uid/gid mapping does
|
|
||||||
# fit local user uid. If the container runs as root (uid 0) it is fine as
|
|
||||||
# it actually maps to your non-root user on the host (e.g. 1000).
|
|
||||||
# Otherwise uid mapping will not match your non-root user.
|
|
||||||
- UID=0
|
|
||||||
- GID=0
|
|
||||||
volumes:
|
|
||||||
- ./backend/synapse_tmp:/data:Z
|
|
||||||
- ./backend/dev_homeserver.yaml:/data/cfg/homeserver.yaml:Z
|
|
||||||
networks:
|
|
||||||
- ecbackend
|
|
||||||
|
|
||||||
element-web:
|
|
||||||
image: ghcr.io/element-hq/element-web:develop
|
|
||||||
pull_policy: always
|
|
||||||
volumes:
|
|
||||||
- ./backend/ew.test.config.json:/app/config.json
|
|
||||||
environment:
|
|
||||||
ELEMENT_WEB_PORT: 81
|
|
||||||
ports:
|
|
||||||
- "8081:81"
|
|
||||||
networks:
|
|
||||||
- ecbackend
|
|
||||||
|
|
||||||
nginx:
|
|
||||||
# openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout tls_localhost_key.pem -out tls_localhost_cert.pem -subj "/C=GB/ST=London/L=London/O=Alros/OU=IT Department/CN=localhost"
|
|
||||||
hostname: synapse.localhost
|
|
||||||
image: nginx:latest
|
|
||||||
volumes:
|
|
||||||
- ./backend/tls_localhost_nginx.conf:/etc/nginx/conf.d/default.conf:Z
|
|
||||||
- ./backend/tls_localhost_key.pem:/root/ssl/key.pem:Z
|
|
||||||
- ./backend/tls_localhost_cert.pem:/root/ssl/cert.pem:Z
|
|
||||||
ports:
|
|
||||||
# HOST_PORT:CONTAINER_PORT
|
|
||||||
- "8008:80"
|
|
||||||
- "4443:443"
|
|
||||||
depends_on:
|
|
||||||
- synapse
|
|
||||||
networks:
|
|
||||||
- ecbackend
|
|
||||||
183
docker-compose-dev.yml
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
networks:
|
||||||
|
ecbackend:
|
||||||
|
|
||||||
|
services:
|
||||||
|
auth-service:
|
||||||
|
image: ghcr.io/element-hq/lk-jwt-service:0.4.4
|
||||||
|
pull_policy: always
|
||||||
|
hostname: auth-server
|
||||||
|
environment:
|
||||||
|
- LIVEKIT_JWT_PORT=6080
|
||||||
|
- LIVEKIT_URL=wss://matrix-rtc.m.localhost/livekit/sfu
|
||||||
|
- LIVEKIT_KEY=devkey
|
||||||
|
- LIVEKIT_SECRET=secret
|
||||||
|
# If the configured homeserver runs on localhost, it'll probably be using
|
||||||
|
# a self-signed certificate
|
||||||
|
- LIVEKIT_INSECURE_SKIP_VERIFY_TLS=YES_I_KNOW_WHAT_I_AM_DOING
|
||||||
|
- LIVEKIT_FULL_ACCESS_HOMESERVERS=*
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
ports:
|
||||||
|
# HOST_PORT:CONTAINER_PORT
|
||||||
|
- 6080:6080
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
auth-service-1:
|
||||||
|
image: ghcr.io/element-hq/lk-jwt-service:0.4.4
|
||||||
|
pull_policy: always
|
||||||
|
hostname: auth-server-1
|
||||||
|
environment:
|
||||||
|
- LIVEKIT_JWT_PORT=16080
|
||||||
|
- LIVEKIT_URL=wss://matrix-rtc.othersite.m.localhost/livekit/sfu
|
||||||
|
- LIVEKIT_KEY=devkey
|
||||||
|
- LIVEKIT_SECRET=secret
|
||||||
|
# If the configured homeserver runs on localhost, it'll probably be using
|
||||||
|
# a self-signed certificate
|
||||||
|
- LIVEKIT_INSECURE_SKIP_VERIFY_TLS=YES_I_KNOW_WHAT_I_AM_DOING
|
||||||
|
- LIVEKIT_FULL_ACCESS_HOMESERVERS=*
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
ports:
|
||||||
|
# HOST_PORT:CONTAINER_PORT
|
||||||
|
- 16080:16080
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
livekit:
|
||||||
|
image: livekit/livekit-server:v1.10.1
|
||||||
|
pull_policy: always
|
||||||
|
hostname: livekit-sfu
|
||||||
|
command: --dev --config /etc/livekit.yaml
|
||||||
|
restart: unless-stopped
|
||||||
|
# The SFU seems to work far more reliably when we let it share the host
|
||||||
|
# network rather than opening specific ports (but why?? we're not missing
|
||||||
|
# any…)
|
||||||
|
ports:
|
||||||
|
# HOST_PORT:CONTAINER_PORT
|
||||||
|
- 7880:7880/tcp
|
||||||
|
- 7881:7881/tcp
|
||||||
|
- 7882:7882/tcp
|
||||||
|
- 50100-50200:50100-50200/udp
|
||||||
|
volumes:
|
||||||
|
- ./backend/dev_tls_m.localhost.crt:/local_cert.pem:Z
|
||||||
|
- ./backend/dev_livekit.yaml:/etc/livekit.yaml:Z
|
||||||
|
environment:
|
||||||
|
- SSL_CERT_FILE=/local_cert.pem
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
livekit-1:
|
||||||
|
image: livekit/livekit-server:v1.10.1
|
||||||
|
pull_policy: always
|
||||||
|
hostname: livekit-sfu-1
|
||||||
|
command: --dev --config /etc/livekit.yaml
|
||||||
|
restart: unless-stopped
|
||||||
|
# The SFU seems to work far more reliably when we let it share the host
|
||||||
|
# network rather than opening specific ports (but why?? we're not missing
|
||||||
|
# any…)
|
||||||
|
ports:
|
||||||
|
# HOST_PORT:CONTAINER_PORT
|
||||||
|
- 17880:17880/tcp
|
||||||
|
- 17881:17881/tcp
|
||||||
|
- 17882:17882/tcp
|
||||||
|
- 50300-50400:50300-50400/udp
|
||||||
|
volumes:
|
||||||
|
- ./backend/dev_tls_m.localhost.crt:/local_cert.pem:Z
|
||||||
|
- ./backend/dev_livekit-othersite.yaml:/etc/livekit.yaml:Z
|
||||||
|
environment:
|
||||||
|
- SSL_CERT_FILE=/local_cert.pem
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
synapse:
|
||||||
|
hostname: homeserver
|
||||||
|
image: ghcr.io/element-hq/synapse:latest
|
||||||
|
pull_policy: always
|
||||||
|
environment:
|
||||||
|
- SYNAPSE_CONFIG_PATH=/data/cfg/homeserver.yaml
|
||||||
|
# Needed for rootless podman-compose such that the uid/gid mapping does
|
||||||
|
# fit local user uid. If the container runs as root (uid 0) it is fine as
|
||||||
|
# it actually maps to your non-root user on the host (e.g. 1000).
|
||||||
|
# Otherwise uid mapping will not match your non-root user.
|
||||||
|
- UID=0
|
||||||
|
- GID=0
|
||||||
|
volumes:
|
||||||
|
- ./backend/synapse_tmp:/data:Z
|
||||||
|
- ./backend/dev_homeserver.yaml:/data/cfg/homeserver.yaml:Z
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
synapse-1:
|
||||||
|
hostname: homeserver-1
|
||||||
|
image: ghcr.io/element-hq/synapse:latest
|
||||||
|
pull_policy: always
|
||||||
|
environment:
|
||||||
|
- SYNAPSE_CONFIG_PATH=/data/cfg/homeserver.yaml
|
||||||
|
# Needed for rootless podman-compose such that the uid/gid mapping does
|
||||||
|
# fit local user uid. If the container runs as root (uid 0) it is fine as
|
||||||
|
# it actually maps to your non-root user on the host (e.g. 1000).
|
||||||
|
# Otherwise uid mapping will not match your non-root user.
|
||||||
|
- UID=0
|
||||||
|
- GID=0
|
||||||
|
volumes:
|
||||||
|
- ./backend/synapse_tmp_othersite:/data:Z
|
||||||
|
- ./backend/dev_homeserver-othersite.yaml:/data/cfg/homeserver.yaml:Z
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
element-web:
|
||||||
|
image: ghcr.io/element-hq/element-web:develop
|
||||||
|
pull_policy: always
|
||||||
|
volumes:
|
||||||
|
- ./backend/ew.test.config.json:/app/config.json:Z
|
||||||
|
environment:
|
||||||
|
ELEMENT_WEB_PORT: 8081
|
||||||
|
ports:
|
||||||
|
- "8081:8081"
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
element-web-1:
|
||||||
|
image: ghcr.io/element-hq/element-web:develop
|
||||||
|
pull_policy: always
|
||||||
|
volumes:
|
||||||
|
- ./backend/ew.test.othersite.config.json:/app/config.json:Z
|
||||||
|
environment:
|
||||||
|
ELEMENT_WEB_PORT: 18081
|
||||||
|
ports:
|
||||||
|
# HOST_PORT:CONTAINER_PORT
|
||||||
|
- "18081:18081"
|
||||||
|
networks:
|
||||||
|
- ecbackend
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
# see backend/dev_tls_setup for how to generate the tls certs
|
||||||
|
hostname: synapse.m.localhost
|
||||||
|
image: nginx:latest@sha256:4ae259ae64fbedb67918c07d167fdcb0e05855a1615480ca445bea485e7d65ff
|
||||||
|
pull_policy: always
|
||||||
|
volumes:
|
||||||
|
- ./backend/dev_nginx.conf:/etc/nginx/conf.d/default.conf:Z
|
||||||
|
- ./backend/dev_tls_m.localhost.key:/root/ssl/key.pem:Z
|
||||||
|
- ./backend/dev_tls_m.localhost.crt:/root/ssl/cert.pem:Z
|
||||||
|
ports:
|
||||||
|
# HOST_PORT:CONTAINER_PORT
|
||||||
|
- "443:443"
|
||||||
|
- "8008:80"
|
||||||
|
- "4443:443"
|
||||||
|
- "8448:8448"
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
- "auth-server:127.0.0.1"
|
||||||
|
- "auth-server-1:127.0.0.1"
|
||||||
|
depends_on:
|
||||||
|
- synapse
|
||||||
|
networks:
|
||||||
|
ecbackend:
|
||||||
|
aliases:
|
||||||
|
- synapse.m.localhost
|
||||||
|
- synapse.othersite.m.localhost
|
||||||
|
- matrix-rtc.m.localhost
|
||||||
|
- matrix-rtc.othersite.m.localhost
|
||||||
19
docker-compose-playwright.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# This file contains overrides to docker-compose-dev.yml and should
|
||||||
|
# only be used in combination with that file.
|
||||||
|
services:
|
||||||
|
synapse:
|
||||||
|
# Pin to a SHA so that upstream cannot break our tests. Renovate handles regular updates.
|
||||||
|
image: ghcr.io/element-hq/synapse:latest@sha256:da325af40104051923899e5f5a2f1d537e6e3ccf2f0f38285689ae9bbfdb190a
|
||||||
|
volumes:
|
||||||
|
- ./backend/playwright_homeserver.yaml:/data/cfg/homeserver.yaml:Z
|
||||||
|
synapse-1:
|
||||||
|
# Pin to a SHA so that upstream cannot break our tests. Renovate handles regular updates.
|
||||||
|
image: ghcr.io/element-hq/synapse:latest@sha256:da325af40104051923899e5f5a2f1d537e6e3ccf2f0f38285689ae9bbfdb190a
|
||||||
|
volumes:
|
||||||
|
- ./backend/playwright_homeserver-othersite.yaml:/data/cfg/homeserver.yaml:Z
|
||||||
|
element-web:
|
||||||
|
# Pin to a SHA so that upstream cannot break our tests. Renovate handles regular updates.
|
||||||
|
image: ghcr.io/element-hq/element-web:develop@sha256:d1841828a390d96da2ee64232949db641f11766fb506ec561374819d10533b9a
|
||||||
|
element-web-1:
|
||||||
|
# Pin to a SHA so that upstream cannot break our tests. Renovate handles regular updates.
|
||||||
|
image: ghcr.io/element-hq/element-web:develop@sha256:d1841828a390d96da2ee64232949db641f11766fb506ec561374819d10533b9a
|
||||||
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
This folder contains documentation for setup, usage, and development of Element Call.
|
This folder contains documentation for setup, usage, and development of Element Call.
|
||||||
|
|
||||||
- [Embedded vs standalone mode](./embedded-standalone.md)
|
- [Embedded vs standalone mode](./embedded_standalone.md)
|
||||||
- [Url format and parameters](./url-params.md)
|
- [Url format and parameters](./url_params.md)
|
||||||
- [Global JS controls](./controls.md)
|
- [Global JS controls](./controls.md)
|
||||||
- [Self-Hosting](./self-hosting.md)
|
- [Self-Hosting](./self_hosting.md)
|
||||||
- [Developing with linked packages](./linking.md)
|
- [Developing with linked packages](./linking.md)
|
||||||
|
|||||||
112
docs/_developer.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Developer help
|
||||||
|
|
||||||
|
## Testing on Mobile Devices
|
||||||
|
|
||||||
|
When developing Element Call locally, you may want to test on physical mobile devices (iOS/Android)
|
||||||
|
on the same WiFi network.
|
||||||
|
|
||||||
|
**Known Limitations:** For now this setup allows to use your local EC server but not yet the SFUs and Synapses.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
1. **Start the dev server**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the output for the `➜ Network` (this will contain the local IP address of your laptop)
|
||||||
|
|
||||||
|
```
|
||||||
|
➜ Local: https://m.localhost:3000/ 12:06:48
|
||||||
|
➜ Local: https://vite.m.localhost:3000/ 12:06:48
|
||||||
|
➜ Local: https://vite.othersite.m.localhost:3000/ 12:06:48
|
||||||
|
➜ Local: https://vite.nip.io:3000/ 12:06:48
|
||||||
|
➜ Network: https://192.168.0.122:3000/
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Transfer the CA certificate to your phone**
|
||||||
|
|
||||||
|
The file is located at `backend/dev_tls_local-ca.crt`. Transfer it via:
|
||||||
|
- Matrix room
|
||||||
|
- AirDrop for iphone
|
||||||
|
|
||||||
|
### IOS Setup
|
||||||
|
|
||||||
|
**Install the certificate profile on iPhone**
|
||||||
|
|
||||||
|
- Open the `dev_tls_local-ca.crt` file on your iPhone
|
||||||
|
- You'll see "Profile Downloaded"
|
||||||
|
- Go to **Settings → General → VPN & Device Management** (or **Settings → General → Profiles**)
|
||||||
|
- Tap the "Element Call Dev CA" profile
|
||||||
|
- Tap **Install** and enter your passcode
|
||||||
|
- Confirm by tapping **Install** again
|
||||||
|
|
||||||
|
**Enable full trust (Critical!)**
|
||||||
|
|
||||||
|
- Go to **Settings → General → About → Certificate Trust Settings**
|
||||||
|
- Under "Enable Full Trust for Root Certificates"
|
||||||
|
- Toggle **ON** for "Element Call Dev CA"
|
||||||
|
- Confirm the security warning
|
||||||
|
|
||||||
|
**Access Element Call**
|
||||||
|
|
||||||
|
Find your laptop's IP address (e.g., `192.168.0.122`) and use one of these URLs in Safari to validate:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://192-168-0-122.nip.io:3000/
|
||||||
|
```
|
||||||
|
|
||||||
|
**For Element X iOS Developer Tools**
|
||||||
|
|
||||||
|
In Element X's developer settings, set the Element Call URL to the nip.io url (replace . with - in the IP address):
|
||||||
|
|
||||||
|
```
|
||||||
|
https://192-168-0-122.nip.io:3000/room
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android Setup
|
||||||
|
|
||||||
|
**Transfer the CA certificate to your Android device**
|
||||||
|
|
||||||
|
The file is located at `backend/dev_tls_local-ca.crt`.
|
||||||
|
|
||||||
|
**Install the certificate**
|
||||||
|
|
||||||
|
This might vary by Android version and manufacturer, but generally:
|
||||||
|
|
||||||
|
- Open **Settings** search for "CA Certificate"/"Certificate"
|
||||||
|
- Tap **Install a certificate** or **Install from storage**
|
||||||
|
- Select **CA certificate**
|
||||||
|
- Confirm the security warning
|
||||||
|
- Navigate to and select the `dev_tls_local-ca.crt` file
|
||||||
|
- Give it a name like "Element Call Dev CA"
|
||||||
|
|
||||||
|
**Access Element Call**
|
||||||
|
|
||||||
|
Find your laptop's IP address (e.g., `192.168.0.122`) and use one of these URLs in Chrome to validate:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://192-168-0-122.nip.io:3000/
|
||||||
|
```
|
||||||
|
|
||||||
|
**For Element X Android Developer Tools**
|
||||||
|
|
||||||
|
In Element X's developer settings, set the Element Call URL to the nip.io url (replace . with - in the IP address):
|
||||||
|
|
||||||
|
```
|
||||||
|
https://192-168-0-122.nip.io:3000/room
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why nip.io?
|
||||||
|
|
||||||
|
[nip.io](https://nip.io) is a free wildcard DNS service that automatically resolves domain names containing IP addresses. For example, `192-168-0-122.nip.io` automatically resolves to `192.168.0.122`. This means:
|
||||||
|
|
||||||
|
- No need to regenerate certificates when your laptop's IP changes
|
||||||
|
- Works from any device without DNS configuration
|
||||||
|
- iOS/Android treat it as a proper domain name, not an IP address
|
||||||
|
- One-time certificate setup works for all future IP addresses
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Make sure your network router doesn't enforce DNS rebinding protection (which will
|
||||||
|
> break nip.io). If it does, try allow-listing nip.io in your router's administration interface.
|
||||||
@@ -1,7 +1,34 @@
|
|||||||
# Global JS controls
|
# Global JS controls
|
||||||
|
|
||||||
A few aspects of Element Call's interface can be controlled through a global API on the `window`:
|
A few aspects of Element Call's interface can be controlled through a global API on the `window`.
|
||||||
|
|
||||||
|
## Picture-in-picture
|
||||||
|
|
||||||
- `controls.canEnterPip(): boolean` Determines whether it's possible to enter picture-in-picture mode.
|
- `controls.canEnterPip(): boolean` Determines whether it's possible to enter picture-in-picture mode.
|
||||||
- `controls.enablePip(): void` Puts the call interface into picture-in-picture mode. Throws if not in a call.
|
- `controls.enablePip(): void` Puts the call interface into picture-in-picture mode. Throws if not in a call.
|
||||||
- `controls.disablePip(): void` Takes the call interface out of picture-in-picture mode, restoring it to its natural display mode. Throws if not in a call.
|
- `controls.disablePip(): void` Takes the call interface out of picture-in-picture mode, restoring it to its natural display mode. Throws if not in a call.
|
||||||
|
- `controls.onPipMediaOrientationUpdate: ((orientation: "landscape"|"portrait") => void) | undefined` Callback called whenever the PiP media orientation changes.
|
||||||
|
The client should track this value to already initiate the pip in the right orientation.
|
||||||
|
It should update the orientation of the current Pip window when called.
|
||||||
|
|
||||||
|
## Audio devices
|
||||||
|
|
||||||
|
On mobile platforms (iOS, Android), web views do not reliably support selecting audio output devices such as the main speaker, earpiece, or headset. To address this limitation, the following functions allow the hosting application (e.g., Element Web, Element X) to manage audio devices via exposed JavaScript interfaces. These functions must be enabled using the URL parameter `controlledAudioDevices` to take effect.
|
||||||
|
|
||||||
|
- `controls.setAvailableAudioDevices(devices: { id: string, name: string, forEarpiece?: boolean, isEarpiece?: boolean isSpeaker?: boolean, isExternalHeadset?: boolean }[]): void` Sets the list of available audio outputs. `forEarpiece` is used on iOS only.
|
||||||
|
It flags the device that should be used if the user selects earpiece mode. This should be the main stereo loudspeaker of the device.
|
||||||
|
- `controls.onAudioDeviceSelect: ((id: string) => void) | undefined` Callback called whenever the user or application selects a new audio output.
|
||||||
|
- `controls.setAudioDevice(id: string): void` Sets the selected audio device in Element Call's menu. This should be used if the OS decides to automatically switch to Bluetooth, for example.
|
||||||
|
- `controls.setAudioEnabled(enabled: boolean)` Enables/disables all audio output from the application. Output is enabled by default.
|
||||||
|
- `controls.onAudioPlaybackStarted: ((id: string) => void) | undefined`: This will be called the first time we start
|
||||||
|
playing audio in the webview. It can be helpful to do device setup on the native app when the webviews audio is ready.
|
||||||
|
In particular android is using it to setup the output channel so that the call volume can
|
||||||
|
be controlled by the hardware volume rocker.
|
||||||
|
|
||||||
|
## Element Call button delegation
|
||||||
|
|
||||||
|
Callbacks for buttons in EC that are handled by the native application
|
||||||
|
|
||||||
|
- `showNativeAudioDevicePicker: (() => void) | undefined`. Callback called whenever the user presses the output button in the settings menu.
|
||||||
|
This button is only shown on iOS. (`/iPad|iPhone|iPod|Mac/.test(navigator.userAgent)`)
|
||||||
|
- `onBackButtonPressed: (() => void) | undefined`. Callback when the webview detects a tab on the header's back button.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 929 KiB After Width: | Height: | Size: 941 KiB |
|
Before Width: | Height: | Size: 886 KiB After Width: | Height: | Size: 908 KiB |
BIN
docs/embedded_package.drawio.png
Normal file
|
After Width: | Height: | Size: 302 KiB |
@@ -14,7 +14,7 @@ The table below provides a comparison of the two packages:
|
|||||||
| **Release artifacts** | Docker Image, Tarball | Tarball, NPM for Web, Android AAR, SwiftPM for iOS |
|
| **Release artifacts** | Docker Image, Tarball | Tarball, NPM for Web, Android AAR, SwiftPM for iOS |
|
||||||
| **Recommended for** | Standalone/guest access usage | Embedding within messenger apps |
|
| **Recommended for** | Standalone/guest access usage | Embedding within messenger apps |
|
||||||
| **Responsibility for regulatory compliance** | The administrator that is deploying the app is responsible for compliance with any applicable regulations (e.g. privacy) | The developer of the messenger app is responsible for compliance |
|
| **Responsibility for regulatory compliance** | The administrator that is deploying the app is responsible for compliance with any applicable regulations (e.g. privacy) | The developer of the messenger app is responsible for compliance |
|
||||||
| **Analytics consent** | Element Call will show a consent UI. | Element Call will not show a consent UI. The messenger app should only provide the embedded Element Call with the [analytics URL parameters](./url-params.md#embedded-only-parameters) if consent has been granted. |
|
| **Analytics consent** | Element Call will show a consent UI. | Element Call will not show a consent UI. The messenger app should only provide the embedded Element Call with the [analytics URL parameters](./url_params.md#embedded-only-parameters) if consent has been granted. |
|
||||||
| **Analytics data** | Element Call will send data to the Posthog, Sentry and Open Telemetry targets specified by the administrator in the `config.json` | Element Call will send data to the Posthog and Sentry targets specified in the URL parameters by the messenger app |
|
| **Analytics data** | Element Call will send data to the Posthog, Sentry and Open Telemetry targets specified by the administrator in the `config.json` | Element Call will send data to the Posthog and Sentry targets specified in the URL parameters by the messenger app |
|
||||||
|
|
||||||
### Using the embedded package within a messenger app
|
### Using the embedded package within a messenger app
|
||||||
@@ -25,8 +25,8 @@ The basics are:
|
|||||||
|
|
||||||
1. Add the appropriate platform dependency as given for a [release](https://github.com/element-hq/element-call/releases), or use the embedded tarball. e.g. `npm install @element-hq/element-call-embedded@0.9.0`
|
1. Add the appropriate platform dependency as given for a [release](https://github.com/element-hq/element-call/releases), or use the embedded tarball. e.g. `npm install @element-hq/element-call-embedded@0.9.0`
|
||||||
2. Include the assets from the platform dependency in the build process. e.g. copy the assets during a [Webpack](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/webpack.config.js#L677-L682) build.
|
2. Include the assets from the platform dependency in the build process. e.g. copy the assets during a [Webpack](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/webpack.config.js#L677-L682) build.
|
||||||
3. Use the `index.html` entrypointof the imported assets when you are constructing the WebView or iframe. e.g. using a [relative path in a webapp](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/src/models/Call.ts#L680), or on the the Android [WebViewAssetLoader](https://github.com/element-hq/element-x-android/blob/fe5aab6588ecdcf9354a3bfbd9e97c1b31175a8f/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt#L20)
|
3. Use the `index.html` entrypoint of the imported assets when you are constructing the WebView or iframe. e.g. using a [relative path in a webapp](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/src/models/Call.ts#L680), or on the the Android [WebViewAssetLoader](https://github.com/element-hq/element-x-android/blob/fe5aab6588ecdcf9354a3bfbd9e97c1b31175a8f/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt#L20)
|
||||||
4. Set any of the [embedded-only URL parameters](./url-params.md#embedded-only-parameters) that you need.
|
4. Set any of the [embedded-only URL parameters](./url_params.md#embedded-only-parameters) that you need.
|
||||||
|
|
||||||
## Widget vs standalone mode
|
## Widget vs standalone mode
|
||||||
|
|
||||||
@@ -35,5 +35,5 @@ Element Call is developed using the [js-sdk](https://github.com/matrix-org/matri
|
|||||||
As a widget, the app only uses the core calling (MatrixRTC) parts. The rest (authentication, sending events, getting room state updates about calls) is done by the hosting client.
|
As a widget, the app only uses the core calling (MatrixRTC) parts. The rest (authentication, sending events, getting room state updates about calls) is done by the hosting client.
|
||||||
Element Call and the hosting client are connected via the widget API.
|
Element Call and the hosting client are connected via the widget API.
|
||||||
|
|
||||||
Element Call detects that it is run as a widget if a widgetId is defined in the url parameters. If `widgetId` is present then Element Call will try to connect to the client via the widget postMessage API using the parameters provided in [Url Format and parameters
|
Element Call detects that it is run as a widget if `widgetId` is defined in the url parameters. If `widgetId` is present then Element Call will try to connect to the client via the widget postMessage API using the parameters provided in [Url Format and parameters
|
||||||
](./url-params.md).
|
](./url_params.md).
|
||||||
BIN
docs/full_package.drawio.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
@@ -1,39 +1,65 @@
|
|||||||
# Developing with linked packages
|
## Quickstart guide
|
||||||
|
|
||||||
If you want to make changes to a package that Element Call depends on and see those changes applied in real time, you can create a link to a local copy of the package. Yarn has a command for this (`yarn link`), but it's not recommended to use it as it ends up modifying package.json with details specific to your development environment.
|
Run:
|
||||||
|
|
||||||
Instead, you can use our little 'linker' plugin. Create a file named `.links.yaml` in the Element Call project directory, listing the names and paths of any dependencies you want to link. For example:
|
```bash
|
||||||
|
./scripts/setup-linking.sh
|
||||||
```yaml
|
|
||||||
matrix-js-sdk: ../path/to/matrix-js-sdk
|
|
||||||
"@vector-im/compound-web": /home/alice/path/to/compound-web
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then run `yarn install`.
|
Read the script output:
|
||||||
|
|
||||||
|
```
|
||||||
|
Setup complete.
|
||||||
|
Update: .links.cjs to your liking
|
||||||
|
Run: 'pnpm links:on' to test your .links.cjs
|
||||||
|
Run: 'git commit' with links enabled to test the git pre-commit hook.
|
||||||
|
Run: 'pnpm links:off' to be able to commit again
|
||||||
|
Run: 'git config --local core.hooksPath ""' to allow committing with linking (not recommended)
|
||||||
|
Run: 'rm links.cjs' & 'git config --local core.hooksPath ""' to fully revert what this script did
|
||||||
|
```
|
||||||
|
|
||||||
|
# Developing with linked packages
|
||||||
|
|
||||||
|
If you want to make changes to a package that Element Call depends on and see those changes applied in real time, you can create a link to a local copy of the package. `pnpm` has a command for this (`pnpm link`), but it's not recommended to use it as it ends up modifying package.json with details specific to your development environment.
|
||||||
|
|
||||||
|
Instead, create a file named `.links.cjs` in the Element Call project directory (or run `./scripts/setup-linking.sh` to create a template), listing the names and paths of any dependencies you want to link. For example:
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
// Packages to link to local checkouts
|
||||||
|
module.exports = {
|
||||||
|
"matrix-js-sdk": "../your/path/matrix-js-sdk",
|
||||||
|
"matrix-widget-api": "../your/path/matrix-widget-api",
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run `pnpm links:on`. (this will activate the pnpm file + run `pnpm install` to setup the linking)
|
||||||
|
|
||||||
## Hooks
|
## Hooks
|
||||||
|
|
||||||
Changes in `.links.yaml` will also update `yarn.lock` when `yarn` is executed. The lockfile will then contain the local
|
Changes in `.links.cjs` will also update `pnpm-lock.yaml` when `pnpm install` is executed. The lockfile will then contain the local
|
||||||
version of the package which would not work on others dev setups or the github CI.
|
version of the package which would not work on others dev setups or the github CI.
|
||||||
One always needs to run:
|
|
||||||
|
One always needs to remove the pnpm `readPackage` script (the `.pnpmfile.cjs`) and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mv .links.yaml .links.disabled.yaml
|
pnpm install
|
||||||
yarn
|
|
||||||
```
|
```
|
||||||
|
|
||||||
before committing a change.
|
before committing a change.
|
||||||
|
|
||||||
To make it more convenient to work with this linking system we added git hooks for your conviniece.
|
To make this less of a foot gun we added a git hook.
|
||||||
A `pre-commit` hook will run `mv .links.yaml .links.disabled.yaml`, `yarn` and `git add yarn.lock` if it detects
|
A `pre-commit` hook will check if linking is currently used. If it detects
|
||||||
a `.links.yaml` file and abort the commit.
|
a `.pnpmfile.cjs` file it will abort the commit with an explanatory message.
|
||||||
You will than need to check if the resulting changes are appropriate and commit again.
|
You will then need to run `pnpm links:off` and commit again.
|
||||||
|
|
||||||
A `post-commit` hook will setup the linking as it was
|
To activate the hooks configure git with (when using the setup script (`./scripts/setup-linking.sh`) this is already done):
|
||||||
before if a `.links.disabled.yaml` is present. It runs `mv .links.disabled.yaml .links.yaml` and `yarn`.
|
|
||||||
|
|
||||||
To activate the hooks automatically configure git with
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git config --local core.hooksPath .githooks/
|
git config --local core.hooksPath .githooks
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will add the hook path for this repository only to .gihooks. which is a tracked (by git) folder containing the pre-commit hook.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
Information, why this approach is used can be found in the [linking concept reasoning](./linking_concept_reasoning.md) document.
|
||||||
|
|||||||
30
docs/linking_concept_reasoning.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
### Why do we not enable .pnpmfile.cjs by default
|
||||||
|
|
||||||
|
Background: The presence of the `.pnpmfile.cjs` adds a field to the `pnpm-lock.yaml` called: `pnpmfileChecksum`. This field is a checksum of the content of the `.pnpmfile.cjs` file.
|
||||||
|
`pnpm install --frozen-lockfile` **fails** if there is a `.pnpmfile.cjs` but no `pnpmfileChecksum` or vice versa (or on mismatch).
|
||||||
|
|
||||||
|
_TLDR: running with `--ignore-pnpmfile` will fail if `pnpmfileChecksum` is present._
|
||||||
|
|
||||||
|
#### `pnpmfileChecksum` + renovate bot
|
||||||
|
|
||||||
|
When the renovate bot creates a PR it runs `pnpm install --ignore-pnpmfile`. This means that the `pnpmfileChecksum` in the lockfile will be **empty**.
|
||||||
|
This breaks builds that **don't** ignore the `.pnpmfile.cjs`-file. (CI that runs on the renovate PR)
|
||||||
|
From here we have two possible paths:
|
||||||
|
|
||||||
|
- ignore `.pnpmfile.cjs` in all CI builds (CI will also fail if we accidently add it locally).
|
||||||
|
- fixup the `pnpm-lock.yaml` in the renovate PR to contain the correct `pnpmfileChecksum`.
|
||||||
|
|
||||||
|
Ignoring in all CI builds means that CI will always fail if we enable the linking system.
|
||||||
|
This is annoying but can be worked around with the git hook we provide that at least lets us know that we are
|
||||||
|
commiting with enabled linking.
|
||||||
|
Only if we remember setting it back/disbale linking (or let ourselves remember by the git hook) the CI will work.
|
||||||
|
|
||||||
|
#### Summary
|
||||||
|
|
||||||
|
- We will always run into conflicts with the `pnpmfileChecksum` because in renovate prs it will be empty (`--ignore-pnpmfile`)
|
||||||
|
- To keep it simple we set `--ignore-pnpmfile` in all of our CI builds to see issues immediately.
|
||||||
|
- The only solution is to never have a `.pnpmfile.cjs` in the repository when pushing.
|
||||||
|
- This way there will never be a commit with `pnpmfileChecksum` in the lockfile.
|
||||||
|
- renovate (which uses `--ignore-pnpmfile` which we cannot disable) and other CI will work.
|
||||||
|
- We are able to use the linking system locally if we `cp` this file from the scripts folder into `./` on demand.
|
||||||
|
- `pnpm links:on` and `pnpm links:off` + `./scripts/setup-linking.sh` will help us with this.
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
# Self-Hosting Element Call
|
# Self-Hosting Element Call
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> For In-App calling (Element X, Element Web, Element Desktop) use-case only
|
||||||
|
> section [Prerequisites](#Prerequisites) is required.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
@@ -51,23 +55,29 @@ rc_message:
|
|||||||
# Note key sharing events are bursty
|
# Note key sharing events are bursty
|
||||||
per_second: 0.5
|
per_second: 0.5
|
||||||
burst_count: 30
|
burst_count: 30
|
||||||
# This needs to match at least the heart-beat frequency plus a bit of headroom
|
|
||||||
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2s
|
|
||||||
rc_delayed_event_mgmt:
|
rc_delayed_event_mgmt:
|
||||||
|
# This needs to match at least the heart-beat frequency plus a bit of headroom
|
||||||
|
# Currently the heart-beat is every 5 seconds which translates into a rate of 0.2Hz
|
||||||
per_second: 1
|
per_second: 1
|
||||||
burst_count: 20
|
burst_count: 20
|
||||||
```
|
```
|
||||||
|
|
||||||
|
As a prerequisite for the
|
||||||
|
[MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)
|
||||||
|
make sure that your Synapse server has either a `federation` or `openid`
|
||||||
|
[listener configured](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#listeners).
|
||||||
|
|
||||||
### MatrixRTC Backend
|
### MatrixRTC Backend
|
||||||
|
|
||||||
In order to **guarantee smooth operation** of Element Call MatrixRTC backend is
|
In order to **guarantee smooth operation** of Element Call, a MatrixRTC backend is
|
||||||
required for each site deployment.
|
required for each site deployment.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
As depicted above in the `example.com` site deployment, Element Call requires a
|
As depicted above in the `example.com` site deployment, Element Call requires a
|
||||||
[Livekit SFU](https://github.com/livekit/livekit) alongside a
|
[Livekit SFU](https://github.com/livekit/livekit) alongside a
|
||||||
[Matrix Livekit JWT auth service](https://github.com/element-hq/lk-jwt-service)
|
[MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service)
|
||||||
to implement
|
to implement
|
||||||
[MSC4195: MatrixRTC using LiveKit backend](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md).
|
[MSC4195: MatrixRTC using LiveKit backend](https://github.com/hughns/matrix-spec-proposals/blob/hughns/matrixrtc-livekit/proposals/4195-matrixrtc-livekit.md).
|
||||||
|
|
||||||
@@ -79,11 +89,11 @@ the example above, this results in:
|
|||||||
| Service | Endpoint | Example |
|
| Service | Endpoint | Example |
|
||||||
| -------- | ------- | ------- |
|
| -------- | ------- | ------- |
|
||||||
| [Livekit SFU](https://github.com/livekit/livekit) WebSocket signalling connection | `/livekit/sfu` | `matrix-rtc.example.com/livekit/sfu` |
|
| [Livekit SFU](https://github.com/livekit/livekit) WebSocket signalling connection | `/livekit/sfu` | `matrix-rtc.example.com/livekit/sfu` |
|
||||||
| [Matrix Livekit JWT auth service](https://github.com/element-hq/lk-jwt-service) | `/livekit/jwt` | `matrix-rtc.example.com/livekit/jwt` |
|
| [MatrixRTC Authorization Service](https://github.com/element-hq/lk-jwt-service) | `/livekit/jwt` | `matrix-rtc.example.com/livekit/jwt` |
|
||||||
|
|
||||||
Using Nginx, you can achieve this by:
|
Using Nginx, you can achieve this by:
|
||||||
|
|
||||||
```jsonc
|
```nginx configuration file
|
||||||
server {
|
server {
|
||||||
...
|
...
|
||||||
location ^~ /livekit/jwt/ {
|
location ^~ /livekit/jwt/ {
|
||||||
@@ -92,7 +102,7 @@ server {
|
|||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
# JWT Service running at port 8080
|
# MatrixRTC Authorization Service running at port 8080
|
||||||
proxy_pass http://localhost:8080/;
|
proxy_pass http://localhost:8080/;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +126,72 @@ server {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or Using Caddy, you can achieve this by:
|
||||||
|
|
||||||
|
```caddy configuration file
|
||||||
|
# Route for lk-jwt-service with livekit/jwt prefix
|
||||||
|
@jwt_service path /livekit/jwt/sfu/get /livekit/jwt/healthz
|
||||||
|
handle @jwt_service {
|
||||||
|
uri strip_prefix /livekit/jwt
|
||||||
|
reverse_proxy http://[::1]:8080 {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Forwarded-Server {host}
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Default route for livekit
|
||||||
|
handle {
|
||||||
|
reverse_proxy http://localhost:7880 {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Forwarded-Server {host}
|
||||||
|
header_up X-Real-IP {remote_host}
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using Haproxy, you can achieve this by:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Frontend
|
||||||
|
# Match /livekit/sfu/ path
|
||||||
|
acl is_sfu path_beg -i /livekit/sfu/
|
||||||
|
use_backend sfu_backend if is_sfu matrixrtc_domain
|
||||||
|
|
||||||
|
acl is_mxrtc_auth path_beg -i /sfu/get
|
||||||
|
use_backend mxrtc_auth_backend if is_mxrtc_auth matrixrtc_domain
|
||||||
|
|
||||||
|
# Backend
|
||||||
|
## MatrixRTC backend
|
||||||
|
backend sfu_backend
|
||||||
|
server livekit 127.0.0.1:7880
|
||||||
|
http-request set-path %[path,regsub(^/livekit/sfu/,/)]
|
||||||
|
http-request set-header Host %[req.hdr(host)]
|
||||||
|
timeout server 120s
|
||||||
|
# WebSocket support
|
||||||
|
option forwardfor
|
||||||
|
option http-server-close
|
||||||
|
option http-buffer-request
|
||||||
|
|
||||||
|
backend mxrtc_auth_backend
|
||||||
|
server sfu 127.0.0.1:8070
|
||||||
|
http-request set-header Host %[req.hdr(host)]
|
||||||
|
timeout server 120s
|
||||||
|
# WebSocket support
|
||||||
|
option forwardfor
|
||||||
|
option http-server-close
|
||||||
|
option http-buffer-request
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
#### MatrixRTC backend announcement
|
#### MatrixRTC backend announcement
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> As defined in
|
> As defined in
|
||||||
> [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143)
|
> [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143),
|
||||||
> MatrixRTC backend must be announced to the client via your **Matrix site's
|
> the MatrixRTC backend(s) must be announced to the client via your **Matrix site's
|
||||||
> `.well-known/matrix/client`** file (e.g.
|
> `.well-known/matrix/client`** file (e.g.
|
||||||
> `example.com/.well-known/matrix/client` matching the site deployment example
|
> `example.com/.well-known/matrix/client` matching the site deployment example
|
||||||
> from above). The configuration is a list of Foci configs:
|
> from above). The configuration is a list of Foci configs:
|
||||||
@@ -135,17 +205,24 @@ server {
|
|||||||
{
|
{
|
||||||
"type": "livekit",
|
"type": "livekit",
|
||||||
"livekit_service_url": "https://matrix-rtc-2.example.com/livekit/jwt"
|
"livekit_service_url": "https://matrix-rtc-2.example.com/livekit/jwt"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "nextgen_new_foci_type",
|
|
||||||
"props_for_nextgen_foci": "val"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Make sure this file is served with the correct MIME type (`application/json`).
|
||||||
|
Additionally, ensure the appropriate CORS headers are set to allow web clients
|
||||||
|
to access it across origins. For more details, refer to the
|
||||||
|
[Matrix Client-Server API: 2. Web Browser Clients](https://spec.matrix.org/latest/client-server-api/#web-browser-clients).
|
||||||
|
|
||||||
|
```
|
||||||
|
Access-Control-Allow-Origin: *
|
||||||
|
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
||||||
|
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization
|
||||||
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Most `org.matrix.msc4143.rtc_foci` configurations will only have one entry in
|
> Most `org.matrix.msc4143.rtc_foci` configurations will only have one entry in
|
||||||
> the array
|
> the array.
|
||||||
|
|
||||||
## Building Element Call
|
## Building Element Call
|
||||||
|
|
||||||
@@ -160,8 +237,8 @@ source. First, clone and install the package:
|
|||||||
git clone https://github.com/element-hq/element-call.git
|
git clone https://github.com/element-hq/element-call.git
|
||||||
cd element-call
|
cd element-call
|
||||||
corepack enable
|
corepack enable
|
||||||
yarn
|
pnpm install
|
||||||
yarn build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
If all went well, you can now find the build output under `dist` as a series of
|
If all went well, you can now find the build output under `dist` as a series of
|
||||||
@@ -182,7 +259,7 @@ Because Element Call uses client-side routing, your server must be able to route
|
|||||||
any requests to non-existing paths back to `/index.html`. For example, in Nginx
|
any requests to non-existing paths back to `/index.html`. For example, in Nginx
|
||||||
you can achieve this with the `try_files` directive:
|
you can achieve this with the `try_files` directive:
|
||||||
|
|
||||||
```jsonc
|
```nginx configuration file
|
||||||
server {
|
server {
|
||||||
...
|
...
|
||||||
location / {
|
location / {
|
||||||
@@ -197,7 +274,7 @@ server {
|
|||||||
There are currently two different config files. `.env` holds variables that are
|
There are currently two different config files. `.env` holds variables that are
|
||||||
used at build time, while `public/config.json` holds variables that are used at
|
used at build time, while `public/config.json` holds variables that are used at
|
||||||
runtime. Documentation and default values for `public/config.json` can be found
|
runtime. Documentation and default values for `public/config.json` can be found
|
||||||
in [ConfigOptions.ts](src/config/ConfigOptions.ts).
|
in [ConfigOptions.ts](../src/config/ConfigOptions.ts).
|
||||||
|
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
> Please note configuring MatrixRTC backend via `config.json` of
|
> Please note configuring MatrixRTC backend via `config.json` of
|
||||||
@@ -214,8 +291,40 @@ be able to handle those yet and it may behave unreliably.
|
|||||||
|
|
||||||
Therefore, to use a self-hosted homeserver, this is recommended to be a new
|
Therefore, to use a self-hosted homeserver, this is recommended to be a new
|
||||||
server where any user account created has not joined any normal rooms anywhere
|
server where any user account created has not joined any normal rooms anywhere
|
||||||
in the Matrix federated network. The homeserver used can be setup to disable
|
in the Matrix federated network. The homeserver used can be set up to disable
|
||||||
federation, so as to prevent spam registrations (if you keep registrations open)
|
federation, so as to prevent spam registrations (if you keep registrations open)
|
||||||
and to ensure Element Call continues to work in case any user decides to log in
|
and to ensure Element Call continues to work in case any user decides to log in
|
||||||
to their Element Call account using the standard Element app and joins normal
|
to their Element Call account using the standard Element app and joins normal
|
||||||
rooms that Element Call cannot handle.
|
rooms that Element Call cannot handle.
|
||||||
|
|
||||||
|
# 📚 Community Guides & How-Tos
|
||||||
|
|
||||||
|
Looking for real-world tips, tutorials, and experiences from the community?
|
||||||
|
Below is a collection of blog posts, walkthroughs, and how-tos created by other
|
||||||
|
self-hosters and developers working with Element Call.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> These resources are community-created and may reflect different setups or
|
||||||
|
> versions. Use them alongside the official documentation for best results.
|
||||||
|
|
||||||
|
## 🌐 Blog Posts & Articles
|
||||||
|
|
||||||
|
- [How to resolve stuck MatrixRTC calls](https://sspaeth.de/2025/02/how-to-resolve-stuck-matrixrtc-calls/)
|
||||||
|
|
||||||
|
## 📝 How-Tos & Tutorials
|
||||||
|
|
||||||
|
- [MatrixRTC aka Element-call setup (Geek warning)](https://sspaeth.de/2024/11/sfu/)
|
||||||
|
- [MatrixRTC with Synology Container Manager (Docker)](https://ztfr.de/matrixrtc-with-synology-container-manager-docker/)
|
||||||
|
- [Encrypted & Scalable Video Calls: How to deploy an Element Call backend with Synapse Using Docker-Compose](https://willlewis.co.uk/blog/posts/deploy-element-call-backend-with-synapse-and-docker-compose/)
|
||||||
|
- [Element Call einrichten: Verschlüsselte Videoanrufe mit Element X und Matrix Synapse](https://www.cleveradmin.de/blog/2025/04/matrixrtc-element-call-backend-einrichten/)
|
||||||
|
- [MatrixRTC Back-End for Synapse with Docker Compose and Traefik](https://forge.avontech.net/kstro1/matrixrtc-docker-traefik/)
|
||||||
|
|
||||||
|
## 🛠️ Tools
|
||||||
|
|
||||||
|
- [A Matrix server sanity tester including tests for proper MatrixRTC setup](https://codeberg.org/spaetz/testmatrix)
|
||||||
|
|
||||||
|
## 🤝 Want to Contribute?
|
||||||
|
|
||||||
|
Have a guide or blog post you'd like to share? Open a
|
||||||
|
[PR](https://github.com/element-hq/element-call/pulls) to add it here, or drop a
|
||||||
|
link in the [#webrtc:matrix.org](https://matrix.to/#/#webrtc:matrix.org) room.
|
||||||
@@ -4,7 +4,7 @@ There are two formats for Element Call URLs.
|
|||||||
|
|
||||||
## Link for sharing
|
## Link for sharing
|
||||||
|
|
||||||
Requires Element Call to be deployed in [standalone](./embedded-standalone.md) mode.
|
Requires Element Call to be deployed in [standalone](./embedded_standalone.md) mode.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
https://element_call.domain/room/#
|
https://element_call.domain/room/#
|
||||||
@@ -12,7 +12,7 @@ https://element_call.domain/room/#
|
|||||||
```
|
```
|
||||||
|
|
||||||
The URL is split into two sections. The `https://element_call.domain/room/#`
|
The URL is split into two sections. The `https://element_call.domain/room/#`
|
||||||
contains the app and the intend that the link brings you into a specific room
|
contains the app and the intent that the link brings you into a specific room
|
||||||
(`https://call.element.io/#` would be the homepage). The fragment is used for
|
(`https://call.element.io/#` would be the homepage). The fragment is used for
|
||||||
query parameters to make sure they never get sent to the element_call.domain
|
query parameters to make sure they never get sent to the element_call.domain
|
||||||
server. Here we have the actual Matrix room ID and the password which are used
|
server. Here we have the actual Matrix room ID and the password which are used
|
||||||
@@ -36,62 +36,66 @@ possible to support encryption.
|
|||||||
|
|
||||||
| Package | Deployment | URL |
|
| Package | Deployment | URL |
|
||||||
| ------------------------------------ | ----------------------------- | ----------------------------------------------------------------------------- |
|
| ------------------------------------ | ----------------------------- | ----------------------------------------------------------------------------- |
|
||||||
| [Full](./embedded-standalone.md) | All | `https://element_call.domain/room` |
|
| [Full](./embedded_standalone.md) | All | `https://element_call.domain/room` |
|
||||||
| [Embedded](./embedded-standalone.md) | Remote URL | `https://element_call.domain/` n.b. no `/room` part |
|
| [Embedded](./embedded_standalone.md) | Remote URL | `https://element_call.domain/` n.b. no `/room` part |
|
||||||
| [Embedded](./embedded-standalone.md) | Embedded within messenger app | Platform dependent, but you load the `index.html` file without a `/room` part |
|
| [Embedded](./embedded_standalone.md) | Embedded within messenger app | Platform dependent, but you load the `index.html` file without a `/room` part |
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
### Common Parameters
|
### Common Parameters
|
||||||
|
|
||||||
These parameters are relevant to both [widget](./embedded-standalone.md) and [standalone](./embedded-standalone.md) modes:
|
These parameters are relevant to both [widget](./embedded_standalone.md) and [standalone](./embedded_standalone.md) modes:
|
||||||
|
|
||||||
| Name | Values | Required for widget | Required for SPA | Description |
|
| Name | Values | Required for widget | Required for SPA | Description |
|
||||||
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `allowIceFallback` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Allows use of fallback STUN servers for ICE if the user's homeserver doesn’t provide any. |
|
| `intent` | `start_call`, `join_existing`, `start_call_voice`, `join_existing_voice`, `start_call_dm`, `join_existing_dm`, `start_call_dm_voice`, or `join_existing_dm_voice`. | No, defaults to `start_call` | No, defaults to `start_call` | The intent is a special url parameter that defines the defaults for all the other parameters. In most cases it should be enough to only set the intent to setup element-call. |
|
||||||
| `analyticsID` (deprecated: use `posthogUserId` instead) | Posthog analytics ID | No | No | Available only with user's consent for sharing telemetry in Element Web. |
|
| `allowIceFallback` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Allows use of fallback STUN servers for ICE if the user's homeserver doesn’t provide any. |
|
||||||
| `appPrompt` | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Prompts the user to launch the native mobile app upon entering a room, applicable only on Android and iOS, and must be enabled in config. |
|
| `posthogUserId` | Posthog analytics ID | No | No | Available only with user's consent for sharing telemetry in Element Web. |
|
||||||
| `confineToRoom` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Keeps the user confined to the current call/room. |
|
| `confineToRoom` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Keeps the user confined to the current call/room. |
|
||||||
| `displayName` | | No | No | Display name used for auto-registration. |
|
| `displayName` | | No | No | Display name used for auto-registration. |
|
||||||
| `enableE2EE` (deprecated) | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Legacy flag to enable end-to-end encryption, not used in the `livekit` branch. |
|
| `enableE2EE` (deprecated) | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Legacy flag to enable end-to-end encryption, not used in the `livekit` branch. |
|
||||||
| `fontScale` | A decimal number such as `0.9` | No, defaults to `1.0` | No, defaults to `1.0` | Factor by which to scale the interface's font size. |
|
| `fontScale` | A decimal number such as `0.9` | No, defaults to `1.0` | No, defaults to `1.0` | Factor by which to scale the interface's font size. |
|
||||||
| `fonts` | | No | No | Defines the font(s) used by the interface. Multiple font parameters can be specified: `?font=font-one&font=font-two...`. |
|
| `fonts` | | No | No | Defines the font(s) used by the interface. Multiple font parameters can be specified: `?font=font-one&font=font-two...`. |
|
||||||
| `hideHeader` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Hides the room header when in a call. |
|
| `header` | `none`, `standard` or `app_bar` | No, defaults to `standard` | No, defaults to `standard` | The style of headers to show. `standard` is the default arrangement, `none` hides the header entirely, and `app_bar` produces a header with a back button like you might see in mobile apps. The callback for the back button is `window.controls.onBackButtonPressed`. |
|
||||||
| `hideScreensharing` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Hides the screen-sharing button. |
|
| `hideScreensharing` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Hides the screen-sharing button. |
|
||||||
| `homeserver` | | Not applicable | No | Homeserver for registering a new (guest) user, configures non-default guest user server when creating a spa link. |
|
| `homeserver` | | Not applicable | No | Homeserver for registering a new (guest) user, configures non-default guest user server when creating a spa link. |
|
||||||
| `intent` | `start_call` or `join_existing` | No, defaults to `start_call` | No, defaults to `start_call` | The intent of the user with respect to the call. e.g. if they clicked a Start Call button, this would be `start_call`. If it was a Join Call button, it would be `join_existing`. |
|
| `lang` | [BCP 47](https://www.rfc-editor.org/info/bcp47) code | No | No | The language the app should use. |
|
||||||
| `lang` | [BCP 47](https://www.rfc-editor.org/info/bcp47) code | No | No | The language the app should use. |
|
| `password` | | No | No | E2EE password when using a shared secret. (For individual sender keys in embedded mode this is not required.) |
|
||||||
| `password` | | No | No | E2EE password when using a shared secret. (For individual sender keys in embedded mode this is not required.) |
|
| `perParticipantE2EE` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Enables per participant encryption with Keys exchanged over encrypted matrix room messages. |
|
||||||
| `perParticipantE2EE` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Enables per participant encryption with Keys exchanged over encrypted matrix room messages. |
|
| `controlledAudioDevices` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Whether the [global JS controls for audio devices](./controls.md#audio-devices) should be enabled, allowing the list of audio devices to be controlled by the app hosting Element Call. |
|
||||||
| `roomId` | [Matrix Room ID](https://spec.matrix.org/v1.12/appendices/#room-ids) | Yes | No | Anything about what room we're pointed to should be from useRoomIdentifier which parses the path and resolves alias with respect to the default server name, however roomId is an exception as we need the room ID in embedded widget mode, and not the room alias (or even the via params because we are not trying to join it). This is also not validated, where it is in `useRoomIdentifier()`. |
|
| `roomId` | [Matrix Room ID](https://spec.matrix.org/v1.12/appendices/#room-ids) | Yes | No | Anything about what room we're pointed to should be from useRoomIdentifier which parses the path and resolves alias with respect to the default server name, however roomId is an exception as we need the room ID in embedded widget mode, and not the room alias (or even the via params because we are not trying to join it). This is also not validated, where it is in `useRoomIdentifier()`. |
|
||||||
| `showControls` | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Displays controls like mute, screen-share, invite, and hangup buttons during a call. |
|
| `showControls` | `true` or `false` | No, defaults to `true` | No, defaults to `true` | Displays controls like mute, screen-share, invite, and hangup buttons during a call. |
|
||||||
| `skipLobby` (deprecated: use `intent` instead) | `true` or `false` | No. If `intent` is explicitly `start_call` then defaults to `true`. Otherwise defaults to `false` | No, defaults to `false` | Skips the lobby to join a call directly, can be combined with preload in widget. When `true` the audio and video inputs will be muted by default. (This means there currently is no way to start without muted video if one wants to skip the lobby. Also not in widget mode.) |
|
| `skipLobby` (deprecated: use `intent` instead) | `true` or `false` | No. If `intent` is explicitly `start_call` then defaults to `true`. Otherwise defaults to `false` | No, defaults to `false` | Skips the lobby to join a call directly, can be combined with preload in widget. When `true` the audio and video inputs will be muted by default. (This means there currently is no way to start without muted video if one wants to skip the lobby. Also not in widget mode.) |
|
||||||
| `theme` | One of: `light`, `dark`, `light-high-contrast`, `dark-high-contrast` | No, defaults to `dark` | No, defaults to `dark` | UI theme to use. |
|
| `theme` | One of: `light`, `dark`, `light-high-contrast`, `dark-high-contrast` | No, defaults to `dark` | No, defaults to `dark` | UI theme to use. |
|
||||||
| `viaServers` | Comma separated list of [Matrix Server Names](https://spec.matrix.org/v1.12/appendices/#server-name) | Not applicable | No | Homeserver for joining a room, non-empty value required for rooms not on the user’s default homeserver. |
|
| `background` | One of: `solid`, `gradient` | No, defaults to `gradient` | No, defaults to `gradient` | Visual style of the page background. |
|
||||||
|
| `viaServers` | Comma separated list of [Matrix Server Names](https://spec.matrix.org/v1.12/appendices/#server-name) | Not applicable | No | Homeserver for joining a room, non-empty value required for rooms not on the user’s default homeserver. |
|
||||||
|
| `sendNotificationType` | `ring` or `notification` | No | No | Will send a "ring" or "notification" `m.rtc.notification` event if the user is the first one in the call. |
|
||||||
|
| `autoLeave` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | Whether the app should automatically leave the call when there is no one left in the call. |
|
||||||
|
| `waitForCallPickup` | `true` or `false` | No, defaults to `false` | No, defaults to `false` | When sending a notification, show UI that the app is awaiting an answer, play a dial tone, and (in widget mode) auto-close the widget once the notification expires. |
|
||||||
|
|
||||||
### Widget-only parameters
|
### Widget-only parameters
|
||||||
|
|
||||||
These parameters are only supported in [widget](./embedded-standalone.md) mode.
|
These parameters are only supported in [widget](./embedded_standalone.md) mode.
|
||||||
|
|
||||||
| Name | Values | Required | Description |
|
| Name | Values | Required | Description |
|
||||||
| --------------- | ----------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------- | ----------------------------------------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `baseUrl` | | Yes | The base URL of the homeserver to use for media lookups. |
|
| `baseUrl` | | Yes | The base URL of the homeserver to use for media lookups. |
|
||||||
| `deviceId` | Matrix device ID | Yes | The Matrix device ID for the widget host. |
|
| `deviceId` | Matrix device ID | Yes | The Matrix device ID for the widget host. |
|
||||||
| `parentUrl` | | Yes | The url used to send widget action postMessages. This should be the domain of the client or the webview the widget is hosted in. (in case the widget is not in an Iframe but in a dedicated webview we send the postMessages same WebView the widget lives in. Filtering is done in the widget so it ignores the messages it receives from itself) |
|
| `parentUrl` | | Yes | The url used to send widget action postMessages. This should be the domain of the client or the webview the widget is hosted in. (In case the widget is not in an Iframe but in a dedicated webview, we send the postMessages in the same WebView the widget lives in. Filtering is done in the widget so it ignores the messages it receives from itself.) |
|
||||||
| `posthogUserId` | Posthog user identifier | No | This replaces the `analyticsID` parameter |
|
| `posthogUserId` | Posthog user identifier | No | This replaces the `analyticsID` parameter |
|
||||||
| `preload` | `true` or `false` | No, defaults to `false` | Pauses app before joining a call until an `io.element.join` widget action is seen, allowing preloading. |
|
| `preload` | `true` or `false` | No, defaults to `false` | Pauses app before joining a call until an `io.element.join` widget action is seen, allowing preloading. |
|
||||||
| `returnToLobby` | `true` or `false` | No, defaults to `false` | Displays the lobby in widget mode after leaving a call; shows a blank page if set to `false`. Useful for video rooms. |
|
| `returnToLobby` | `true` or `false` | No, defaults to `false` | Displays the lobby in widget mode after leaving a call; shows a blank page if set to `false`. Useful for video rooms. |
|
||||||
| `userId` | [Matrix User Identifier](https://spec.matrix.org/v1.12/appendices/#user-identifiers) | Yes | The Matrix user ID. |
|
| `userId` | [Matrix User Identifier](https://spec.matrix.org/v1.12/appendices/#user-identifiers) | Yes | The Matrix user ID. |
|
||||||
| `widgetId` | [MSC2774](https://github.com/matrix-org/matrix-spec-proposals/pull/2774) format widget ID | Yes | The id used by the widget. The presence of this parameter implies that element call will not connect to a homeserver directly and instead tries to establish postMessage communication via the `parentUrl`. |
|
| `widgetId` | [MSC2774](https://github.com/matrix-org/matrix-spec-proposals/pull/2774) format widget ID | Yes | The id used by the widget. The presence of this parameter implies that element call will not connect to a homeserver directly and instead tries to establish postMessage communication via the `parentUrl`. |
|
||||||
|
|
||||||
### Embedded-only parameters
|
### Embedded-only parameters
|
||||||
|
|
||||||
These parameters are only supported in the [embedded](./embedded-standalone.md) package of Element Call and will be ignored in the [full](./embedded-standalone.md) package.
|
These parameters are only supported in the [embedded](./embedded_standalone.md) package of Element Call and will be ignored in the [full](./embedded_standalone.md) package.
|
||||||
|
|
||||||
| Name | Values | Required | Description |
|
| Name | Values | Required | Description |
|
||||||
| -------------------- | -------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------- | -------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `posthogApiHost` | Posthog server URL | No | e.g. `https://posthog-element-call.element.io`. Only supported in embedded package. In full package the value from config is used. |
|
| `posthogApiHost` | Posthog server URL | No | e.g. `https://posthog-element-call.element.io`. Only supported in embedded package. In full package the value from config is used. |
|
||||||
| `posthogApiKey` | Posthog project API key | No | Only supported in embedded package. In full package the value from config is used. |
|
| `posthogApiKey` | Posthog project API key | No | Only supported in embedded package. In full package the value from config is used. |
|
||||||
| `rageshakeSubmitUrl` | Rageshake server URL endpoint | No | e.g. `https://element.io/bugreports/submit`. In full package the value from config is used. |
|
| `rageshakeSubmitUrl` | Rageshake server URL endpoint | No | e.g. `https://rageshakes.element.io/api/submit`. In full package the value from config is used. |
|
||||||
| `sentryDsn` | Sentry [DSN](https://docs.sentry.io/concepts/key-terms/dsn-explainer/) | No | In full package the value from config is used. |
|
| `sentryDsn` | Sentry [DSN](https://docs.sentry.io/concepts/key-terms/dsn-explainer/) | No | In full package the value from config is used. |
|
||||||
| `sentryEnvironment` | Sentry [environment](https://docs.sentry.io/concepts/key-terms/key-terms/) | No | In full package the value from config is used. |
|
| `sentryEnvironment` | Sentry [environment](https://docs.sentry.io/concepts/key-terms/key-terms/) | No | In full package the value from config is used. |
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
||||||
|
|
||||||
[versions]
|
[versions]
|
||||||
android_gradle_plugin = "8.8.0"
|
android_gradle_plugin = "8.13.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref = "android_gradle_plugin" }
|
android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref = "android_gradle_plugin" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android_library = { id = "com.android.library", version.ref = "android_gradle_plugin" }
|
android_library = { id = "com.android.library", version.ref = "android_gradle_plugin" }
|
||||||
maven_publish = { id = "com.vanniktech.maven.publish", version = "0.30.0" }
|
maven_publish = { id = "com.vanniktech.maven.publish", version = "0.36.0" }
|
||||||
|
|||||||
BIN
embedded/android/gradle/wrapper/gradle-wrapper.jar
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.5-all.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
9
embedded/android/gradlew
vendored
@@ -86,8 +86,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
' "$PWD" ) || exit
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -115,7 +114,7 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH="\\\"\\\""
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@@ -206,7 +205,7 @@ fi
|
|||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command:
|
# Collect all arguments for the java command:
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# and any embedded shellness will be escaped.
|
# and any embedded shellness will be escaped.
|
||||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# treated as '${Hostname}' itself on the command line.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-classpath "$CLASSPATH" \
|
-classpath "$CLASSPATH" \
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
# Stop when "xargs" is not available.
|
||||||
|
|||||||
4
embedded/android/gradlew.bat
vendored
@@ -70,11 +70,11 @@ goto fail
|
|||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.vanniktech.maven.publish.SonatypeHost
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.library)
|
alias(libs.plugins.android.library)
|
||||||
alias(libs.plugins.maven.publish)
|
alias(libs.plugins.maven.publish)
|
||||||
@@ -27,7 +25,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
publishToMavenCentral(SonatypeHost.S01)
|
publishToMavenCentral(automaticRelease = true)
|
||||||
|
|
||||||
signAllPublications()
|
signAllPublications()
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pushd $CURRENT_DIR > /dev/null
|
|||||||
function build_assets() {
|
function build_assets() {
|
||||||
echo "Generating Element Call assets..."
|
echo "Generating Element Call assets..."
|
||||||
pushd ../.. > /dev/null
|
pushd ../.. > /dev/null
|
||||||
yarn build
|
pnpm build
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
66
eslint/CopyrightHeader.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
||||||
|
|
||||||
|
const rule = ESLintUtils.RuleCreator(
|
||||||
|
() => "https://github.com/element-hq/element-call",
|
||||||
|
)({
|
||||||
|
name: "copyright-header",
|
||||||
|
meta: {
|
||||||
|
type: "problem",
|
||||||
|
fixable: "code",
|
||||||
|
docs: {
|
||||||
|
description: "Require a copyright header in files.",
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
noHeader: "Copyright header is required.",
|
||||||
|
},
|
||||||
|
schema: [{ type: "string" }],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
const code = context.getSourceCode();
|
||||||
|
|
||||||
|
return {
|
||||||
|
Program(node) {
|
||||||
|
const firstToken = code.getFirstToken(node, { includeComments: false });
|
||||||
|
|
||||||
|
if (!firstToken) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const headComments = code.getCommentsBefore(firstToken);
|
||||||
|
const hasSomeCopyrightHeader = headComments?.some((comment) =>
|
||||||
|
comment?.value?.includes("Copyright"),
|
||||||
|
);
|
||||||
|
if (hasSomeCopyrightHeader) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const headerTemplate = context.options[0];
|
||||||
|
const fix = headerTemplate
|
||||||
|
? function (fixer) {
|
||||||
|
return fixer.insertTextBefore(
|
||||||
|
firstToken,
|
||||||
|
headerTemplate.replace(
|
||||||
|
/%%CURRENT_YEAR%%/g,
|
||||||
|
new Date().getFullYear(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
context.report({
|
||||||
|
messageId: "noHeader",
|
||||||
|
node,
|
||||||
|
fix,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default rule;
|
||||||
78
eslint/NoObservableScopeLeak.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
||||||
|
|
||||||
|
// These ObservableScope methods will not generally cause resource leaks even if
|
||||||
|
// called from a callback
|
||||||
|
const safeScopeMethods = new Set(["bind", "end"]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the variable with the given name is local to
|
||||||
|
* the enclosing function or class scope.
|
||||||
|
*/
|
||||||
|
function isLocal(name, scope) {
|
||||||
|
// If it is nowhere to be found in the "through" scope, it is local.
|
||||||
|
if (!scope.through.some(({ identifier }) => identifier.name === name))
|
||||||
|
return true;
|
||||||
|
if (scope.type === "function" || scope.type === "class") return false;
|
||||||
|
// If this is something other than a function or class scope, check its outer
|
||||||
|
// scope.
|
||||||
|
return !scope.upper || isLocal(name, scope.upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rule = ESLintUtils.RuleCreator(
|
||||||
|
() => "https://github.com/element-hq/element-call",
|
||||||
|
)({
|
||||||
|
name: "no-observablescope-leak",
|
||||||
|
meta: {
|
||||||
|
type: "problem",
|
||||||
|
docs: {
|
||||||
|
description:
|
||||||
|
"Require referenced ObservableScopes to be defined in the very same scope to avoid resource leaks.",
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
scopeLeak:
|
||||||
|
"Do not reference ObservableScopes defined in an outer scope; this may create resource leaks.",
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
return {
|
||||||
|
Identifier(node) {
|
||||||
|
const scope = context.sourceCode.getScope(node);
|
||||||
|
if (
|
||||||
|
// Is this a reference to a variable defined in an outer scope?
|
||||||
|
!isLocal(node.name, scope) &&
|
||||||
|
// Exclude calls to "safe" ObservableScope methods
|
||||||
|
node.parent?.type === "MemberExpression" &&
|
||||||
|
node.parent.object === node &&
|
||||||
|
node.parent.property.type === "Identifier" &&
|
||||||
|
!safeScopeMethods.has(node.parent.property.name) &&
|
||||||
|
/(^s|S)cope$/.test(node.name)
|
||||||
|
) {
|
||||||
|
// TODO: Once oxlint supports lint rules that rely on TypeScript type-awareness,
|
||||||
|
// Verify that the variable is actually of type ObservableScope rather than just
|
||||||
|
// checking its name. This is expensive so we should do this last.
|
||||||
|
//
|
||||||
|
// const services = ESLintUtils.getParserServices(context);
|
||||||
|
// const type = services.getTypeAtLocation(node);
|
||||||
|
// if (type.symbol?.name === "ObservableScope") { ... }
|
||||||
|
|
||||||
|
// This ObservableScope method call may be causing resource leaks.
|
||||||
|
context.report({
|
||||||
|
messageId: "scopeLeak",
|
||||||
|
loc: node.loc,
|
||||||
|
node,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default rule;
|
||||||
92
eslint/NoTopLevelLoggerGetChild.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ESLintUtils } from "@typescript-eslint/utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node types that introduce a new non-module scope. A getChild() call nested
|
||||||
|
* inside any of these is considered "not at the top level".
|
||||||
|
*/
|
||||||
|
const FUNCTION_OR_CLASS_TYPES = new Set([
|
||||||
|
"FunctionDeclaration",
|
||||||
|
"FunctionExpression",
|
||||||
|
"ArrowFunctionExpression",
|
||||||
|
"ClassBody",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rule = ESLintUtils.RuleCreator(
|
||||||
|
() => "https://github.com/element-hq/element-call",
|
||||||
|
)({
|
||||||
|
name: "no-top-level-logger-get-child",
|
||||||
|
meta: {
|
||||||
|
type: "problem",
|
||||||
|
docs: {
|
||||||
|
description:
|
||||||
|
"Disallow calling logger.getChild() at the top level of a module." +
|
||||||
|
"`getChild` has to be called after the rageshake logger `init()`." +
|
||||||
|
"If it is called at the top level the child logger will never be setup for rageshakes.",
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
noTopLevelGetChild:
|
||||||
|
"Do not call logger.getChild() at the top level of a module; move it inside a function or class instead that gets called after rageshake logger `init()` is called.",
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
// Tracks the local binding names that refer to the logger imported from
|
||||||
|
// 'matrix-js-sdk/lib/logger', e.g. both `logger` and `rootLogger` in:
|
||||||
|
// import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
|
// import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||||
|
const loggerNames = new Set();
|
||||||
|
|
||||||
|
return {
|
||||||
|
ImportDeclaration(node) {
|
||||||
|
if (node.source.value !== "matrix-js-sdk/lib/logger") return;
|
||||||
|
for (const specifier of node.specifiers) {
|
||||||
|
if (
|
||||||
|
specifier.type === "ImportSpecifier" &&
|
||||||
|
specifier.imported.name === "logger"
|
||||||
|
) {
|
||||||
|
loggerNames.add(specifier.local.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
CallExpression(node) {
|
||||||
|
// Must be a non-computed member expression call: something.getChild(...)
|
||||||
|
if (
|
||||||
|
node.callee.type !== "MemberExpression" ||
|
||||||
|
node.callee.computed ||
|
||||||
|
node.callee.property.type !== "Identifier" ||
|
||||||
|
node.callee.property.name !== "getChild"
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The receiver must be one of the tracked logger names.
|
||||||
|
const object = node.callee.object;
|
||||||
|
if (object.type !== "Identifier" || !loggerNames.has(object.name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Flag the call only when it is at module top level — i.e. there is no
|
||||||
|
// enclosing function or class body anywhere in the ancestor chain.
|
||||||
|
const ancestors = context.sourceCode.getAncestors(node);
|
||||||
|
const isTopLevel = !ancestors.some((a) =>
|
||||||
|
FUNCTION_OR_CLASS_TYPES.has(a.type),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isTopLevel) {
|
||||||
|
context.report({
|
||||||
|
messageId: "noTopLevelGetChild",
|
||||||
|
node,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default rule;
|
||||||
8
eslint/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
rules: {
|
||||||
|
"copyright-header": require("./CopyrightHeader").default,
|
||||||
|
"no-observablescope-leak": require("./NoObservableScopeLeak").default,
|
||||||
|
"no-top-level-logger-get-child": require("./NoTopLevelLoggerGetChild")
|
||||||
|
.default,
|
||||||
|
},
|
||||||
|
};
|
||||||
4
eslint/package.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "eslint-plugin-element-call",
|
||||||
|
"version": "0.0.0"
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
export default {
|
|
||||||
keySeparator: ".",
|
|
||||||
namespaceSeparator: false,
|
|
||||||
contextSeparator: "|",
|
|
||||||
pluralSeparator: "_",
|
|
||||||
createOldCatalogs: false,
|
|
||||||
defaultNamespace: "app",
|
|
||||||
lexers: {
|
|
||||||
ts: [
|
|
||||||
{
|
|
||||||
lexer: "JavascriptLexer",
|
|
||||||
functions: ["t", "translatedError"],
|
|
||||||
namespaceFunctions: ["useTranslation", "withTranslation"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tsx: [
|
|
||||||
{
|
|
||||||
lexer: "JsxLexer",
|
|
||||||
functions: ["t", "translatedError"],
|
|
||||||
namespaceFunctions: ["useTranslation", "withTranslation"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
locales: ["en"],
|
|
||||||
output: "locales/$LOCALE/$NAMESPACE.json",
|
|
||||||
input: ["src/**/*.{ts,tsx}"],
|
|
||||||
sort: true,
|
|
||||||
};
|
|
||||||
20
i18next.config.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { defineConfig } from "i18next-cli";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
locales: ["en"],
|
||||||
|
extract: {
|
||||||
|
input: ["src/**/*.{ts,tsx}"],
|
||||||
|
output: "locales/{{language}}/{{namespace}}.json",
|
||||||
|
defaultNS: "app",
|
||||||
|
keySeparator: ".",
|
||||||
|
nsSeparator: false,
|
||||||
|
contextSeparator: "|",
|
||||||
|
extractFromComments: false,
|
||||||
|
functions: ["t", "*.t", "translatedError", "i18nKey"],
|
||||||
|
transComponents: ["Trans"],
|
||||||
|
},
|
||||||
|
types: {
|
||||||
|
input: ["locales/{{language}}/{{namespace}}.json"],
|
||||||
|
output: "src/types/i18next.d.ts",
|
||||||
|
},
|
||||||
|
});
|
||||||
15
index.html
@@ -10,12 +10,25 @@
|
|||||||
|
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
|
content="viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0"
|
||||||
/>
|
/>
|
||||||
<title><%- brand %></title>
|
<title><%- brand %></title>
|
||||||
<script>
|
<script>
|
||||||
window.global = window;
|
window.global = window;
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Polyfill for Chrome < 119 (Huawei WebView, etc.) -->
|
||||||
|
<script>
|
||||||
|
if (!Promise.withResolvers) {
|
||||||
|
Promise.withResolvers = function () {
|
||||||
|
var resolve, reject;
|
||||||
|
var promise = new Promise(function (a, b) {
|
||||||
|
resolve = a;
|
||||||
|
reject = b;
|
||||||
|
});
|
||||||
|
return { promise: promise, resolve: resolve, reject: reject };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<% if (packageType === "full") { %>
|
<% if (packageType === "full") { %>
|
||||||
<!-- Open graph meta tags -->
|
<!-- Open graph meta tags -->
|
||||||
|
|||||||
32
knip.ts
@@ -1,33 +1,45 @@
|
|||||||
import { KnipConfig } from "knip";
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { type KnipConfig } from "knip";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
vite: {
|
vite: {
|
||||||
config: ["vite.config.js", "vite-embedded.config.js"],
|
config: ["vite.config.ts", "vite-embedded.config.ts", "vite-sdk.config.ts"],
|
||||||
},
|
},
|
||||||
entry: ["src/main.tsx", "i18next-parser.config.ts"],
|
entry: ["src/main.tsx", "eslint/index.js", "i18next.config.ts"],
|
||||||
ignoreBinaries: [
|
ignoreBinaries: [
|
||||||
// This is deprecated, so Knip doesn't actually recognize it as a globally
|
// This is deprecated, so Knip doesn't actually recognize it as a globally
|
||||||
// installed binary. TODO We should switch to Compose v2:
|
// installed binary. TODO We should switch to Compose v2:
|
||||||
// https://docs.docker.com/compose/migrate/
|
// https://docs.docker.com/compose/migrate/
|
||||||
"docker-compose",
|
"docker-compose",
|
||||||
|
// This is a shell built-in.
|
||||||
|
"printf",
|
||||||
|
],
|
||||||
|
ignoreFiles: [
|
||||||
|
"scripts/.pnpmfile.cjs",
|
||||||
|
// Deliberately added prior to any component or business logic
|
||||||
|
// implementation
|
||||||
|
"src/state/ServiceInterruptionsViewModel.ts",
|
||||||
],
|
],
|
||||||
ignoreDependencies: [
|
ignoreDependencies: [
|
||||||
// Used in CSS
|
// Used in CSS
|
||||||
"normalize.css",
|
"normalize.css",
|
||||||
// Used for its global type declarations
|
// Used for its global type declarations
|
||||||
"@types/grecaptcha",
|
"@types/grecaptcha",
|
||||||
// Because we use matrix-js-sdk as a Git dependency rather than consuming
|
|
||||||
// the proper release artifacts, and also import directly from src/, we're
|
|
||||||
// forced to re-install some of the types that it depends on even though
|
|
||||||
// these look unused to Knip
|
|
||||||
"@types/content-type",
|
|
||||||
"@types/sdp-transform",
|
"@types/sdp-transform",
|
||||||
"@types/uuid",
|
// We obviously use this, but if the package has been linked with pnpm link,
|
||||||
// We obviously use this, but if the package has been linked with yarn link,
|
|
||||||
// then Knip will flag it as a false positive
|
// then Knip will flag it as a false positive
|
||||||
// https://github.com/webpro-nl/knip/issues/766
|
// https://github.com/webpro-nl/knip/issues/766
|
||||||
"@vector-im/compound-web",
|
"@vector-im/compound-web",
|
||||||
"matrix-widget-api",
|
"matrix-widget-api",
|
||||||
|
// Used by oxlint
|
||||||
|
"eslint-plugin-element-call",
|
||||||
|
"eslint-plugin-storybook",
|
||||||
],
|
],
|
||||||
ignoreExportsUsedInFile: true,
|
ignoreExportsUsedInFile: true,
|
||||||
} satisfies KnipConfig;
|
} satisfies KnipConfig;
|
||||||
|
|||||||
@@ -4,27 +4,36 @@
|
|||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"close": "Затвори",
|
"close": "Затвори",
|
||||||
|
"copy_link": "Копиране на връзката",
|
||||||
"go": "Напред",
|
"go": "Напред",
|
||||||
|
"invite": "Покана",
|
||||||
"no": "Не",
|
"no": "Не",
|
||||||
"register": "Регистрация",
|
"register": "Регистрация",
|
||||||
"remove": "Премахни",
|
"remove": "Премахни",
|
||||||
"sign_in": "Влез",
|
"sign_in": "Влез",
|
||||||
"sign_out": "Излез"
|
"sign_out": "Излез",
|
||||||
|
"submit": "Израти"
|
||||||
},
|
},
|
||||||
|
"analytics_notice": "Когато участвате в тази бета, вие съгласявате се с събирането на анонимни данни, които използваме, за да подобрим продукта. Повечето информация за данните, които следим, можете да намерите в нашата <2>Политика за поверителност</2> и нашата <6>Политика за бисквитки</6>.",
|
||||||
"call_ended_view": {
|
"call_ended_view": {
|
||||||
"create_account_button": "Създай акаунт",
|
"create_account_button": "Създай акаунт",
|
||||||
"create_account_prompt": "<0>Защо не настройте парола за да запазите акаунта си?</0><1>Ще можете да запазите името и аватара си за бъдещи разговори</1>",
|
"create_account_prompt": "<0>Защо не настройте парола за да запазите акаунта си?</0><1>Ще можете да запазите името и аватара си за бъдещи разговори</1>",
|
||||||
"not_now_button": "Не сега, върни се на началния екран"
|
"feedback_done": "<0>Благодаря за обратната връзка!</0>",
|
||||||
|
"headline": "{{displayName}}, разговорът Ви приключи.",
|
||||||
|
"not_now_button": "Не сега, върни се на началния екран",
|
||||||
|
"survey_prompt": "Как мина?"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"audio": "Звук",
|
"audio": "Звук",
|
||||||
"avatar": "Аватар",
|
"avatar": "Аватар",
|
||||||
"display_name": "Име/псевдоним",
|
"display_name": "Име/псевдоним",
|
||||||
|
"encrypted": "Шифровано",
|
||||||
"home": "Начало",
|
"home": "Начало",
|
||||||
"loading": "Зареждане…",
|
"loading": "Зареждане…",
|
||||||
"password": "Парола",
|
"password": "Парола",
|
||||||
"profile": "Профил",
|
"profile": "Профил",
|
||||||
"settings": "Настройки",
|
"settings": "Настройки",
|
||||||
|
"unencrypted": "Нешифровано",
|
||||||
"username": "Потребителско име",
|
"username": "Потребителско име",
|
||||||
"video": "Видео"
|
"video": "Видео"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,40 +4,169 @@
|
|||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"close": "Zavřít",
|
"close": "Zavřít",
|
||||||
"go": "Pokračovat",
|
"copy_link": "Kopírovat odkaz",
|
||||||
|
"edit": "Upravit",
|
||||||
|
"go": "Přejít",
|
||||||
|
"invite": "Pozvat",
|
||||||
|
"lower_hand": "Snížení ruky",
|
||||||
"no": "Ne",
|
"no": "Ne",
|
||||||
|
"pick_reaction": "Vybrat reakci",
|
||||||
|
"raise_hand": "Zvednutí ruky",
|
||||||
"register": "Registrace",
|
"register": "Registrace",
|
||||||
"remove": "Odstranit",
|
"remove": "Odstranit",
|
||||||
|
"show_less": "Zobrazit méně",
|
||||||
|
"show_more": "Zobrazit více",
|
||||||
"sign_in": "Přihlásit se",
|
"sign_in": "Přihlásit se",
|
||||||
"sign_out": "Odhlásit se"
|
"sign_out": "Odhlásit se",
|
||||||
|
"submit": "Odeslat",
|
||||||
|
"upload_file": "Nahrát soubor"
|
||||||
},
|
},
|
||||||
|
"analytics_notice": "Účastí v této beta verzi souhlasíte se shromažďováním anonymních údajů, které používáme ke zlepšování produktu. Více informací o tom, které údaje sledujeme, najdete v našich <2>Zásadách ochrany osobních údajů</2> a <6>Zásadách používání souborů cookie</6>.",
|
||||||
"call_ended_view": {
|
"call_ended_view": {
|
||||||
"create_account_button": "Vytvořit účet",
|
"create_account_button": "Vytvořit účet",
|
||||||
"create_account_prompt": "<0>Proč neskončit nastavením hesla, abyste mohli účet použít znovu?</0><1>Budete si moci nechat své jméno a nastavit si avatar pro budoucí hovory </1>",
|
"create_account_prompt": "<0>Proč neskončit nastavením hesla, abyste mohli účet použít znovu?</0><1>Budete si moci nechat své jméno a nastavit si avatar pro budoucí hovory </1>",
|
||||||
"not_now_button": "Teď ne, vrátit se na domovskou obrazovku"
|
"feedback_done": "<0>Děkujeme za vaši zpětnou vazbu! </0>",
|
||||||
|
"feedback_prompt": "<0>Rádi si vyslechneme vaši zpětnou vazbu, abychom mohli vaše prostředí vylepšit. </0>",
|
||||||
|
"headline": "{{displayName}}, váš hovor skončil.",
|
||||||
|
"not_now_button": "Teď ne, vrátit se na domovskou obrazovku",
|
||||||
|
"reconnect_button": "Znovu připojit",
|
||||||
|
"survey_prompt": "Jak to probíhalo?"
|
||||||
},
|
},
|
||||||
|
"call_name": "Název hovoru",
|
||||||
"common": {
|
"common": {
|
||||||
|
"analytics": "Analytika",
|
||||||
|
"audio": "Zvuk",
|
||||||
|
"avatar": "Avatar",
|
||||||
|
"back": "Zpět",
|
||||||
"display_name": "Zobrazované jméno",
|
"display_name": "Zobrazované jméno",
|
||||||
|
"encrypted": "Šifrováno",
|
||||||
"home": "Domov",
|
"home": "Domov",
|
||||||
"loading": "Načítání…",
|
"loading": "Načítání…",
|
||||||
|
"next": "Další",
|
||||||
|
"options": "Možnosti",
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
|
"preferences": "Předvolby",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
|
"reaction": "Reakce",
|
||||||
|
"reactions": "Reakce",
|
||||||
|
"reconnecting": "Opětovné spojení...",
|
||||||
"settings": "Nastavení",
|
"settings": "Nastavení",
|
||||||
"username": "Uživatelské jméno"
|
"unencrypted": "Nešifrováno",
|
||||||
|
"username": "Uživatelské jméno",
|
||||||
|
"video": "Video"
|
||||||
},
|
},
|
||||||
|
"developer_mode": {
|
||||||
|
"always_show_iphone_earpiece": "Zobrazit možnost sluchátek pro iPhone na všech platformách",
|
||||||
|
"crypto_version": "Kryptografická verze: {{version}}",
|
||||||
|
"custom_livekit_url": {
|
||||||
|
"current_url": "Aktuálně nastaveno na: ",
|
||||||
|
"from_config": "Aktuálně není nastaveno žádné přepsání. Používá se URL z well-known nebo konfigurace.",
|
||||||
|
"label": "Vlastní Livekit-url",
|
||||||
|
"reset": "Resetovat přepsání",
|
||||||
|
"save": "Uložit",
|
||||||
|
"saving": "Ukládání..."
|
||||||
|
},
|
||||||
|
"debug_tile_layout_label": "Ladění rozložení dlaždic",
|
||||||
|
"device_id": "ID zařízení: {{id}}",
|
||||||
|
"duplicate_tiles_label": "Počet dalších kopií dlaždic na účastníka",
|
||||||
|
"environment_variables": "Proměnné prostředí",
|
||||||
|
"hostname": "Název hostitele: {{hostname}}",
|
||||||
|
"livekit_server_info": "Informace o serveru LiveKit",
|
||||||
|
"livekit_sfu": "LiveKit SFU: {{url}}",
|
||||||
|
"matrixRTCMode": {
|
||||||
|
"Comptibility": {
|
||||||
|
"description": "Kompatibilní s domovskými servery, které nepodporují přilnavé události (ale všechny ostatní EC klienti jsou v0.17.0 nebo novější)",
|
||||||
|
"label": "Kompatibilita: stavové události a více SFU"
|
||||||
|
},
|
||||||
|
"Legacy": {
|
||||||
|
"description": "Kompatibilní se starými verzemi EC, které nepodporují multi SFU",
|
||||||
|
"label": "Zastaralé: stavové události a nejstarší členské SFU"
|
||||||
|
},
|
||||||
|
"Matrix_2_0": {
|
||||||
|
"description": "Kompatibilní pouze s domovskými servery podporujícími přilnavé události a všemi klienty EC v0.17.0 nebo novějšími.",
|
||||||
|
"label": "Matrix 2.0: přilnavé události a multi SFU"
|
||||||
|
},
|
||||||
|
"title": "Režim MatrixRTC"
|
||||||
|
},
|
||||||
|
"matrix_id": "Matrix ID: {{id}}",
|
||||||
|
"mute_all_audio": "Ztlumit všechny zvuky (účastníci, reakce, zvuky připojení)",
|
||||||
|
"show_connection_stats": "Zobrazit statistiky připojení",
|
||||||
|
"url_params": "Parametry URL"
|
||||||
|
},
|
||||||
|
"disconnected_banner": "Připojení k serveru bylo ztraceno.",
|
||||||
|
"error": {
|
||||||
|
"call_is_not_supported": "Volání není podporováno",
|
||||||
|
"call_not_found": "Volání nebylo nalezeno",
|
||||||
|
"call_not_found_description": "<0>Zdá se, že tento odkaz nepatří k žádnému existujícímu hovoru. Zkontrolujte, zda máte správný odkaz, nebo<2> vytvořte nový</2>.</0>",
|
||||||
|
"connection_lost": "Spojení ztraceno",
|
||||||
|
"connection_lost_description": "Hovor byl přerušen.",
|
||||||
|
"e2ee_unsupported": "Nekompatibilní prohlížeč",
|
||||||
|
"e2ee_unsupported_description": "Váš webový prohlížeč nepodporuje šifrované hovory. Mezi podporované prohlížeče patří Chrome, Safari a Firefox 117+.",
|
||||||
|
"failed_to_start_livekit": "Nepodařilo se navázat připojení k Livekitu",
|
||||||
|
"generic": "Něco se pokazilo.",
|
||||||
|
"generic_description": "Odeslání protokolů ladění nám pomůže vystopovat problém.",
|
||||||
|
"insufficient_capacity": "Nedostatečná kapacita",
|
||||||
|
"insufficient_capacity_description": "Server dosáhl své maximální kapacity a v tuto chvíli se nemůžete připojit k hovoru. Zkuste to později nebo se obraťte na správce serveru, pokud problém přetrvává.",
|
||||||
|
"matrix_rtc_transport_missing": "Server není nakonfigurován pro práci s {{brand}}. Obraťte se na správce serveru (Doména: {{domain}}, Kód chyby: {{ errorCode }}).",
|
||||||
|
"membership_manager": "Chyba Správce členství",
|
||||||
|
"membership_manager_description": "Správce členství musel být ukončen. To je způsobeno mnoha po sobě jdoucími neúspěšnými síťovými požadavky.",
|
||||||
|
"no_matrix_2_authorization_service": "Autorizační služba vašeho mediálního serveru (SFU) je zastaralá.",
|
||||||
|
"open_elsewhere": "Otevřeno na jiné kartě",
|
||||||
|
"open_elsewhere_description": "{{brand}} byl otevřen v jiné záložce. Pokud to nezní správně, zkuste stránku znovu načíst.",
|
||||||
|
"room_creation_restricted": "Nepodařilo se vytvořit hovor",
|
||||||
|
"room_creation_restricted_description": "Vytváření hovorů může být omezeno pouze na oprávněné uživatele. Zkuste to znovu později nebo se obraťte na správce serveru, pokud problém přetrvává.",
|
||||||
|
"unexpected_ec_error": "Došlo k neočekávané chybě (<0>Error Code:</0> <1>{{ errorCode }}</1>). Obraťte se prosím na správce serveru."
|
||||||
|
},
|
||||||
|
"group_call_loader": {
|
||||||
|
"banned_body": "Byl jste vykázán z místnosti.",
|
||||||
|
"banned_heading": "Zabanován",
|
||||||
|
"call_ended_body": "Byl jste vyřazen z hovoru.",
|
||||||
|
"call_ended_heading": "Hovor ukončen",
|
||||||
|
"knock_reject_body": "Vaše žádost o připojení byla zamítnuta.",
|
||||||
|
"knock_reject_heading": "Přístup odepřen",
|
||||||
|
"reason": "Důvod"
|
||||||
|
},
|
||||||
|
"handset": {
|
||||||
|
"overlay_back_button": "Zpět do režimu reproduktoru",
|
||||||
|
"overlay_description": "Funguje pouze při používání aplikace",
|
||||||
|
"overlay_title": "Režim sluchátka"
|
||||||
|
},
|
||||||
|
"hangup_button_label": "Ukončit hovor",
|
||||||
"header_label": "Domov Element Call",
|
"header_label": "Domov Element Call",
|
||||||
|
"header_participants_label": "Účastníci",
|
||||||
|
"invite_modal": {
|
||||||
|
"link_copied_toast": "Odkaz zkopírován do schránky",
|
||||||
|
"title": "Pozvat na tento hovor"
|
||||||
|
},
|
||||||
"join_existing_call_modal": {
|
"join_existing_call_modal": {
|
||||||
"join_button": "Ano, připojit se",
|
"join_button": "Ano, připojit se",
|
||||||
"text": "Tento hovor již existuje, chcete se připojit?",
|
"text": "Tento hovor již existuje, chcete se připojit?",
|
||||||
"title": "Připojit se k existujícimu hovoru?"
|
"title": "Připojit se k existujícimu hovoru?"
|
||||||
},
|
},
|
||||||
|
"layout_grid_label": "Mřížka",
|
||||||
"layout_spotlight_label": "Soustředěný mód",
|
"layout_spotlight_label": "Soustředěný mód",
|
||||||
|
"layout_switch_label": "Rozvržení",
|
||||||
"lobby": {
|
"lobby": {
|
||||||
"join_button": "Připojit se k hovoru"
|
"ask_to_join": "Žádost o připojení k hovoru",
|
||||||
|
"join_as_guest": "Připojte se jako host",
|
||||||
|
"join_button": "Připojit se k hovoru",
|
||||||
|
"leave_button": "Zpět na poslední",
|
||||||
|
"waiting_for_invite": "Žádost odeslána! Čekáme na povolení k připojení..."
|
||||||
},
|
},
|
||||||
|
"log_in": "Přihlášení",
|
||||||
"logging_in": "Přihlašování se…",
|
"logging_in": "Přihlašování se…",
|
||||||
"login_auth_links": "<0>Vytvořit účet</0> Or <2>Jako host</2>",
|
"login_auth_links": "<0>Vytvořit účet</0> Or <2>Jako host</2>",
|
||||||
|
"login_auth_links_prompt": "Ještě nejste zaregistrováni?",
|
||||||
|
"login_subheading": "Chcete-li pokračovat na Element",
|
||||||
"login_title": "Přihlášení",
|
"login_title": "Přihlášení",
|
||||||
|
"microphone_off": "Mikrofon vypnutý",
|
||||||
|
"microphone_on": "Mikrofon zapnutý",
|
||||||
|
"mute_microphone_button_label": "Ztlumit mikrofon",
|
||||||
|
"participant_count_one": "{{count, number}}",
|
||||||
|
"participant_count_few": "{{count, number}}",
|
||||||
|
"participant_count_other": "{{count, number}}",
|
||||||
|
"qr_code": "QR kód",
|
||||||
|
"rageshake_button_error_caption": "Znovu zkusit odeslat protokoly",
|
||||||
"rageshake_request_modal": {
|
"rageshake_request_modal": {
|
||||||
"body": "Jiný uživatel v tomto hovoru má problémy. Abychom mohli diagnostikovat problém, rádi bychom shromáždili protokoly ladění.",
|
"body": "Jiný uživatel v tomto hovoru má problémy. Abychom mohli diagnostikovat problém, rádi bychom shromáždili protokoly ladění.",
|
||||||
"title": "Žádost o protokoly ladění"
|
"title": "Žádost o protokoly ladění"
|
||||||
@@ -45,22 +174,87 @@
|
|||||||
"rageshake_send_logs": "Poslat ladící záznam",
|
"rageshake_send_logs": "Poslat ladící záznam",
|
||||||
"rageshake_sending": "Posílání…",
|
"rageshake_sending": "Posílání…",
|
||||||
"rageshake_sending_logs": "Posílání ladícího záznamu…",
|
"rageshake_sending_logs": "Posílání ladícího záznamu…",
|
||||||
|
"rageshake_sent": "Díky!",
|
||||||
"recaptcha_dismissed": "Recaptcha byla zamítnuta",
|
"recaptcha_dismissed": "Recaptcha byla zamítnuta",
|
||||||
"recaptcha_not_loaded": "Recaptcha se nenačetla",
|
"recaptcha_not_loaded": "Recaptcha se nenačetla",
|
||||||
|
"recaptcha_ssla_caption": "Tato stránka je chráněna ReCAPTCHA a platí zde <2>Ochrana osobních údajů</2> a <6>Podmínky služby</6> od Google.<9></9>Kliknutím na \"Registrovat\" souhlasíte s naší <12>Licenční smlouvou na software a služby (SSLA)</12>",
|
||||||
"register": {
|
"register": {
|
||||||
"passwords_must_match": "Hesla se musí shodovat",
|
"passwords_must_match": "Hesla se musí shodovat",
|
||||||
"registering": "Registrování…"
|
"registering": "Registrování…"
|
||||||
},
|
},
|
||||||
"register_auth_links": "<0>Už máte účet?</0><1><0>Přihlásit se</0> Or <2>Jako host</2></1>",
|
"register_auth_links": "<0>Už máte účet?</0><1><0>Přihlásit se</0> Or <2>Jako host</2></1>",
|
||||||
"register_confirm_password_label": "Potvrdit heslo",
|
"register_confirm_password_label": "Potvrdit heslo",
|
||||||
|
"register_heading": "Vytvořte si účet",
|
||||||
"return_home_button": "Vrátit se na domácí obrazovku",
|
"return_home_button": "Vrátit se na domácí obrazovku",
|
||||||
|
"room_auth_view_continue_button": "Pokračovat",
|
||||||
|
"room_auth_view_ssla_caption": "Kliknutím na „Připojit se k hovoru nyní“ souhlasíte s naší <2>Licenční smlouvou o softwaru a službách (SSLA) </2>",
|
||||||
"screenshare_button_label": "Sdílet obrazovku",
|
"screenshare_button_label": "Sdílet obrazovku",
|
||||||
"settings": {
|
"settings": {
|
||||||
|
"audio_tab": {
|
||||||
|
"effect_volume_description": "Upravit hlasitost přehrávání reakcí a efektů zvednutých rukou.",
|
||||||
|
"effect_volume_label": "Hlasitost zvukového efektu"
|
||||||
|
},
|
||||||
|
"background_blur_header": "Pozadí",
|
||||||
|
"background_blur_label": "Rozostřit pozadí videa",
|
||||||
|
"blur_not_supported_by_browser": "(Toto zařízení nepodporuje rozostření pozadí.)",
|
||||||
"developer_tab_title": "Vývojář",
|
"developer_tab_title": "Vývojář",
|
||||||
|
"devices": {
|
||||||
|
"camera": "Fotoaparát",
|
||||||
|
"camera_numbered": "Fotoaparát {{n}}",
|
||||||
|
"change_device_button": "Změnit zvukové zařízení",
|
||||||
|
"default": "Výchozí",
|
||||||
|
"default_named": "Výchozí <2> ({{name}}) </2>",
|
||||||
|
"handset": "Sluchátko",
|
||||||
|
"loudspeaker": "Reproduktor",
|
||||||
|
"microphone": "Mikrofon",
|
||||||
|
"microphone_numbered": "Mikrofon {{n}}",
|
||||||
|
"speaker": "Reproduktor",
|
||||||
|
"speaker_numbered": "Reproduktor {{n}}"
|
||||||
|
},
|
||||||
|
"feedback_tab_body": "Pokud se potýkáte s problémy nebo byste nám jen chtěli poskytnout zpětnou vazbu, pošlete nám prosím krátký popis níže.",
|
||||||
|
"feedback_tab_description_label": "Vaše zpětná vazba",
|
||||||
"feedback_tab_h4": "Dát feedback",
|
"feedback_tab_h4": "Dát feedback",
|
||||||
"feedback_tab_send_logs_label": "Zahrnout ladící záznamy"
|
"feedback_tab_send_logs_label": "Zahrnout ladící záznamy",
|
||||||
|
"feedback_tab_thank_you": "Děkujeme, obdrželi jsme vaši zpětnou vazbu!",
|
||||||
|
"feedback_tab_title": "Zpětná vazba",
|
||||||
|
"opt_in_description": "<0></0><1></1>Souhlas můžete odvolat zrušením zaškrtnutí tohoto políčka. Pokud jste právě v hovoru, toto nastavení se projeví po jeho skončení.",
|
||||||
|
"preferences_tab": {
|
||||||
|
"developer_mode_label": "Vývojářský režim",
|
||||||
|
"developer_mode_label_description": "Povolte vývojářský režim a zobrazte kartu nastavení vývojáře.",
|
||||||
|
"introduction": "Zde můžete nakonfigurovat další možnosti pro lepší zážitek.",
|
||||||
|
"reactions_play_sound_description": "Přehrát zvukový efekt, když někdo odešle reakci do hovoru.",
|
||||||
|
"reactions_play_sound_label": "Přehrát reakční zvuky",
|
||||||
|
"reactions_show_description": "Zobrazit animaci, když někdo pošle reakci.",
|
||||||
|
"reactions_show_label": "Zobrazit reakce",
|
||||||
|
"show_hand_raised_timer_description": "Zobrazit časovač, když účastník zvedne ruku",
|
||||||
|
"show_hand_raised_timer_label": "Zobrazit dobu trvání zvednutí ruky"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
"star_rating_input_label_one": "{{count}} hvězdička",
|
||||||
|
"star_rating_input_label_few": "{{count}} hvězdičky",
|
||||||
|
"star_rating_input_label_other": "{{count}} hvězdiček",
|
||||||
|
"start_new_call": "Zahájit nový hovor",
|
||||||
|
"start_video_button_label": "Spustit video",
|
||||||
|
"stop_screenshare_button_label": "Sdílení obrazovky",
|
||||||
|
"stop_video_button_label": "Zastavit video",
|
||||||
|
"submitting": "Odeslání...",
|
||||||
|
"switch_camera": "Přepnout kameru",
|
||||||
"unauthenticated_view_body": "Nejste registrovaní? <2>Vytvořit účet</2>",
|
"unauthenticated_view_body": "Nejste registrovaní? <2>Vytvořit účet</2>",
|
||||||
"unauthenticated_view_login_button": "Přihlásit se ke svému účtu",
|
"unauthenticated_view_login_button": "Přihlásit se ke svému účtu",
|
||||||
"version": "Verze: {{version}}"
|
"unauthenticated_view_ssla_caption": "Kliknutím na tlačítko \"Přejít\" souhlasíte s naší <2>Licenční smlouvou na software a služby (SSLA)</2>",
|
||||||
|
"unmute_microphone_button_label": "Zrušit ztlumení mikrofonu",
|
||||||
|
"version": "{{productName}}verze: {{version}}",
|
||||||
|
"video_tile": {
|
||||||
|
"always_show": "Vždy zobrazit",
|
||||||
|
"call_ended": "Hovor ukončen",
|
||||||
|
"calling": "Volání…",
|
||||||
|
"camera_starting": "Načítání videa...",
|
||||||
|
"collapse": "Sbalit",
|
||||||
|
"expand": "Rozbalit",
|
||||||
|
"mute_for_me": "Pro mě ztlumit",
|
||||||
|
"muted_for_me": "Pro mě ztlumené",
|
||||||
|
"screen_share_volume": "Hlasitost sdílení obrazovky",
|
||||||
|
"volume": "Hlasitost",
|
||||||
|
"waiting_for_media": "Čekání na média..."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
226
locales/da/app.json
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
{
|
||||||
|
"a11y": {
|
||||||
|
"user_menu": "Brugermenu"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"close": "Luk",
|
||||||
|
"copy_link": "Kopiér link",
|
||||||
|
"edit": "Rediger",
|
||||||
|
"go": "Gå",
|
||||||
|
"invite": "Invitér",
|
||||||
|
"lower_hand": "Sænk hånd",
|
||||||
|
"no": "Nej",
|
||||||
|
"pick_reaction": "Vælg reaktion",
|
||||||
|
"raise_hand": "Ræk hånden op",
|
||||||
|
"register": "Registrér",
|
||||||
|
"remove": "Fjern",
|
||||||
|
"show_less": "Vis mindre",
|
||||||
|
"show_more": "Vis mere",
|
||||||
|
"sign_in": "Log ind",
|
||||||
|
"sign_out": "Log ud",
|
||||||
|
"submit": "Indsend",
|
||||||
|
"upload_file": "Upload fil"
|
||||||
|
},
|
||||||
|
"analytics_notice": "Ved at deltage i denne beta giver du samtykke til indsamling af anonyme data, som vi bruger til at forbedre produktet. Du kan finde flere oplysninger om, hvilke data vi sporer, i vores <2>fortrolighedspolitik</2> og vores <6>cookiepolitik</6>.",
|
||||||
|
"call_ended_view": {
|
||||||
|
"create_account_button": "Opret konto",
|
||||||
|
"create_account_prompt": "<0>Hvorfor ikke afslutte med at oprette en adgangskode for at beholde din konto? </0><1>Du kan beholde dit navn og indstille en avatar til brug ved fremtidige opkald </1>",
|
||||||
|
"feedback_done": "<0>Tak for din feedback! </0>",
|
||||||
|
"feedback_prompt": "<0>Vi vil meget gerne høre din feedback, så vi kan forbedre din oplevelse.</0>",
|
||||||
|
"headline": "{{displayName}}, dit opkald er afsluttet.",
|
||||||
|
"not_now_button": "Ikke nu, vend tilbage til startskærmen",
|
||||||
|
"reconnect_button": "Tilslut igen",
|
||||||
|
"survey_prompt": "Hvordan gik det?"
|
||||||
|
},
|
||||||
|
"call_name": "Navn på opkald",
|
||||||
|
"common": {
|
||||||
|
"analytics": "Analyse-værktøj",
|
||||||
|
"audio": "Lyd",
|
||||||
|
"avatar": "Avatar",
|
||||||
|
"back": "Tilbage",
|
||||||
|
"display_name": "Vist navn",
|
||||||
|
"encrypted": "Krypteret",
|
||||||
|
"home": "Hjem",
|
||||||
|
"loading": "Indlæser...",
|
||||||
|
"next": "Næste",
|
||||||
|
"options": "Valgmuligheder",
|
||||||
|
"password": "Adgangskode",
|
||||||
|
"preferences": "Foretrukne",
|
||||||
|
"profile": "Profil",
|
||||||
|
"reaction": "Reaktion",
|
||||||
|
"reactions": "Reaktioner",
|
||||||
|
"reconnecting": "Genopretter forbindelse…",
|
||||||
|
"settings": "Indstillinger",
|
||||||
|
"unencrypted": "Ikke krypteret",
|
||||||
|
"username": "Brugernavn",
|
||||||
|
"video": "Video"
|
||||||
|
},
|
||||||
|
"developer_mode": {
|
||||||
|
"always_show_iphone_earpiece": "Vis mulighed for iPhone-høretelefon på alle platforme",
|
||||||
|
"crypto_version": "Krypto-version: {{version}}",
|
||||||
|
"debug_tile_layout_label": "Fejlfinding af fliselayout",
|
||||||
|
"device_id": "Enheds-id: {{id}}",
|
||||||
|
"duplicate_tiles_label": "Antal ekstra flisekopier pr. deltager",
|
||||||
|
"environment_variables": "Miljøvariabler",
|
||||||
|
"hostname": "Værtsnavn: {{hostname}}",
|
||||||
|
"livekit_server_info": "LiveKit Serverinfo",
|
||||||
|
"livekit_sfu": "LiveKit SFU: {{url}}",
|
||||||
|
"matrix_id": "Matrix ID: {{id}}",
|
||||||
|
"mute_all_audio": "Slå al lyd fra (deltagere, reaktioner, deltagelseslyde)",
|
||||||
|
"show_connection_stats": "Vis forbindelsesstatistik",
|
||||||
|
"url_params": "URL-parametre"
|
||||||
|
},
|
||||||
|
"disconnected_banner": "Forbindelsen til serveren er gået tabt.",
|
||||||
|
"error": {
|
||||||
|
"call_is_not_supported": "Opkald er ikke understøttet",
|
||||||
|
"call_not_found": "Opkald ikke fundet",
|
||||||
|
"call_not_found_description": "<0>Det link ser ikke ud til at høre til et eksisterende opkald. Tjek at du har det rigtige link, eller <2> opret et nyt</2>.</0>",
|
||||||
|
"connection_lost": "Forbindelsen gik tabt",
|
||||||
|
"connection_lost_description": "Du blev afbrudt fra opkaldet.",
|
||||||
|
"e2ee_unsupported": "Inkompatibel browser",
|
||||||
|
"e2ee_unsupported_description": "Din webbrowser understøtter ikke krypterede opkald. Understøttede browsere inkluderer Chrome, Safari og Firefox 117+.",
|
||||||
|
"generic": "Noget gik galt",
|
||||||
|
"generic_description": "Indsendelse af fejlfindingslogfiler hjælper os med at spore problemet.",
|
||||||
|
"insufficient_capacity": "Utilstrækkelig kapacitet",
|
||||||
|
"insufficient_capacity_description": "Serveren har nået sin maksimale kapacitet, og du kan ikke deltage i opkaldet på dette tidspunkt. Prøv igen senere, eller kontakt din serveradministrator, hvis problemet fortsætter.",
|
||||||
|
"open_elsewhere": "Åbnet i en anden fane",
|
||||||
|
"open_elsewhere_description": "{{brand}} er blevet åbnet i en anden fane. Hvis det ikke lyder rigtigt, kan du prøve at genindlæse siden.",
|
||||||
|
"room_creation_restricted": "Kunne ikke oprette opkald",
|
||||||
|
"room_creation_restricted_description": "Oprettelse af opkald er muligvis begrænset til autoriserede brugere. Prøv igen senere, eller kontakt din serveradministrator, hvis problemet fortsætter.",
|
||||||
|
"unexpected_ec_error": "Der opstod en uventet fejl (<0>Fejlkode:</0> <1> {{ errorCode }}</1>). Kontakt venligst din serveradministrator."
|
||||||
|
},
|
||||||
|
"group_call_loader": {
|
||||||
|
"banned_body": "Du er blevet spærret fra rummet.",
|
||||||
|
"banned_heading": "Spærret",
|
||||||
|
"call_ended_body": "Du er blevet fjernet fra opkaldet.",
|
||||||
|
"call_ended_heading": "Opkaldet afsluttet",
|
||||||
|
"knock_reject_body": "Din anmodning om at deltage blev afvist.",
|
||||||
|
"knock_reject_heading": "Adgang nægtet",
|
||||||
|
"reason": "Årsag: {{reason}}"
|
||||||
|
},
|
||||||
|
"handset": {
|
||||||
|
"overlay_back_button": "Tilbage til højttalertilstand",
|
||||||
|
"overlay_description": "Virker kun, når du bruger appen",
|
||||||
|
"overlay_title": "Telefon-højtaler"
|
||||||
|
},
|
||||||
|
"hangup_button_label": "Afslut opkald",
|
||||||
|
"header_label": "Element Ring hjem",
|
||||||
|
"header_participants_label": "Deltagere",
|
||||||
|
"invite_modal": {
|
||||||
|
"link_copied_toast": "Link kopieret til udklipsholder",
|
||||||
|
"title": "Inviter til dette opkald"
|
||||||
|
},
|
||||||
|
"join_existing_call_modal": {
|
||||||
|
"join_button": "Ja, deltag i opkald",
|
||||||
|
"text": "Dette opkald findes allerede, vil du være med?",
|
||||||
|
"title": "Deltag i eksisterende opkald?"
|
||||||
|
},
|
||||||
|
"layout_grid_label": "Gitter",
|
||||||
|
"layout_spotlight_label": "Spotlys",
|
||||||
|
"lobby": {
|
||||||
|
"ask_to_join": "Anmod om at deltage i opkaldet",
|
||||||
|
"join_as_guest": "Deltag som gæst",
|
||||||
|
"join_button": "Deltag i opkald",
|
||||||
|
"leave_button": "Tilbage til seneste",
|
||||||
|
"waiting_for_invite": "Anmodning sendt! Venter på tilladelse til at deltage..."
|
||||||
|
},
|
||||||
|
"log_in": "Log ind",
|
||||||
|
"logging_in": "Logger ind...",
|
||||||
|
"login_auth_links": "<0>Opret en konto </0> eller <2> få adgang som gæst </2>",
|
||||||
|
"login_auth_links_prompt": "Ikke registreret endnu?",
|
||||||
|
"login_subheading": "For at fortsætte til Element",
|
||||||
|
"login_title": "Login",
|
||||||
|
"microphone_off": "Mikrofon slukket",
|
||||||
|
"microphone_on": "Mikrofon tændt",
|
||||||
|
"mute_microphone_button_label": "Slå mikrofonen fra",
|
||||||
|
"participant_count_one": "{{count, number}}",
|
||||||
|
"participant_count_other": "{{count, number}}",
|
||||||
|
"qr_code": "QR-kode",
|
||||||
|
"rageshake_button_error_caption": "Prøv at sende logfiler igen",
|
||||||
|
"rageshake_request_modal": {
|
||||||
|
"body": "En anden bruger på dette opkald har et problem. For bedre at kunne diagnosticere sådanne problemer, vil vi gerne indsamle en fejlfindingslog.",
|
||||||
|
"title": "Anmodning om fejlfindingslogfil"
|
||||||
|
},
|
||||||
|
"rageshake_send_logs": "Send fejlfindingslogfiler",
|
||||||
|
"rageshake_sending": "Sender...",
|
||||||
|
"rageshake_sending_logs": "Afsendelse af fejlfindingslogfiler...",
|
||||||
|
"rageshake_sent": "Tak!",
|
||||||
|
"recaptcha_dismissed": "Recaptcha afvist",
|
||||||
|
"recaptcha_not_loaded": "Recaptcha ikke indlæst",
|
||||||
|
"recaptcha_ssla_caption": "Dette websted er beskyttet af ReCAPTCHA og Googles <2>Privatlivspolitik</2> og <6>Servicevilkår</6> gælder.<9></9>Ved at klikke på \"Registrer\" accepterer du vores <12>Software og Services Licensaftale (SSLA)</12>",
|
||||||
|
"register": {
|
||||||
|
"passwords_must_match": "Adgangskoderne skal være identiske",
|
||||||
|
"registering": "Registrering..."
|
||||||
|
},
|
||||||
|
"register_auth_links": "<0>Har du allerede en konto? </0><1><0>Log ind </0> eller <2> få adgang som gæst </2> </1>",
|
||||||
|
"register_confirm_password_label": "Bekræft adgangskode",
|
||||||
|
"register_heading": "Opret din konto",
|
||||||
|
"return_home_button": "Vend tilbage til startskærmen",
|
||||||
|
"room_auth_view_continue_button": "Fortsæt",
|
||||||
|
"room_auth_view_ssla_caption": "Ved at klikke på „Deltag i opkald nu“ accepterer du vores <2> Software og Services Licensaftale (SSLA) </2>",
|
||||||
|
"screenshare_button_label": "Del din skærm",
|
||||||
|
"settings": {
|
||||||
|
"audio_tab": {
|
||||||
|
"effect_volume_description": "Juster den lydstyrke som reaktioner og håndsoprækninger afspilles med.",
|
||||||
|
"effect_volume_label": "Lydstyrke for lydeffekter"
|
||||||
|
},
|
||||||
|
"background_blur_header": "Baggrund",
|
||||||
|
"background_blur_label": "Gør videoens baggrund sløret",
|
||||||
|
"blur_not_supported_by_browser": "(Baggrundssløring understøttes ikke af denne enhed.)",
|
||||||
|
"developer_tab_title": "Udvikler",
|
||||||
|
"devices": {
|
||||||
|
"camera": "Kamera",
|
||||||
|
"camera_numbered": "Kamera {{n}}",
|
||||||
|
"change_device_button": "Skift lydenhed",
|
||||||
|
"default": "Standard",
|
||||||
|
"default_named": "Standard <2>({{name}})</2>",
|
||||||
|
"handset": "Telefon",
|
||||||
|
"loudspeaker": "Højttaler",
|
||||||
|
"microphone": "Mikrofon",
|
||||||
|
"microphone_numbered": "Mikrofon {{n}}",
|
||||||
|
"speaker": "Højttaler",
|
||||||
|
"speaker_numbered": "Højttaler {{n}}"
|
||||||
|
},
|
||||||
|
"feedback_tab_body": "Hvis du oplever problemer eller bare gerne vil give feedback, kan du sende os en kort beskrivelse herunder.",
|
||||||
|
"feedback_tab_description_label": "Din tilbagemelding",
|
||||||
|
"feedback_tab_h4": "Indsend feedback",
|
||||||
|
"feedback_tab_send_logs_label": "Medtag fejlfindingslogfiler",
|
||||||
|
"feedback_tab_thank_you": "Tak, vi har modtaget din feedback!",
|
||||||
|
"feedback_tab_title": "Feedback",
|
||||||
|
"opt_in_description": "<0></0><1></1>Du kan trække dit samtykke tilbage ved at fjerne markeringen i dette felt. Hvis du i øjeblikket er i gang med et opkald, træder denne indstilling i kraft ved afslutningen af opkaldet.",
|
||||||
|
"preferences_tab": {
|
||||||
|
"developer_mode_label": "Udviklertilstand",
|
||||||
|
"developer_mode_label_description": "Aktivér udviklertilstand og vis fanen udviklerindstillinger.",
|
||||||
|
"introduction": "Her kan du konfigurere ekstra muligheder for en forbedret oplevelse.",
|
||||||
|
"reactions_play_sound_description": "Afspil en lydeffekt, når nogen sender en reaktion i et opkald.",
|
||||||
|
"reactions_play_sound_label": "Afspil reaktionslyde",
|
||||||
|
"reactions_show_description": "Vis en animation, når nogen sender en reaktion.",
|
||||||
|
"reactions_show_label": "Vis reaktioner",
|
||||||
|
"show_hand_raised_timer_description": "Vis en timer, når en deltager rækker hånden",
|
||||||
|
"show_hand_raised_timer_label": "Vis varighed af håndsoprækning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"star_rating_input_label_one": "{{count}} stjerne",
|
||||||
|
"star_rating_input_label_other": "{{count}} stjerner",
|
||||||
|
"start_new_call": "Start nyt opkald",
|
||||||
|
"start_video_button_label": "Start video",
|
||||||
|
"stop_screenshare_button_label": "Skærmen bliver delt",
|
||||||
|
"stop_video_button_label": "Stop video",
|
||||||
|
"submitting": "Indsender...",
|
||||||
|
"switch_camera": "Skift kamera",
|
||||||
|
"unauthenticated_view_body": "Ikke registreret endnu? <2>Opret en konto </2>",
|
||||||
|
"unauthenticated_view_login_button": "Log ind på din konto",
|
||||||
|
"unauthenticated_view_ssla_caption": "Ved at klikke på \"Start\" accepterer du vores <2>Software og Services Licensaftale (SSLA)</2>",
|
||||||
|
"unmute_microphone_button_label": "Slå mikrofonen til",
|
||||||
|
"version": "{{productName}} version: {{version}}",
|
||||||
|
"video_tile": {
|
||||||
|
"always_show": "Vis altid",
|
||||||
|
"camera_starting": "Indlæser video",
|
||||||
|
"collapse": "Fold sammen",
|
||||||
|
"expand": "Udvid",
|
||||||
|
"mute_for_me": "Slå lyden fra for mig",
|
||||||
|
"muted_for_me": "Dæmpet for mig",
|
||||||
|
"volume": "Lydstyrke",
|
||||||
|
"waiting_for_media": "Venter på medier..."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,12 +22,6 @@
|
|||||||
"upload_file": "Datei hochladen"
|
"upload_file": "Datei hochladen"
|
||||||
},
|
},
|
||||||
"analytics_notice": "Mit der Teilnahme an der Beta akzeptierst du die Sammlung von anonymen Daten, die wir zur Verbesserung des Produkts verwenden. Weitere Informationen zu den von uns erhobenen Daten findest du in unserer <2>Datenschutzerklärung</2> und unseren <6>Cookie-Richtlinien</6>.",
|
"analytics_notice": "Mit der Teilnahme an der Beta akzeptierst du die Sammlung von anonymen Daten, die wir zur Verbesserung des Produkts verwenden. Weitere Informationen zu den von uns erhobenen Daten findest du in unserer <2>Datenschutzerklärung</2> und unseren <6>Cookie-Richtlinien</6>.",
|
||||||
"app_selection_modal": {
|
|
||||||
"continue_in_browser": "Weiter im Browser",
|
|
||||||
"open_in_app": "In der App öffnen",
|
|
||||||
"text": "Bereit, beizutreten?",
|
|
||||||
"title": "App auswählen"
|
|
||||||
},
|
|
||||||
"call_ended_view": {
|
"call_ended_view": {
|
||||||
"create_account_button": "Konto erstellen",
|
"create_account_button": "Konto erstellen",
|
||||||
"create_account_prompt": "<0>Warum vergibst du nicht abschließend ein Passwort, um dein Konto zu erhalten?</0><1>Du kannst deinen Namen behalten und ein Profilbild für zukünftige Anrufe festlegen.</1>",
|
"create_account_prompt": "<0>Warum vergibst du nicht abschließend ein Passwort, um dein Konto zu erhalten?</0><1>Du kannst deinen Namen behalten und ein Profilbild für zukünftige Anrufe festlegen.</1>",
|
||||||
@@ -55,13 +49,23 @@
|
|||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"reaction": "Reaktion",
|
"reaction": "Reaktion",
|
||||||
"reactions": "Reaktionen",
|
"reactions": "Reaktionen",
|
||||||
|
"reconnecting": "Verbindung wird wiederhergestellt...",
|
||||||
"settings": "Einstellungen",
|
"settings": "Einstellungen",
|
||||||
"unencrypted": "Nicht verschlüsselt",
|
"unencrypted": "Nicht verschlüsselt",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"video": "Video"
|
"video": "Video"
|
||||||
},
|
},
|
||||||
"developer_mode": {
|
"developer_mode": {
|
||||||
|
"always_show_iphone_earpiece": "iPhone-Ohrhörer-Option auf allen Plattformen anzeigen",
|
||||||
"crypto_version": "Krypto-Version: {{version}}",
|
"crypto_version": "Krypto-Version: {{version}}",
|
||||||
|
"custom_livekit_url": {
|
||||||
|
"current_url": "Derzeit eingestellt auf: ",
|
||||||
|
"from_config": "Derzeit ist keine spezielle (benutzerdefinierte) URL eingestellt. Daher wird automatisch die URL verwendet, die entweder via „.well-known“ oder in der Webbrowser-Konfiguration („config“) hinterlegt ist.",
|
||||||
|
"label": "Benutzerdefinierte Livekit-URL",
|
||||||
|
"reset": "Zurücksetzen der benutzerdefinierten URL",
|
||||||
|
"save": "Speichern",
|
||||||
|
"saving": "Speichern..."
|
||||||
|
},
|
||||||
"debug_tile_layout_label": "Kachel-Layout debuggen",
|
"debug_tile_layout_label": "Kachel-Layout debuggen",
|
||||||
"device_id": "Geräte-ID: {{id}}",
|
"device_id": "Geräte-ID: {{id}}",
|
||||||
"duplicate_tiles_label": "Anzahl zusätzlicher Kachelkopien pro Teilnehmer",
|
"duplicate_tiles_label": "Anzahl zusätzlicher Kachelkopien pro Teilnehmer",
|
||||||
@@ -69,28 +73,48 @@
|
|||||||
"hostname": "Hostname: {{hostname}}",
|
"hostname": "Hostname: {{hostname}}",
|
||||||
"livekit_server_info": "LiveKit-Server Informationen",
|
"livekit_server_info": "LiveKit-Server Informationen",
|
||||||
"livekit_sfu": "LiveKit SFU: {{url}}",
|
"livekit_sfu": "LiveKit SFU: {{url}}",
|
||||||
|
"matrixRTCMode": {
|
||||||
|
"Comptibility": {
|
||||||
|
"description": "Kompatibel mit Homeservern ohne Sticky Events Support, wobei alle beteiligten Element Call Clients v0.17.0 oder neuer sein müssen.",
|
||||||
|
"label": "Kompatibilität: State Events & Multi-SFU"
|
||||||
|
},
|
||||||
|
"Legacy": {
|
||||||
|
"description": "Kompatibel mit älteren Versionen von Element Call, welche Multi-SFU nicht unterstützen",
|
||||||
|
"label": "Legacy: State Events und \"Oldest Membership\" SFU"
|
||||||
|
},
|
||||||
|
"Matrix_2_0": {
|
||||||
|
"description": "Nur mit Homeservern kompatibel, die Sticky Events unterstützen, wobei alle beteiligten Element Call Clients Version v0.17.0 oder neuer sein müssen.",
|
||||||
|
"label": "Matrix 2.0: Sticky Events und Multi-SFU"
|
||||||
|
},
|
||||||
|
"title": "MatrixRTC Modus"
|
||||||
|
},
|
||||||
"matrix_id": "Matrix-ID: {{id}}",
|
"matrix_id": "Matrix-ID: {{id}}",
|
||||||
|
"mute_all_audio": "Stummschalten aller Audiosignale (Teilnehmer, Reaktionen, Beitrittsgeräusche)",
|
||||||
"show_connection_stats": "Verbindungsstatistiken anzeigen",
|
"show_connection_stats": "Verbindungsstatistiken anzeigen",
|
||||||
"show_non_member_tiles": "Kacheln für Nicht-Mitgliedermedien anzeigen",
|
"url_params": "URL-Parameter"
|
||||||
"url_params": "URL-Parameter",
|
|
||||||
"use_new_membership_manager": "Neuen MembershipManager verwenden"
|
|
||||||
},
|
},
|
||||||
"disconnected_banner": "Die Verbindung zum Server wurde getrennt.",
|
"disconnected_banner": "Die Verbindung zum Server wurde getrennt.",
|
||||||
"error": {
|
"error": {
|
||||||
"call_is_not_supported": "Anrufe werden nicht unterstützt",
|
"call_is_not_supported": "Anrufe werden nicht unterstützt",
|
||||||
"call_not_found": "Anruf nicht gefunden",
|
"call_not_found": "Anruf nicht gefunden",
|
||||||
"call_not_found_description": "<0>Dieser Link scheint zu keinem bestehenden Anruf zu gehören. Vergewissern Sie sich, dass Sie den richtigen Link haben, oder <1> erstellen Sie einen neuen</1>. </0>",
|
"call_not_found_description": "<0>Dieser Link scheint zu keinem bestehenden Anruf zu gehören. Es sollte geprüft werden, ob der Link korrekt ist, oder <2>ein neuer erstellt werden</2>.</0>",
|
||||||
"connection_lost": "Verbindung verloren",
|
"connection_lost": "Verbindung verloren",
|
||||||
"connection_lost_description": "Ihre Verbindung zum Anruf wurde unterbrochen.",
|
"connection_lost_description": "Ihre Verbindung zum Anruf wurde unterbrochen.",
|
||||||
"e2ee_unsupported": "Inkompatibler Browser",
|
"e2ee_unsupported": "Inkompatibler Browser",
|
||||||
"e2ee_unsupported_description": "Ihr Webbrowser unterstützt keine verschlüsselten Anrufe. Zu den unterstützten Browsern gehören Chrome, Safari und Firefox 117+.",
|
"e2ee_unsupported_description": "Ihr Webbrowser unterstützt keine verschlüsselten Anrufe. Zu den unterstützten Browsern gehören Chrome, Safari und Firefox 117+.",
|
||||||
|
"failed_to_start_livekit": "LiveKit-Verbindung konnte nicht hergestellt werden",
|
||||||
"generic": "Etwas ist schief gelaufen",
|
"generic": "Etwas ist schief gelaufen",
|
||||||
"generic_description": "Durch das Senden von Debugprotokollen können wir das Problem leichter eingrenzen.",
|
"generic_description": "Durch das Senden von Debugprotokollen können wir das Problem leichter eingrenzen.",
|
||||||
"insufficient_capacity": "Unzureichende Kapazität",
|
"insufficient_capacity": "Unzureichende Kapazität",
|
||||||
"insufficient_capacity_description": "Der Server hat seine maximale Kapazität erreicht, daher ist ein Beitritt zum Anruf derzeit nicht möglich. Bitte später erneut versuchen oder den Serveradministrator kontaktieren, falls das Problem weiterhin besteht.",
|
"insufficient_capacity_description": "Der Server hat seine maximale Kapazität erreicht, daher ist ein Beitritt zum Anruf derzeit nicht möglich. Bitte später erneut versuchen oder den Serveradministrator kontaktieren, falls das Problem weiterhin besteht.",
|
||||||
"matrix_rtc_focus_missing": "Der Server ist nicht für die Verwendung mit {{brand}} konfiguriert. Bitte den Serveradministrator kontaktieren (Domain: {{domain}}, Fehlercode: {{ errorCode }}).",
|
"matrix_rtc_transport_missing": "Der Server ist nicht für die Verwendung mit {{brand}} konfiguriert. Bitte den Server Admin kontaktieren (Domain: {{domain}}, Fehlercode: {{ errorCode }}).",
|
||||||
|
"membership_manager": "Fehler im MatrixRTC Mitgliedschaftsmanager",
|
||||||
|
"membership_manager_description": "Der MatrixRTC Mitgliedschaftsmanager wurde unerwartet aufgrund fehlgeschlagener Netzwerkanfragen beendet.",
|
||||||
|
"no_matrix_2_authorization_service": "Der Autorisierungsdienst des Medien Servers (SFU) ist veraltet.",
|
||||||
"open_elsewhere": "In einem anderen Tab geöffnet",
|
"open_elsewhere": "In einem anderen Tab geöffnet",
|
||||||
"open_elsewhere_description": "{{brand}} wurde in einem anderen Tab geöffnet. Wenn das nicht richtig klingt, versuchen Sie, die Seite neu zu laden.",
|
"open_elsewhere_description": "{{brand}} wurde in einem anderen Tab geöffnet. Wenn das nicht richtig klingt, versuche, die Seite neu zu laden.",
|
||||||
|
"room_creation_restricted": "Anruf konnte nicht erstellt werden",
|
||||||
|
"room_creation_restricted_description": "Das Erstellen von Anrufen ist nur für autorisierte Nutzer möglich. Versuche es später erneut oder kontaktiere deinen Serveradministrator, falls das Problem weiterhin besteht.",
|
||||||
"unexpected_ec_error": "Ein unerwarteter Fehler ist aufgetreten (<0>Fehlercode: </0> <1>{{ errorCode }}</1>). Bitte den Serveradministrator kontaktieren."
|
"unexpected_ec_error": "Ein unerwarteter Fehler ist aufgetreten (<0>Fehlercode: </0> <1>{{ errorCode }}</1>). Bitte den Serveradministrator kontaktieren."
|
||||||
},
|
},
|
||||||
"group_call_loader": {
|
"group_call_loader": {
|
||||||
@@ -102,6 +126,11 @@
|
|||||||
"knock_reject_heading": "Zugriff verweigert",
|
"knock_reject_heading": "Zugriff verweigert",
|
||||||
"reason": "Grund: {{reason}}"
|
"reason": "Grund: {{reason}}"
|
||||||
},
|
},
|
||||||
|
"handset": {
|
||||||
|
"overlay_back_button": "Zurück zum Lautsprechermodus",
|
||||||
|
"overlay_description": "Nur wenn App im Vordergrund nutzbar",
|
||||||
|
"overlay_title": "Ohrhörer Modus"
|
||||||
|
},
|
||||||
"hangup_button_label": "Anruf beenden",
|
"hangup_button_label": "Anruf beenden",
|
||||||
"header_label": "Element Call-Startseite",
|
"header_label": "Element Call-Startseite",
|
||||||
"header_participants_label": "Teilnehmende",
|
"header_participants_label": "Teilnehmende",
|
||||||
@@ -163,12 +192,18 @@
|
|||||||
"effect_volume_description": "Lautstärke anpassen, mit der Reaktionen und Handmeldungen abgespielt werden.",
|
"effect_volume_description": "Lautstärke anpassen, mit der Reaktionen und Handmeldungen abgespielt werden.",
|
||||||
"effect_volume_label": "Lautstärke der Soundeffekte"
|
"effect_volume_label": "Lautstärke der Soundeffekte"
|
||||||
},
|
},
|
||||||
|
"background_blur_header": "Hintergrund",
|
||||||
|
"background_blur_label": "Unschärfeeffekt für den Hintergrund aktivieren",
|
||||||
|
"blur_not_supported_by_browser": "(Hintergrundunschärfe wird von diesem Gerät nicht unterstützt.)",
|
||||||
"developer_tab_title": "Entwickler",
|
"developer_tab_title": "Entwickler",
|
||||||
"devices": {
|
"devices": {
|
||||||
"camera": "Kamera",
|
"camera": "Kamera",
|
||||||
"camera_numbered": "Kamera {{n}}",
|
"camera_numbered": "Kamera {{n}}",
|
||||||
|
"change_device_button": "Audiogerät wechseln",
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"default_named": "Standard<2> ({{name}} )</2>",
|
"default_named": "Standard<2> ({{name}} )</2>",
|
||||||
|
"handset": "Ohrhörer",
|
||||||
|
"loudspeaker": "Lautsprecher",
|
||||||
"microphone": "Mikrofon",
|
"microphone": "Mikrofon",
|
||||||
"microphone_numbered": "Mikrofon{{n}}",
|
"microphone_numbered": "Mikrofon{{n}}",
|
||||||
"speaker": "Lautsprecher",
|
"speaker": "Lautsprecher",
|
||||||
@@ -183,9 +218,9 @@
|
|||||||
"opt_in_description": "<0></0><1></1>Du kannst deine Zustimmung durch Abwählen dieses Kästchens zurückziehen. Falls du dich aktuell in einem Anruf befindest, wird diese Einstellung nach dem Ende des Anrufs wirksam.",
|
"opt_in_description": "<0></0><1></1>Du kannst deine Zustimmung durch Abwählen dieses Kästchens zurückziehen. Falls du dich aktuell in einem Anruf befindest, wird diese Einstellung nach dem Ende des Anrufs wirksam.",
|
||||||
"preferences_tab": {
|
"preferences_tab": {
|
||||||
"developer_mode_label": "Entwickler-Modus",
|
"developer_mode_label": "Entwickler-Modus",
|
||||||
"developer_mode_label_description": "Aktivieren Sie den Entwicklermodus und zeigen Sie die Registerkarte mit den Entwicklereinstellungen an.",
|
"developer_mode_label_description": "Aktiviere den Entwicklermodus und zeige Entwicklereinstellungen an.",
|
||||||
"introduction": "Hier können zusätzliche Optionen für individuelle Anforderungen eingestellt werden.",
|
"introduction": "Hier können zusätzliche Optionen für individuelle Anforderungen eingestellt werden.",
|
||||||
"reactions_play_sound_description": "Spielen Sie einen Soundeffekt ab, wenn jemand eine Reaktion auf einen Anruf sendet.",
|
"reactions_play_sound_description": "Spiele einen Soundeffekt ab, wenn jemand eine Reaktion auf einen Anruf sendet.",
|
||||||
"reactions_play_sound_label": "Reaktionstöne abspielen",
|
"reactions_play_sound_label": "Reaktionstöne abspielen",
|
||||||
"reactions_show_description": "Zeige eine Animation, wenn jemand eine Reaktion sendet.",
|
"reactions_show_description": "Zeige eine Animation, wenn jemand eine Reaktion sendet.",
|
||||||
"reactions_show_label": "Reaktionen anzeigen",
|
"reactions_show_label": "Reaktionen anzeigen",
|
||||||
@@ -208,12 +243,14 @@
|
|||||||
"version": "{{productName}} Version: {{version}}",
|
"version": "{{productName}} Version: {{version}}",
|
||||||
"video_tile": {
|
"video_tile": {
|
||||||
"always_show": "Immer anzeigen",
|
"always_show": "Immer anzeigen",
|
||||||
|
"call_ended": "Anruf beendet",
|
||||||
|
"calling": "Anruf…",
|
||||||
"camera_starting": "Video wird geladen...",
|
"camera_starting": "Video wird geladen...",
|
||||||
"change_fit_contain": "An Fenster anpassen",
|
|
||||||
"collapse": "Minimieren",
|
"collapse": "Minimieren",
|
||||||
"expand": "Erweitern",
|
"expand": "Erweitern",
|
||||||
"mute_for_me": "Für mich stumm schalten",
|
"mute_for_me": "Für mich stumm schalten",
|
||||||
"muted_for_me": "Für mich stumm geschaltet",
|
"muted_for_me": "Für mich stumm geschaltet",
|
||||||
|
"screen_share_volume": "Lautstärke der Bildschirmfreigabe",
|
||||||
"volume": "Lautstärke",
|
"volume": "Lautstärke",
|
||||||
"waiting_for_media": "Warten auf Medien..."
|
"waiting_for_media": "Warten auf Medien..."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,24 @@
|
|||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"close": "Κλείσιμο",
|
"close": "Κλείσιμο",
|
||||||
|
"copy_link": "Αντιγραφή συνδέσμου",
|
||||||
|
"edit": "Επεξεργασία",
|
||||||
"go": "Μετάβαση",
|
"go": "Μετάβαση",
|
||||||
|
"invite": "Πρόσκληση",
|
||||||
|
"lower_hand": "Κατεβάστε το χέρι",
|
||||||
"no": "Όχι",
|
"no": "Όχι",
|
||||||
|
"pick_reaction": "Επιλέξτε αντίδραση",
|
||||||
|
"raise_hand": "Σηκώστε το χέρι",
|
||||||
"register": "Εγγραφή",
|
"register": "Εγγραφή",
|
||||||
"remove": "Αφαίρεση",
|
"remove": "Αφαίρεση",
|
||||||
|
"show_less": "Εμφάνιση λιγότερων",
|
||||||
|
"show_more": "Εμφάνιση περισσότερων",
|
||||||
"sign_in": "Σύνδεση",
|
"sign_in": "Σύνδεση",
|
||||||
"sign_out": "Αποσύνδεση",
|
"sign_out": "Αποσύνδεση",
|
||||||
"submit": "Υποβολή"
|
"submit": "Υποβολή",
|
||||||
|
"upload_file": "Μεταφόρτωση αρχείου"
|
||||||
},
|
},
|
||||||
"analytics_notice": "Συμμετέχοντας σε αυτή τη δοκιμαστική έκδοση, συναινείτε στη συλλογή ανώνυμων δεδομένων, τα οποία χρησιμοποιούμε για τη βελτίωση του προϊόντος. Μπορείτε να βρείτε περισσότερες πληροφορίες σχετικά με το ποια δεδομένα καταγράφουμε στην <2>Πολιτική απορρήτου</2> και στην <5>Πολιτική cookies</5>.",
|
"analytics_notice": "Συμμετέχοντας σε αυτή τη δοκιμαστική έκδοση, συναινείτε στη συλλογή ανώνυμων δεδομένων, τα οποία χρησιμοποιούμε για τη βελτίωση του προϊόντος. Μπορείτε να βρείτε περισσότερες πληροφορίες σχετικά με το ποια δεδομένα καταγράφουμε στην <2>Πολιτική απορρήτου</2> και στην <6>Πολιτική cookies</6>.",
|
||||||
"call_ended_view": {
|
"call_ended_view": {
|
||||||
"create_account_button": "Δημιουργία λογαριασμού",
|
"create_account_button": "Δημιουργία λογαριασμού",
|
||||||
"create_account_prompt": "<0>Γιατί να μην ολοκληρώσετε με τη δημιουργία ενός κωδικού πρόσβασης για τη διατήρηση του λογαριασμού σας;</0><1>Θα μπορείτε να διατηρήσετε το όνομά σας και να ορίσετε ένα avatar για χρήση σε μελλοντικές κλήσεις.</1>",
|
"create_account_prompt": "<0>Γιατί να μην ολοκληρώσετε με τη δημιουργία ενός κωδικού πρόσβασης για τη διατήρηση του λογαριασμού σας;</0><1>Θα μπορείτε να διατηρήσετε το όνομά σας και να ορίσετε ένα avatar για χρήση σε μελλοντικές κλήσεις.</1>",
|
||||||
@@ -20,19 +29,44 @@
|
|||||||
"feedback_prompt": "<0>Θα θέλαμε να ακούσουμε τα σχόλιά σας ώστε να βελτιώσουμε την εμπειρία σας.</0>",
|
"feedback_prompt": "<0>Θα θέλαμε να ακούσουμε τα σχόλιά σας ώστε να βελτιώσουμε την εμπειρία σας.</0>",
|
||||||
"headline": "{{displayName}}, η κλήση σας τερματίστηκε.",
|
"headline": "{{displayName}}, η κλήση σας τερματίστηκε.",
|
||||||
"not_now_button": "Όχι τώρα, επιστροφή στην αρχική οθόνη",
|
"not_now_button": "Όχι τώρα, επιστροφή στην αρχική οθόνη",
|
||||||
|
"reconnect_button": "Επανασύνδεση",
|
||||||
"survey_prompt": "Πώς σας φάνηκε;"
|
"survey_prompt": "Πώς σας φάνηκε;"
|
||||||
},
|
},
|
||||||
|
"call_name": "Όνομα κλήσης",
|
||||||
"common": {
|
"common": {
|
||||||
|
"analytics": "Δεδομένα ανάλυσης",
|
||||||
"audio": "Ήχος",
|
"audio": "Ήχος",
|
||||||
|
"avatar": "Εικόνα Προφίλ",
|
||||||
|
"back": "Πίσω",
|
||||||
"display_name": "Εμφανιζόμενο όνομα",
|
"display_name": "Εμφανιζόμενο όνομα",
|
||||||
|
"encrypted": "Κρυπτογραφημένο",
|
||||||
"home": "Αρχική",
|
"home": "Αρχική",
|
||||||
"loading": "Φόρτωση…",
|
"loading": "Φόρτωση…",
|
||||||
|
"next": "Επόμενο",
|
||||||
|
"options": "Επιλογές",
|
||||||
"password": "Κωδικός",
|
"password": "Κωδικός",
|
||||||
|
"preferences": "Προτιμήσεις",
|
||||||
"profile": "Προφίλ",
|
"profile": "Προφίλ",
|
||||||
|
"reaction": "Αντίδραση",
|
||||||
|
"reactions": "Αντιδράσεις",
|
||||||
"settings": "Ρυθμίσεις",
|
"settings": "Ρυθμίσεις",
|
||||||
|
"unencrypted": "Μη κρυπτογραφημένο",
|
||||||
"username": "Όνομα χρήστη",
|
"username": "Όνομα χρήστη",
|
||||||
"video": "Βίντεο"
|
"video": "Βίντεο"
|
||||||
},
|
},
|
||||||
|
"developer_mode": {
|
||||||
|
"crypto_version": "Έκδοση κρυπτογράφησης: {{version}}",
|
||||||
|
"debug_tile_layout_label": "Διάταξη πλακιδίων εντοπισμού σφαλμάτων",
|
||||||
|
"device_id": "Αναγνωριστικό συσκευής: {{id}}",
|
||||||
|
"duplicate_tiles_label": "Αριθμός επιπλέον αντιγράφων πλακιδίων ανά συμμετέχοντα",
|
||||||
|
"environment_variables": "Μεταβλητές περιβάλλοντος",
|
||||||
|
"hostname": "Όνομα κεντρικού υπολογιστή: {{hostname}}",
|
||||||
|
"livekit_server_info": "Πληροφορίες διακομιστή LiveKit",
|
||||||
|
"livekit_sfu": "LiveKit SFU: {{url}}",
|
||||||
|
"matrix_id": "Αναγνωριστικό Matrix: {{id}}",
|
||||||
|
"show_connection_stats": "Εμφάνιση στατιστικών σύνδεσης",
|
||||||
|
"url_params": "Παράμετροι URL"
|
||||||
|
},
|
||||||
"header_label": "Element Κεντρική Οθόνη Κλήσεων",
|
"header_label": "Element Κεντρική Οθόνη Κλήσεων",
|
||||||
"join_existing_call_modal": {
|
"join_existing_call_modal": {
|
||||||
"join_button": "Ναι, συμμετοχή στην κλήση",
|
"join_button": "Ναι, συμμετοχή στην κλήση",
|
||||||
|
|||||||