mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-28 11:28:12 +08:00
Merge branch 'develop' of https://github.com/vector-im/element-web into t3chguy/react18/context
This commit is contained in:
commit
7bb4d91e5c
@ -87,6 +87,7 @@ module.exports = {
|
||||
// We don't need super strict typing in test utilities
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
)
|
||||
)
|
||||
steps:
|
||||
- uses: tibdex/backport@v2
|
||||
- uses: tibdex/backport@2e217641d82d02ba0603f46b1aeedefb258890ac # v2
|
||||
with:
|
||||
labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>"
|
||||
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
|
||||
|
15
.github/workflows/build_develop.yml
vendored
15
.github/workflows/build_develop.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
environment: develop
|
||||
env:
|
||||
R2_BUCKET: "element-web-develop"
|
||||
R2_URL: ${{ secrets.CF_R2_S3_API }}
|
||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||
R2_PUBLIC_URL: "https://element-web-develop.element.io"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -68,12 +68,17 @@ jobs:
|
||||
run: cp .github/cfp_headers _deploy/_headers
|
||||
|
||||
# Redirect requests for the develop tarball and the historical bundles to R2
|
||||
# We find the latest 100 bundle.css files and add their bundles to the redirects file
|
||||
# S3 has no sane way to get the age of a directory as they don't really exist
|
||||
- name: Populate _redirects
|
||||
run: |
|
||||
{
|
||||
echo "/develop.tar.gz $R2_PUBLIC_URL/develop.tar.gz 301"
|
||||
for bundle in $(aws s3 ls s3://$R2_BUCKET/bundles/ --endpoint-url $R2_URL --region=auto | awk '{print $2}'); do
|
||||
echo "/bundles/${bundle}* $R2_PUBLIC_URL/bundles/${bundle}:splat 301"
|
||||
RESULTS=$(aws s3api --region auto --endpoint-url $R2_URL list-objects-v2 --bucket $R2_BUCKET \
|
||||
--query "sort_by(Contents[?ends_with(Key, '/bundle.css')], &LastModified)[-100:].Key"
|
||||
--prefix "bundles/" | jq -r '.[]' | grep -oE '[^\"].*\/\s*')
|
||||
for bundle in "$RESULTS"; do
|
||||
echo "/${path}* $R2_PUBLIC_URL/${path}:splat 301"
|
||||
done
|
||||
} | tee _deploy/_redirects
|
||||
env:
|
||||
@ -81,7 +86,7 @@ jobs:
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
|
||||
|
||||
- name: Wait for other steps to succeed
|
||||
uses: lewagon/wait-on-check-action@v1.3.1
|
||||
uses: t3chguy/wait-on-check-action@05861d3a448898eb33dfce34153bd1ecb9422fb9 # fork
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
running-workflow-name: "Build & Deploy develop.element.io"
|
||||
@ -102,7 +107,7 @@ jobs:
|
||||
|
||||
- name: Deploy to Cloudflare Pages
|
||||
id: cfp
|
||||
uses: cloudflare/pages-action@1
|
||||
uses: cloudflare/pages-action@61eafe73baad0195ab582cb447b2c6e15a0df9ce # v1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
|
||||
accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }}
|
||||
|
14
.github/workflows/dockerhub.yaml
vendored
14
.github/workflows/dockerhub.yaml
vendored
@ -6,7 +6,7 @@ on:
|
||||
schedule:
|
||||
# This job can take a while, and we have usage limits, so just publish develop only twice a day
|
||||
- cron: "0 7/12 * * *"
|
||||
concurrency: ${{ github.ref_name }}
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref_name }}
|
||||
jobs:
|
||||
buildx:
|
||||
name: Docker Buildx
|
||||
@ -18,22 +18,22 @@ jobs:
|
||||
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@c4ee3adeed93b1fa6a762f209fb01608c1a22f1e # v4
|
||||
with:
|
||||
images: |
|
||||
vectorim/element-web
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
@ -53,7 +53,7 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Update repo description
|
||||
uses: peter-evans/dockerhub-description@v3
|
||||
uses: peter-evans/dockerhub-description@202973a37c8a723405c0c5f0a71b6d99db470dae # v3
|
||||
continue-on-error: true
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
|
1
.github/workflows/pull_request.yaml
vendored
1
.github/workflows/pull_request.yaml
vendored
@ -2,7 +2,6 @@ name: Pull Request
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, labeled, unlabeled, synchronize]
|
||||
concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }}
|
||||
jobs:
|
||||
action:
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
||||
|
6
.github/workflows/static_analysis.yaml
vendored
6
.github/workflows/static_analysis.yaml
vendored
@ -41,13 +41,13 @@ jobs:
|
||||
|
||||
- name: Get diff lines
|
||||
id: diff
|
||||
uses: Equip-Collaboration/diff-line-numbers@v1.0.0
|
||||
uses: Equip-Collaboration/diff-line-numbers@df70b4b83e05105c15f20dc6cc61f1463411b2a6 # v1.0.0
|
||||
with:
|
||||
include: '["\\.tsx?$"]'
|
||||
|
||||
- name: Detecting files changed
|
||||
id: files
|
||||
uses: futuratrepadeira/changed-files@v4.0.0
|
||||
uses: futuratrepadeira/changed-files@0239328a3a6268aad16af7c3e4efc78e32d6c0f0 # v4.0.1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pattern: '^.*\.tsx?$'
|
||||
@ -80,7 +80,7 @@ jobs:
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install --pure-lockfile"
|
||||
run: "yarn install --frozen-lockfile"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn run lint:js"
|
||||
|
4
.github/workflows/tests.yaml
vendored
4
.github/workflows/tests.yaml
vendored
@ -27,10 +27,10 @@ jobs:
|
||||
|
||||
- name: Get number of CPU cores
|
||||
id: cpu-cores
|
||||
uses: SimenB/github-actions-cpu-cores@v1
|
||||
uses: SimenB/github-actions-cpu-cores@410541432439795d30db6501fb1d8178eb41e502 # v1
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: "yarn coverage --ci --reporters github-actions --max-workers ${{ steps.cpu-cores.outputs.count }}"
|
||||
run: "yarn coverage --ci --max-workers ${{ steps.cpu-cores.outputs.count }}"
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
19
.github/workflows/triage-assigned.yml
vendored
19
.github/workflows/triage-assigned.yml
vendored
@ -12,20 +12,7 @@ jobs:
|
||||
contains(github.event.issue.assignees.*.login, 'andybalaam') ||
|
||||
contains(github.event.issue.assignees.*.login, 'justjanne')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AKjJS"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/67
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
243
.github/workflows/triage-labelled.yml
vendored
243
.github/workflows/triage-labelled.yml
vendored
@ -72,23 +72,10 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
||||
contains(github.event.issue.labels.*.name, 'A11y'))
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/18
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
add_product_issues:
|
||||
name: X-Needs-Product to product project board
|
||||
@ -96,23 +83,10 @@ jobs:
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/28
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
Search_issues_to_board:
|
||||
name: Search issues to project board
|
||||
@ -120,114 +94,10 @@ jobs:
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4ADtaO"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_voice-message_issues:
|
||||
name: A-Voice Messages to voice message board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Voice Messages')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc2KCw"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_message_bubbles_issues:
|
||||
name: A-Message-Bubbles to Message bubbles board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc3m-g"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_ftue_issues:
|
||||
name: Z-FTUE issues to the FTUE project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Z-FTUE')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AAqVx"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_WTF_issues:
|
||||
name: Z-WTF issues to the WTF project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Z-WTF')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AArk0"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/48
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
ps_features1:
|
||||
name: Add labelled issues to PS features team 1
|
||||
@ -240,23 +110,10 @@ jobs:
|
||||
(contains(github.event.issue.labels.*.name, 'A-Session-Mgmt') &&
|
||||
contains(github.event.issue.labels.*.name, 'A-User-Settings'))
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AHJKF"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/56
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
ps_features2:
|
||||
name: Add labelled issues to PS features team 2
|
||||
@ -265,23 +122,10 @@ jobs:
|
||||
contains(github.event.issue.labels.*.name, 'A-DM-Start') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Broadcast')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AHJKd"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/58
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
ps_features3:
|
||||
name: Add labelled issues to PS features team 3
|
||||
@ -289,23 +133,10 @@ jobs:
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AHJKW"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/57
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
voip:
|
||||
name: Add labelled issues to VoIP project board
|
||||
@ -313,23 +144,10 @@ jobs:
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Team: VoIP')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4ABMIk"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/41
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
verticals_feature:
|
||||
name: Add labelled issues to Verticals Feature project
|
||||
@ -337,20 +155,7 @@ jobs:
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
- uses: actions/add-to-project@main
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AHJKW"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
project-url: https://github.com/orgs/vector-im/projects/57
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
@ -43,11 +43,11 @@ jobs:
|
||||
|
||||
# If either a reviewer matches a team member, or a team matches our team, say "true"
|
||||
if [ $(join /tmp/team_members.txt /tmp/reviewers.txt | wc -l) != 0 ]; then
|
||||
echo "::set-output name=match::true"
|
||||
echo "match=true" >> $GITHUB_OUTPUT
|
||||
elif [ $(join /tmp/team.txt /tmp/team_reviewers.txt | wc -l) != 0 ]; then
|
||||
echo "::set-output name=match::true"
|
||||
echo "match=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "::set-output name=match::false"
|
||||
echo "match=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
TEAM: "design"
|
||||
@ -110,11 +110,11 @@ jobs:
|
||||
|
||||
# If either a reviewer matches a team member, or a team matches our team, say "true"
|
||||
if [ $(join /tmp/team_members.txt /tmp/reviewers.txt | wc -l) != 0 ]; then
|
||||
echo "::set-output name=match::true"
|
||||
echo "match=true" >> $GITHUB_OUTPUT
|
||||
elif [ $(join /tmp/team.txt /tmp/team_reviewers.txt | wc -l) != 0 ]; then
|
||||
echo "::set-output name=match::true"
|
||||
echo "match=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "::set-output name=match::false"
|
||||
echo "match=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
TEAM: "product"
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
...require("matrix-react-sdk/.stylelintrc.js"),
|
||||
extends: ["stylelint-config-standard", "stylelint-config-prettier"],
|
||||
extends: ["stylelint-config-standard"],
|
||||
};
|
||||
|
186
CHANGELOG.md
186
CHANGELOG.md
@ -1,3 +1,189 @@
|
||||
Changes in [1.11.30](https://github.com/vector-im/element-web/releases/tag/v1.11.30) (2023-04-25)
|
||||
=================================================================================================
|
||||
|
||||
## 🔒 Security
|
||||
* Fixes for [CVE-2023-30609](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE-2023-30609) / GHSA-xv83-x443-7rmw
|
||||
|
||||
## ✨ Features
|
||||
* Pick sensible default option for phone country dropdown ([\#10627](https://github.com/matrix-org/matrix-react-sdk/pull/10627)). Fixes #3528.
|
||||
* Relate field validation tooltip via aria-describedby ([\#10522](https://github.com/matrix-org/matrix-react-sdk/pull/10522)). Fixes #24963.
|
||||
* Handle more completion types in rte autocomplete ([\#10560](https://github.com/matrix-org/matrix-react-sdk/pull/10560)). Contributed by @alunturner.
|
||||
* Show a tile for an unloaded predecessor room if it has via_servers ([\#10483](https://github.com/matrix-org/matrix-react-sdk/pull/10483)). Contributed by @andybalaam.
|
||||
* Exclude message timestamps from aria live region ([\#10584](https://github.com/matrix-org/matrix-react-sdk/pull/10584)). Fixes #5696.
|
||||
* Make composer format bar an aria toolbar ([\#10583](https://github.com/matrix-org/matrix-react-sdk/pull/10583)). Fixes #11283.
|
||||
* Improve accessibility of font slider ([\#10473](https://github.com/matrix-org/matrix-react-sdk/pull/10473)). Fixes #20168 and #24962.
|
||||
* fix file size display from kB to KB ([\#10561](https://github.com/matrix-org/matrix-react-sdk/pull/10561)). Fixes #24866. Contributed by @NSV1991.
|
||||
* Handle /me in rte ([\#10558](https://github.com/matrix-org/matrix-react-sdk/pull/10558)). Contributed by @alunturner.
|
||||
* bind html with switch for manage extension setting option ([\#10553](https://github.com/matrix-org/matrix-react-sdk/pull/10553)). Contributed by @NSV1991.
|
||||
* Handle command completions in RTE ([\#10521](https://github.com/matrix-org/matrix-react-sdk/pull/10521)). Contributed by @alunturner.
|
||||
* Add room and user avatars to rte ([\#10497](https://github.com/matrix-org/matrix-react-sdk/pull/10497)). Contributed by @alunturner.
|
||||
* Support for MSC3882 revision 1 ([\#10443](https://github.com/matrix-org/matrix-react-sdk/pull/10443)). Contributed by @hughns.
|
||||
* Check profiles before starting a DM ([\#10472](https://github.com/matrix-org/matrix-react-sdk/pull/10472)). Fixes #24830.
|
||||
* Quick settings: Change the copy / labels on the options ([\#10427](https://github.com/matrix-org/matrix-react-sdk/pull/10427)). Fixes #24522. Contributed by @justjanne.
|
||||
* Update rte autocomplete styling ([\#10503](https://github.com/matrix-org/matrix-react-sdk/pull/10503)). Contributed by @alunturner.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix create subspace dialog not working ([\#10652](https://github.com/matrix-org/matrix-react-sdk/pull/10652)). Fixes vector-im/element-web#24882
|
||||
* Fix multiple accessibility defects identified by AXE ([\#10606](https://github.com/matrix-org/matrix-react-sdk/pull/10606)).
|
||||
* Fix view source from edit history dialog always showing latest event ([\#10626](https://github.com/matrix-org/matrix-react-sdk/pull/10626)). Fixes #21859.
|
||||
* #21451 Fix WebGL disabled error message ([\#10589](https://github.com/matrix-org/matrix-react-sdk/pull/10589)). Contributed by @rashmitpankhania.
|
||||
* Properly translate errors in `AddThreepid.ts` so they show up translated to the user but not in our logs ([\#10432](https://github.com/matrix-org/matrix-react-sdk/pull/10432)). Contributed by @MadLittleMods.
|
||||
* Fix overflow on auth pages ([\#10605](https://github.com/matrix-org/matrix-react-sdk/pull/10605)). Fixes #19548.
|
||||
* Fix incorrect avatar background colour when using a custom theme ([\#10598](https://github.com/matrix-org/matrix-react-sdk/pull/10598)). Contributed by @jdauphant.
|
||||
* Remove dependency on `org.matrix.e2e_cross_signing` unstable feature ([\#10593](https://github.com/matrix-org/matrix-react-sdk/pull/10593)).
|
||||
* Update setting description to match reality ([\#10600](https://github.com/matrix-org/matrix-react-sdk/pull/10600)). Fixes #25106.
|
||||
* Fix no identity server in help & about settings ([\#10563](https://github.com/matrix-org/matrix-react-sdk/pull/10563)). Fixes #25077.
|
||||
* Fix: Images no longer reserve their space in the timeline correctly ([\#10571](https://github.com/matrix-org/matrix-react-sdk/pull/10571)). Fixes #25082. Contributed by @kerryarchibald.
|
||||
* Fix issues with inhibited accessible focus outlines ([\#10579](https://github.com/matrix-org/matrix-react-sdk/pull/10579)). Fixes #19742.
|
||||
* Fix read receipts falling from sky ([\#10576](https://github.com/matrix-org/matrix-react-sdk/pull/10576)). Fixes #25081.
|
||||
* Fix avatar text issue in rte ([\#10559](https://github.com/matrix-org/matrix-react-sdk/pull/10559)). Contributed by @alunturner.
|
||||
* fix resizer only work with left mouse click ([\#10546](https://github.com/matrix-org/matrix-react-sdk/pull/10546)). Contributed by @NSV1991.
|
||||
* Fix send two join requests when joining a room from spotlight search ([\#10534](https://github.com/matrix-org/matrix-react-sdk/pull/10534)). Fixes #25054.
|
||||
* Highlight event when any version triggered a highlight ([\#10502](https://github.com/matrix-org/matrix-react-sdk/pull/10502)). Fixes #24923 and #24970. Contributed by @kerryarchibald.
|
||||
* Fix spacing of headings of integration manager on General settings tab ([\#10232](https://github.com/matrix-org/matrix-react-sdk/pull/10232)). Fixes #24085. Contributed by @luixxiul.
|
||||
|
||||
Changes in [1.11.29](https://github.com/vector-im/element-web/releases/tag/v1.11.29) (2023-04-11)
|
||||
=================================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Allow desktop app to expose recent rooms in UI integrations ([\#16940](https://github.com/vector-im/element-web/pull/16940)).
|
||||
* Add API params to mute audio and/or video in Jitsi calls by default ([\#24820](https://github.com/vector-im/element-web/pull/24820)). Contributed by @dhenneke.
|
||||
* Style mentions as pills in rich text editor ([\#10448](https://github.com/matrix-org/matrix-react-sdk/pull/10448)). Contributed by @alunturner.
|
||||
* Show room create icon if "UIComponent.roomCreation" is enabled ([\#10364](https://github.com/matrix-org/matrix-react-sdk/pull/10364)). Contributed by @maheichyk.
|
||||
* Mentions as links rte ([\#10463](https://github.com/matrix-org/matrix-react-sdk/pull/10463)). Contributed by @alunturner.
|
||||
* Better error handling in jump to date ([\#10405](https://github.com/matrix-org/matrix-react-sdk/pull/10405)). Contributed by @MadLittleMods.
|
||||
* Show "Invite" menu option if "UIComponent.sendInvites" is enabled. ([\#10363](https://github.com/matrix-org/matrix-react-sdk/pull/10363)). Contributed by @maheichyk.
|
||||
* Added `UserProfilesStore`, `LruCache` and user permalink profile caching ([\#10425](https://github.com/matrix-org/matrix-react-sdk/pull/10425)). Fixes #10559.
|
||||
* Mentions as links rte ([\#10422](https://github.com/matrix-org/matrix-react-sdk/pull/10422)). Contributed by @alunturner.
|
||||
* Implement MSC3952: intentional mentions ([\#9983](https://github.com/matrix-org/matrix-react-sdk/pull/9983)).
|
||||
* Implement MSC3973: Search users in the user directory with the Widget API ([\#10269](https://github.com/matrix-org/matrix-react-sdk/pull/10269)). Contributed by @dhenneke.
|
||||
* Permalinks to message are now displayed as pills ([\#10392](https://github.com/matrix-org/matrix-react-sdk/pull/10392)). Fixes #24751 and #24706.
|
||||
* Show search,dial,explore in filterContainer if "UIComponent.filterContainer" is enabled ([\#10381](https://github.com/matrix-org/matrix-react-sdk/pull/10381)). Contributed by @maheichyk.
|
||||
* Increase space panel collapse clickable area ([\#6084](https://github.com/matrix-org/matrix-react-sdk/pull/6084)). Fixes #17379. Contributed by @jaiwanth-v.
|
||||
* Add fallback for replies to Polls ([\#10380](https://github.com/matrix-org/matrix-react-sdk/pull/10380)). Fixes #24197. Contributed by @kerryarchibald.
|
||||
* Permalinks to rooms and users are now pillified ([\#10388](https://github.com/matrix-org/matrix-react-sdk/pull/10388)). Fixes #24825.
|
||||
* Poll history - access poll history from room settings ([\#10356](https://github.com/matrix-org/matrix-react-sdk/pull/10356)). Contributed by @kerryarchibald.
|
||||
* Add API params to mute audio and/or video in Jitsi calls by default ([\#10376](https://github.com/matrix-org/matrix-react-sdk/pull/10376)). Contributed by @dhenneke.
|
||||
* Notifications: inline error message on notifications saving error ([\#10288](https://github.com/matrix-org/matrix-react-sdk/pull/10288)). Contributed by @kerryarchibald.
|
||||
* Support dynamic room predecessor in SpaceProvider ([\#10348](https://github.com/matrix-org/matrix-react-sdk/pull/10348)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors for RoomProvider ([\#10346](https://github.com/matrix-org/matrix-react-sdk/pull/10346)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in OwnBeaconStore ([\#10339](https://github.com/matrix-org/matrix-react-sdk/pull/10339)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in ForwardDialog ([\#10344](https://github.com/matrix-org/matrix-react-sdk/pull/10344)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in SpaceHierarchy ([\#10341](https://github.com/matrix-org/matrix-react-sdk/pull/10341)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in AddExistingToSpaceDialog ([\#10342](https://github.com/matrix-org/matrix-react-sdk/pull/10342)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in leave-behaviour ([\#10340](https://github.com/matrix-org/matrix-react-sdk/pull/10340)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in StopGapWidgetDriver ([\#10338](https://github.com/matrix-org/matrix-react-sdk/pull/10338)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in WidgetLayoutStore ([\#10326](https://github.com/matrix-org/matrix-react-sdk/pull/10326)). Contributed by @andybalaam.
|
||||
* Support dynamic room predecessors in SpaceStore ([\#10332](https://github.com/matrix-org/matrix-react-sdk/pull/10332)). Contributed by @andybalaam.
|
||||
* Sync polls push rules on changes to account_data ([\#10287](https://github.com/matrix-org/matrix-react-sdk/pull/10287)). Contributed by @kerryarchibald.
|
||||
* Support dynamic room predecessors in BreadcrumbsStore ([\#10295](https://github.com/matrix-org/matrix-react-sdk/pull/10295)). Contributed by @andybalaam.
|
||||
* Improved a11y for Field feedback and Secure Phrase input ([\#10320](https://github.com/matrix-org/matrix-react-sdk/pull/10320)). Contributed by @Sebbones.
|
||||
* Support dynamic room predecessors in RoomNotificationStateStore ([\#10297](https://github.com/matrix-org/matrix-react-sdk/pull/10297)). Contributed by @andybalaam.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Use a newly generated access_token while joining Jitsi ([\#24646](https://github.com/vector-im/element-web/pull/24646)). Fixes #24687. Contributed by @emrahcom.
|
||||
* Fix cloudflare action pointing at commit hash instead of tag ([\#24777](https://github.com/vector-im/element-web/pull/24777)). Contributed by @justjanne.
|
||||
* Allow editing with RTE to overflow for autocomplete visibility ([\#10499](https://github.com/matrix-org/matrix-react-sdk/pull/10499)). Contributed by @alunturner.
|
||||
* Added auto focus to Github URL on opening of debug logs modal ([\#10479](https://github.com/matrix-org/matrix-react-sdk/pull/10479)). Contributed by @ShivamSpm.
|
||||
* Fix detection of encryption for all users in a room ([\#10487](https://github.com/matrix-org/matrix-react-sdk/pull/10487)). Fixes #24995.
|
||||
* Properly generate mentions when editing a reply with MSC3952 ([\#10486](https://github.com/matrix-org/matrix-react-sdk/pull/10486)). Fixes #24924. Contributed by @kerryarchibald.
|
||||
* Improve performance of rendering a room with many hidden events ([\#10131](https://github.com/matrix-org/matrix-react-sdk/pull/10131)). Contributed by @andybalaam.
|
||||
* Prevent future date selection in jump to date ([\#10419](https://github.com/matrix-org/matrix-react-sdk/pull/10419)). Fixes #20800. Contributed by @MadLittleMods.
|
||||
* Add aria labels to message search bar to improve accessibility ([\#10476](https://github.com/matrix-org/matrix-react-sdk/pull/10476)). Fixes #24921.
|
||||
* Fix decryption failure bar covering the timeline ([\#10360](https://github.com/matrix-org/matrix-react-sdk/pull/10360)). Fixes #24780 #24074 and #24183. Contributed by @luixxiul.
|
||||
* Improve profile picture settings accessibility ([\#10470](https://github.com/matrix-org/matrix-react-sdk/pull/10470)). Fixes #24919.
|
||||
* Handle group call redaction ([\#10465](https://github.com/matrix-org/matrix-react-sdk/pull/10465)).
|
||||
* Display relative timestamp for threads on the same calendar day ([\#10399](https://github.com/matrix-org/matrix-react-sdk/pull/10399)). Fixes #24841. Contributed by @kerryarchibald.
|
||||
* Fix timeline list and paragraph display issues ([\#10424](https://github.com/matrix-org/matrix-react-sdk/pull/10424)). Fixes #24602. Contributed by @alunturner.
|
||||
* Use unique keys for voice broadcast pips ([\#10457](https://github.com/matrix-org/matrix-react-sdk/pull/10457)). Fixes #24959.
|
||||
* Fix "show read receipts sent by other users" not applying to threads ([\#10445](https://github.com/matrix-org/matrix-react-sdk/pull/10445)). Fixes #24910.
|
||||
* Fix joining public rooms without aliases in search dialog ([\#10437](https://github.com/matrix-org/matrix-react-sdk/pull/10437)). Fixes #23937.
|
||||
* Add input validation for `m.direct` in `DMRoomMap` ([\#10436](https://github.com/matrix-org/matrix-react-sdk/pull/10436)). Fixes #24909.
|
||||
* Reduce height reserved for "collapse" button's line on IRC layout ([\#10211](https://github.com/matrix-org/matrix-react-sdk/pull/10211)). Fixes #24605. Contributed by @luixxiul.
|
||||
* Fix `creatorUserId is required` error when opening sticker picker ([\#10423](https://github.com/matrix-org/matrix-react-sdk/pull/10423)).
|
||||
* Fix block/inline Element descendants error noise in `NewRoomIntro.tsx` ([\#10412](https://github.com/matrix-org/matrix-react-sdk/pull/10412)). Contributed by @MadLittleMods.
|
||||
* Fix profile resizer to make first character of a line selectable in IRC layout ([\#10396](https://github.com/matrix-org/matrix-react-sdk/pull/10396)). Fixes #14764. Contributed by @luixxiul.
|
||||
* Ensure space between wrapped lines of room name on IRC layout ([\#10188](https://github.com/matrix-org/matrix-react-sdk/pull/10188)). Fixes #24742. Contributed by @luixxiul.
|
||||
* Remove unreadable alt attribute from the room status bar warning icon (nonsense to screenreaders) ([\#10402](https://github.com/matrix-org/matrix-react-sdk/pull/10402)). Contributed by @MadLittleMods.
|
||||
* Fix big date separators when jump to date is enabled ([\#10404](https://github.com/matrix-org/matrix-react-sdk/pull/10404)). Fixes #22969. Contributed by @MadLittleMods.
|
||||
* Fixes user authentication when registering via the module API ([\#10257](https://github.com/matrix-org/matrix-react-sdk/pull/10257)). Contributed by @maheichyk.
|
||||
* Handle more edge cases in Space Hierarchy ([\#10280](https://github.com/matrix-org/matrix-react-sdk/pull/10280)). Contributed by @justjanne.
|
||||
* Further improve performance with lots of hidden events ([\#10353](https://github.com/matrix-org/matrix-react-sdk/pull/10353)). Fixes #24480. Contributed by @andybalaam.
|
||||
* Respect user cancelling upload flow by dismissing spinner ([\#10373](https://github.com/matrix-org/matrix-react-sdk/pull/10373)). Fixes #24667.
|
||||
* When starting a DM, the end-to-end encryption status icon does now only appear if the DM can be encrypted ([\#10394](https://github.com/matrix-org/matrix-react-sdk/pull/10394)). Fixes #24397.
|
||||
* Fix `[object Object]` in feedback metadata ([\#10390](https://github.com/matrix-org/matrix-react-sdk/pull/10390)).
|
||||
* Fix pinned messages card saying nothing pinned while loading ([\#10385](https://github.com/matrix-org/matrix-react-sdk/pull/10385)). Fixes #24615.
|
||||
* Fix import e2e key dialog staying disabled after paste ([\#10375](https://github.com/matrix-org/matrix-react-sdk/pull/10375)). Fixes #24818.
|
||||
* Show all labs even if incompatible, with appropriate tooltip explaining requirements ([\#10369](https://github.com/matrix-org/matrix-react-sdk/pull/10369)). Fixes #24813.
|
||||
* Fix UIFeature.Registration not applying to all paths ([\#10371](https://github.com/matrix-org/matrix-react-sdk/pull/10371)). Fixes #24814.
|
||||
* Clicking on a user pill does now only open the profile in the right panel and no longer navigates to the home view. ([\#10359](https://github.com/matrix-org/matrix-react-sdk/pull/10359)). Fixes #24797.
|
||||
* Fix start DM with pending third party invite ([\#10347](https://github.com/matrix-org/matrix-react-sdk/pull/10347)). Fixes #24781.
|
||||
* Fix long display name overflowing reply tile on IRC layout ([\#10343](https://github.com/matrix-org/matrix-react-sdk/pull/10343)). Fixes #24738. Contributed by @luixxiul.
|
||||
* Display redacted body on ThreadView in the same way as normal messages ([\#9016](https://github.com/matrix-org/matrix-react-sdk/pull/9016)). Fixes #24729. Contributed by @luixxiul.
|
||||
* Handle more edge cases in ACL updates ([\#10279](https://github.com/matrix-org/matrix-react-sdk/pull/10279)). Contributed by @justjanne.
|
||||
* Allow parsing png files to fail if thumbnailing is successful ([\#10308](https://github.com/matrix-org/matrix-react-sdk/pull/10308)).
|
||||
|
||||
Changes in [1.11.28](https://github.com/vector-im/element-web/releases/tag/v1.11.28) (2023-03-31)
|
||||
=================================================================================================
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* (No changes, version bumped to sync with element-desktop.)
|
||||
|
||||
Changes in [1.11.27](https://github.com/vector-im/element-web/releases/tag/v1.11.27) (2023-03-31)
|
||||
=================================================================================================
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix detection of encryption for all users in a room ([\#10487](https://github.com/matrix-org/matrix-react-sdk/pull/10487)). Fixes #24995.
|
||||
|
||||
Changes in [1.11.26](https://github.com/vector-im/element-web/releases/tag/v1.11.26) (2023-03-28)
|
||||
=================================================================================================
|
||||
|
||||
## 🔒 Security
|
||||
* Fixes for [CVE-2023-28427](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE-2023-28427) / GHSA-mwq8-fjpf-c2gr
|
||||
* Fixes for [CVE-2023-28103](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE-2023-28103) / GHSA-6g43-88cp-w5gv
|
||||
|
||||
Changes in [1.11.25](https://github.com/vector-im/element-web/releases/tag/v1.11.25) (2023-03-15)
|
||||
=================================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Remove experimental PWA support for Firefox and Safari ([\#24630](https://github.com/vector-im/element-web/pull/24630)).
|
||||
* Only allow to start a DM with one email if encryption by default is enabled ([\#10253](https://github.com/matrix-org/matrix-react-sdk/pull/10253)). Fixes #23133.
|
||||
* DM rooms are now encrypted if encryption by default is enabled and only inviting a single email address. Any action in the result DM room will be blocked until the other has joined. ([\#10229](https://github.com/matrix-org/matrix-react-sdk/pull/10229)).
|
||||
* Reduce bottom margin of ReplyChain on compact modern layout ([\#8972](https://github.com/matrix-org/matrix-react-sdk/pull/8972)). Fixes #22748. Contributed by @luixxiul.
|
||||
* Support for v2 of MSC3903 ([\#10165](https://github.com/matrix-org/matrix-react-sdk/pull/10165)). Contributed by @hughns.
|
||||
* When starting a DM, existing rooms with pending third-party invites will be reused. ([\#10256](https://github.com/matrix-org/matrix-react-sdk/pull/10256)). Fixes #23139.
|
||||
* Polls push rules: synchronise poll rules with message rules ([\#10263](https://github.com/matrix-org/matrix-react-sdk/pull/10263)). Contributed by @kerryarchibald.
|
||||
* New verification request toast button labels ([\#10259](https://github.com/matrix-org/matrix-react-sdk/pull/10259)).
|
||||
* Remove padding around integration manager iframe ([\#10148](https://github.com/matrix-org/matrix-react-sdk/pull/10148)).
|
||||
* Fix block code styling in rich text editor ([\#10246](https://github.com/matrix-org/matrix-react-sdk/pull/10246)). Contributed by @alunturner.
|
||||
* Poll history: fetch more poll history ([\#10235](https://github.com/matrix-org/matrix-react-sdk/pull/10235)). Contributed by @kerryarchibald.
|
||||
* Sort short/exact emoji matches before longer incomplete matches ([\#10212](https://github.com/matrix-org/matrix-react-sdk/pull/10212)). Fixes #23210. Contributed by @grimhilt.
|
||||
* Poll history: detail screen ([\#10172](https://github.com/matrix-org/matrix-react-sdk/pull/10172)). Contributed by @kerryarchibald.
|
||||
* Provide a more detailed error message than "No known servers" ([\#6048](https://github.com/matrix-org/matrix-react-sdk/pull/6048)). Fixes #13247. Contributed by @aaronraimist.
|
||||
* Say when a call was answered from a different device ([\#10224](https://github.com/matrix-org/matrix-react-sdk/pull/10224)).
|
||||
* Widget permissions customizations using module api ([\#10121](https://github.com/matrix-org/matrix-react-sdk/pull/10121)). Contributed by @maheichyk.
|
||||
* Fix copy button icon overlapping with copyable text ([\#10227](https://github.com/matrix-org/matrix-react-sdk/pull/10227)). Contributed by @Adesh-Pandey.
|
||||
* Support joining non-peekable rooms via the module API ([\#10154](https://github.com/matrix-org/matrix-react-sdk/pull/10154)). Contributed by @maheichyk.
|
||||
* The "new login" toast does now display the same device information as in the settings. "No" does now open the device settings. "Yes, it was me" dismisses the toast. ([\#10200](https://github.com/matrix-org/matrix-react-sdk/pull/10200)).
|
||||
* Do not prompt for a password when doing a „reset all“ after login ([\#10208](https://github.com/matrix-org/matrix-react-sdk/pull/10208)).
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix incorrect copy in space creation flow ([\#10296](https://github.com/matrix-org/matrix-react-sdk/pull/10296)). Fixes #24741.
|
||||
* Fix space settings dialog having rogue title tooltip ([\#10293](https://github.com/matrix-org/matrix-react-sdk/pull/10293)). Fixes #24740.
|
||||
* Show spinner when starting a DM from the user profile (right panel) ([\#10290](https://github.com/matrix-org/matrix-react-sdk/pull/10290)).
|
||||
* Reduce height of toggle on expanded view source event ([\#10283](https://github.com/matrix-org/matrix-react-sdk/pull/10283)). Fixes #22873. Contributed by @luixxiul.
|
||||
* Pillify http and non-prefixed matrix.to links ([\#10277](https://github.com/matrix-org/matrix-react-sdk/pull/10277)). Fixes #20844.
|
||||
* Fix some features not being configurable via `features` ([\#10276](https://github.com/matrix-org/matrix-react-sdk/pull/10276)).
|
||||
* Fix starting a DM from the right panel in some cases ([\#10278](https://github.com/matrix-org/matrix-react-sdk/pull/10278)). Fixes #24722.
|
||||
* Align info EventTile and normal EventTile on IRC layout ([\#10197](https://github.com/matrix-org/matrix-react-sdk/pull/10197)). Fixes #22782. Contributed by @luixxiul.
|
||||
* Fix blowout of waveform of the voice message player on narrow UI ([\#8861](https://github.com/matrix-org/matrix-react-sdk/pull/8861)). Fixes #22604. Contributed by @luixxiul.
|
||||
* Fix the hidden view source toggle on IRC layout ([\#10266](https://github.com/matrix-org/matrix-react-sdk/pull/10266)). Fixes #22872. Contributed by @luixxiul.
|
||||
* Fix buttons on the room header being compressed due to long room name ([\#10155](https://github.com/matrix-org/matrix-react-sdk/pull/10155)). Contributed by @luixxiul.
|
||||
* Use the room avatar as a placeholder in calls ([\#10231](https://github.com/matrix-org/matrix-react-sdk/pull/10231)).
|
||||
* Fix calls showing as 'connecting' after hangup ([\#10223](https://github.com/matrix-org/matrix-react-sdk/pull/10223)).
|
||||
* Prevent multiple Jitsi calls started at the same time ([\#10183](https://github.com/matrix-org/matrix-react-sdk/pull/10183)). Fixes #23009.
|
||||
* Make localization keys compatible with agglutinative and/or SOV type languages ([\#10159](https://github.com/matrix-org/matrix-react-sdk/pull/10159)). Contributed by @luixxiul.
|
||||
|
||||
Changes in [1.11.24](https://github.com/vector-im/element-web/releases/tag/v1.11.24) (2023-02-28)
|
||||
=================================================================================================
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:16-buster as builder
|
||||
FROM --platform=$BUILDPLATFORM node:20-bullseye as builder
|
||||
|
||||
# Support custom branches of the react-sdk and js-sdk. This also helps us build
|
||||
# images of element-web develop.
|
||||
@ -23,7 +23,7 @@ RUN dos2unix /src/scripts/docker-package.sh && bash /src/scripts/docker-package.
|
||||
RUN cp /src/config.sample.json /src/webapp/config.json
|
||||
|
||||
# App
|
||||
FROM nginx:alpine
|
||||
FROM nginx:alpine-slim
|
||||
|
||||
COPY --from=builder /src/webapp /app
|
||||
|
||||
|
@ -18,7 +18,8 @@ Element has several tiers of support for different environments:
|
||||
|
||||
- Supported
|
||||
- Definition: Issues **actively triaged**, regressions **block** the release
|
||||
- Last 2 major versions of Chrome, Firefox, Safari, and Edge on desktop OSes
|
||||
- Last 2 major versions of Chrome, Firefox, and Edge on desktop OSes
|
||||
- Last 2 versions of Safari
|
||||
- Latest release of official Element Desktop app on desktop OSes
|
||||
- Desktop OSes means macOS, Windows, and Linux versions for desktop devices
|
||||
that are actively supported by the OS vendor and receive security updates
|
||||
@ -85,7 +86,7 @@ your web server configuration when hosting Element Web:
|
||||
|
||||
- The `X-Frame-Options: SAMEORIGIN` header, to prevent Element Web from being
|
||||
framed and protect from [clickjacking][owasp-clickjacking].
|
||||
- The `frame-ancestors 'none'` directive to your `Content-Security-Policy`
|
||||
- The `frame-ancestors 'self'` directive to your `Content-Security-Policy`
|
||||
header, as the modern replacement for `X-Frame-Options` (though both should be
|
||||
included since not all browsers support it yet, see
|
||||
[this][owasp-clickjacking-csp]).
|
||||
@ -113,7 +114,7 @@ For Apache, the configuration looks like:
|
||||
Header set X-Frame-Options SAMEORIGIN
|
||||
Header set X-Content-Type-Options nosniff
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
Header set Content-Security-Policy "frame-ancestors 'none'"
|
||||
Header set Content-Security-Policy "frame-ancestors 'self'"
|
||||
```
|
||||
|
||||
Note: In case you are already setting a `Content-Security-Policy` header
|
||||
|
133
code_style.md
133
code_style.md
@ -59,20 +59,21 @@ Unless otherwise specified, the following applies to all code:
|
||||
6. Lines are trimmed of all excess whitespace, including blank lines.
|
||||
7. Long lines are broken up for readability.
|
||||
|
||||
## TypeScript / JavaScript {#typescript-javascript}
|
||||
## TypeScript / JavaScript
|
||||
|
||||
1. Write TypeScript. Turn JavaScript into TypeScript when working in the area.
|
||||
2. Use named exports.
|
||||
3. Use semicolons for block/line termination.
|
||||
2. Use [TSDoc](https://tsdoc.org/) to document your code. See [Comments](#comments) below.
|
||||
3. Use named exports.
|
||||
4. Use semicolons for block/line termination.
|
||||
1. Except when defining interfaces, classes, and non-arrow functions specifically.
|
||||
4. When a statement's body is a single line, it must be written without curly braces, so long as the body is placed on
|
||||
5. When a statement's body is a single line, it must be written without curly braces, so long as the body is placed on
|
||||
the same line as the statement.
|
||||
|
||||
```typescript
|
||||
if (x) doThing();
|
||||
```
|
||||
|
||||
5. Blocks for `if`, `for`, `switch` and so on must have a space surrounding the condition, but not
|
||||
6. Blocks for `if`, `for`, `switch` and so on must have a space surrounding the condition, but not
|
||||
within the condition.
|
||||
|
||||
```typescript
|
||||
@ -81,17 +82,17 @@ Unless otherwise specified, the following applies to all code:
|
||||
}
|
||||
```
|
||||
|
||||
6. lowerCamelCase is used for function and variable naming.
|
||||
7. UpperCamelCase is used for general naming.
|
||||
8. Interface names should not be marked with an uppercase `I`.
|
||||
9. One variable declaration per line.
|
||||
10. If a variable is not receiving a value on declaration, its type must be defined.
|
||||
7. lowerCamelCase is used for function and variable naming.
|
||||
8. UpperCamelCase is used for general naming.
|
||||
9. Interface names should not be marked with an uppercase `I`.
|
||||
10. One variable declaration per line.
|
||||
11. If a variable is not receiving a value on declaration, its type must be defined.
|
||||
|
||||
```typescript
|
||||
let errorMessage: Optional<string>;
|
||||
```
|
||||
|
||||
11. Objects can use shorthand declarations, including mixing of types.
|
||||
12. Objects can use shorthand declarations, including mixing of types.
|
||||
|
||||
```typescript
|
||||
{
|
||||
@ -102,7 +103,7 @@ Unless otherwise specified, the following applies to all code:
|
||||
{ room, prop: this.prop }
|
||||
```
|
||||
|
||||
12. Object keys should always be non-strings when possible.
|
||||
13. Object keys should always be non-strings when possible.
|
||||
|
||||
```typescript
|
||||
{
|
||||
@ -112,23 +113,28 @@ Unless otherwise specified, the following applies to all code:
|
||||
}
|
||||
```
|
||||
|
||||
13. Explicitly cast to a boolean.
|
||||
14. Explicitly cast to a boolean, rather than relying on implicit truthiness of non-boolean values:
|
||||
|
||||
```typescript
|
||||
!!stringVar || Boolean(stringVar);
|
||||
const isRealUser = !!userId && ...;
|
||||
// ... or ...
|
||||
const isRealUser = Boolean(userId) && ...;
|
||||
|
||||
// but *not*:
|
||||
const isRealUser = userId && ...; // invalid implicit cast
|
||||
```
|
||||
|
||||
14. Use `switch` statements when checking against more than a few enum-like values.
|
||||
15. Use `const` for constants, `let` for mutability.
|
||||
16. Describe types exhaustively (ensure noImplictAny would pass).
|
||||
15. Use `switch` statements when checking against more than a few enum-like values.
|
||||
16. Use `const` for constants, `let` for mutability.
|
||||
17. Describe types exhaustively (ensure noImplictAny would pass).
|
||||
1. Notable exceptions are arrow functions used as parameters, when a void return type is
|
||||
obvious, and when declaring and assigning a variable in the same line.
|
||||
17. Declare member visibility (public/private/protected).
|
||||
18. Private members are private and not prefixed unless required for naming conflicts.
|
||||
18. Declare member visibility (public/private/protected).
|
||||
19. Private members are private and not prefixed unless required for naming conflicts.
|
||||
1. Convention is to use an underscore or the word "internal" to denote conflicted member names.
|
||||
2. "Conflicted" typically refers to a getter which wants the same name as the underlying variable.
|
||||
19. Prefer readonly members over getters backed by a variable, unless an internal setter is required.
|
||||
20. Prefer Interfaces for object definitions, and types for parameter-value-only declarations.
|
||||
20. Prefer readonly members over getters backed by a variable, unless an internal setter is required.
|
||||
21. Prefer Interfaces for object definitions, and types for parameter-value-only declarations.
|
||||
|
||||
1. Note that an explicit type is optional if not expected to be used outside of the function call,
|
||||
unlike in this example:
|
||||
@ -145,9 +151,9 @@ Unless otherwise specified, the following applies to all code:
|
||||
}
|
||||
```
|
||||
|
||||
21. Variables/properties which are `public static` should also be `readonly` when possible.
|
||||
22. Interface and type properties are terminated with semicolons, not commas.
|
||||
23. Prefer arrow formatting when declaring functions for interfaces/types:
|
||||
22. Variables/properties which are `public static` should also be `readonly` when possible.
|
||||
23. Interface and type properties are terminated with semicolons, not commas.
|
||||
24. Prefer arrow formatting when declaring functions for interfaces/types:
|
||||
|
||||
```typescript
|
||||
interface Test {
|
||||
@ -155,13 +161,13 @@ Unless otherwise specified, the following applies to all code:
|
||||
}
|
||||
```
|
||||
|
||||
24. Prefer a type definition over an inline type. For example, define an interface.
|
||||
25. Always prefer to add types or declare a type over the use of `any`. Prefer inferred types
|
||||
25. Prefer a type definition over an inline type. For example, define an interface.
|
||||
26. Always prefer to add types or declare a type over the use of `any`. Prefer inferred types
|
||||
when they are not `any`.
|
||||
1. When using `any`, a comment explaining why must be present.
|
||||
26. `import` should be used instead of `require`, as `require` does not have types.
|
||||
27. Export only what can be reused.
|
||||
28. Prefer a type like `Optional<X>` (`type Optional<T> = T | null | undefined`) instead
|
||||
27. `import` should be used instead of `require`, as `require` does not have types.
|
||||
28. Export only what can be reused.
|
||||
29. Prefer a type like `Optional<X>` (`type Optional<T> = T | null | undefined`) instead
|
||||
of truly optional parameters.
|
||||
|
||||
1. A notable exception is when the likelihood of a bug is minimal, such as when a function
|
||||
@ -179,12 +185,12 @@ Unless otherwise specified, the following applies to all code:
|
||||
}
|
||||
```
|
||||
|
||||
29. There should be approximately one interface, class, or enum per file unless the file is named
|
||||
30. There should be approximately one interface, class, or enum per file unless the file is named
|
||||
"types.ts", "global.d.ts", or ends with "-types.ts".
|
||||
1. The file name should match the interface, class, or enum name.
|
||||
30. Bulk functions can be declared in a single file, though named as "foo-utils.ts" or "utils/foo.ts".
|
||||
31. Imports are grouped by external module imports first, then by internal imports.
|
||||
32. File ordering is not strict, but should generally follow this sequence:
|
||||
31. Bulk functions can be declared in a single file, though named as "foo-utils.ts" or "utils/foo.ts".
|
||||
32. Imports are grouped by external module imports first, then by internal imports.
|
||||
33. File ordering is not strict, but should generally follow this sequence:
|
||||
1. Licence header
|
||||
2. Imports
|
||||
3. Constants
|
||||
@ -199,16 +205,16 @@ Unless otherwise specified, the following applies to all code:
|
||||
5. Protected and abstract functions
|
||||
6. Public/private functions
|
||||
7. Public/protected/private static functions
|
||||
33. Variable names should be noticeably unique from their types. For example, "str: string" instead
|
||||
34. Variable names should be noticeably unique from their types. For example, "str: string" instead
|
||||
of "string: string".
|
||||
34. Use double quotes to enclose strings. You may use single quotes if the string contains double quotes.
|
||||
35. Use double quotes to enclose strings. You may use single quotes if the string contains double quotes.
|
||||
|
||||
```typescript
|
||||
const example1 = "simple string";
|
||||
const example2 = 'string containing "double quotes"';
|
||||
```
|
||||
|
||||
35. Prefer async-await to promise-chaining
|
||||
36. Prefer async-await to promise-chaining
|
||||
|
||||
```typescript
|
||||
async function () {
|
||||
@ -255,7 +261,7 @@ Inheriting all the rules of TypeScript, the following additionally apply:
|
||||
if at all possible.
|
||||
11. A component should only use CSS class names in line with the component name.
|
||||
|
||||
1. When knowingly using a class name from another component, document it.
|
||||
1. When knowingly using a class name from another component, document it with a [comment](#comments).
|
||||
|
||||
12. Curly braces within JSX should be padded with a space, however properties on those components should not.
|
||||
See above code example.
|
||||
@ -275,11 +281,15 @@ Inheriting all the rules of TypeScript, the following additionally apply:
|
||||
Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, but actually it is not.
|
||||
|
||||
1. Class names must be prefixed with "mx\_".
|
||||
2. Class names should denote the component which defines them, followed by any context:
|
||||
1. mx_MyFoo
|
||||
2. mx_MyFoo_avatar
|
||||
3. mx_MyFoo_avatar--user
|
||||
3. Use the `$font` and `$spacing` variables instead of manual values.
|
||||
2. Class names must denote the component which defines them, followed by any context.
|
||||
The context is not further specified here in terms of meaning or syntax.
|
||||
Use whatever is appropriate for your implementation use case.
|
||||
Some examples:
|
||||
1. `mx_MyFoo`
|
||||
2. `mx_MyFoo_avatar`
|
||||
3. `mx_MyFoo_avatarUser`
|
||||
4. `mx_MyFoo_avatar--user`
|
||||
3. Use the `$font` variables instead of manual values.
|
||||
4. Keep indentation/nesting to a minimum. Maximum suggested nesting is 5 layers.
|
||||
5. Use the whole class name instead of shortcuts:
|
||||
|
||||
@ -304,7 +314,7 @@ Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, b
|
||||
|
||||
7. Non-shared variables should use $lowerCamelCase. Shared variables use $dashed-naming.
|
||||
8. Overrides to Z indexes, adjustments of dimensions/padding with pixels, and so on should all be
|
||||
documented for what the values mean:
|
||||
[documented](#comments) for what the values mean:
|
||||
|
||||
```scss
|
||||
.mx_MyFoo {
|
||||
@ -314,7 +324,7 @@ Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, b
|
||||
}
|
||||
```
|
||||
|
||||
9. Avoid the use of `!important`. If necessary, add a comment.
|
||||
9. Avoid the use of `!important`. If `!important` is necessary, add a [comment](#comments) explaining why.
|
||||
|
||||
## Tests
|
||||
|
||||
@ -358,3 +368,38 @@ Note: We use PostCSS + some plugins to process our styles. It looks like SCSS, b
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Comments
|
||||
|
||||
1. As a general principle: be liberal with comments. This applies to all files: stylesheets as well as
|
||||
JavaScript/TypeScript.
|
||||
|
||||
Good comments not only help future readers understand and maintain the code; they can also encourage good design
|
||||
by clearly setting out how different parts of the codebase interact where that would otherwise be implicit and
|
||||
subject to interpretation.
|
||||
|
||||
2. Aim to document all types, methods, class properties, functions, etc, with [TSDoc](https://tsdoc.org/) doc comments.
|
||||
This is _especially_ important for public interfaces in `matrix-js-sdk`, but is good practice in general.
|
||||
|
||||
Even very simple interfaces can often benefit from a doc-comment, both as a matter of consistency, and because simple
|
||||
interfaces have a habit of becoming more complex over time.
|
||||
|
||||
3. React components should be documented in the same way as other classes or functions. The documentation should give
|
||||
a brief description of how the component should be used, and, especially for more complex components, each of its
|
||||
properties should be clearly documented.
|
||||
|
||||
4. Inside a function, there is no need to comment every line, but consider:
|
||||
|
||||
- before a particular multiline section of code within the function, give an overview of what it does,
|
||||
to make it easier for a reader to follow the flow through the function as a whole.
|
||||
- if it is anything less than obvious, explain _why_ we are doing a particular operation, with particular emphasis
|
||||
on how this function interacts with other parts of the codebase.
|
||||
|
||||
5. When making changes to existing code, authors are expected to read existing comments and make any necessary changes
|
||||
to ensure they remain accurate.
|
||||
|
||||
6. Reviewers are encouraged to consider whether more comments would be useful, and to ask the author to add them.
|
||||
|
||||
It is natural for an author to feel that the code they have just written is "obvious" and that comments would be
|
||||
redundant, whereas in reality it would take some time for reader unfamiliar with the code to understand it. A
|
||||
reviewer is well-placed to make a more objective judgement.
|
||||
|
@ -508,6 +508,7 @@ Currently, the following UI feature flags are supported:
|
||||
timeline for recent messages. When false day dates will be used.
|
||||
- `UIFeature.BulkUnverifiedSessionsReminder` - Display popup reminders to verify or remove unverified sessions. Defaults
|
||||
to true.
|
||||
- `UIFeature.locationSharing` - Whether or not location sharing menus will be shown.
|
||||
|
||||
## Undocumented / developer options
|
||||
|
||||
|
@ -100,12 +100,6 @@ theme definition.
|
||||
|
||||
For some sample themes, check out [aaronraimist/element-themes](https://github.com/aaronraimist/element-themes).
|
||||
|
||||
## Message preview tweaks
|
||||
|
||||
To enable message previews in the left panel for reactions in all rooms, enable `feature_roomlist_preview_reactions_all`.
|
||||
|
||||
To enable message previews for reactions in DMs only, enable `feature_roomlist_preview_reactions_dms`. This is ignored when it is enabled for all rooms.
|
||||
|
||||
## Dehydrated devices (`feature_dehydration`)
|
||||
|
||||
Allows users to receive encrypted messages by creating a device that is stored
|
||||
|
57
jest.config.ts
Normal file
57
jest.config.ts
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { env } from "process";
|
||||
|
||||
import type { Config } from "jest";
|
||||
|
||||
const config: Config = {
|
||||
testEnvironment: "jsdom",
|
||||
testEnvironmentOptions: {
|
||||
url: "http://localhost/",
|
||||
},
|
||||
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
|
||||
setupFiles: ["jest-canvas-mock"],
|
||||
setupFilesAfterEnv: ["<rootDir>/node_modules/matrix-react-sdk/test/setupTests.js"],
|
||||
moduleNameMapper: {
|
||||
"\\.(css|scss|pcss)$": "<rootDir>/__mocks__/cssMock.js",
|
||||
"\\.(gif|png|ttf|woff2)$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/imageMock.js",
|
||||
"\\.svg$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/svg.js",
|
||||
"\\$webapp/i18n/languages.json": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/languages.json",
|
||||
"^react$": "<rootDir>/node_modules/react",
|
||||
"^react-dom$": "<rootDir>/node_modules/react-dom",
|
||||
"^matrix-js-sdk$": "<rootDir>/node_modules/matrix-js-sdk/src",
|
||||
"^matrix-react-sdk$": "<rootDir>/node_modules/matrix-react-sdk/src",
|
||||
"decoderWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"decoderWorker\\.min\\.wasm": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"waveWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"context-filter-polyfill": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"FontManager.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/FontManager.js",
|
||||
"workers/(.+)\\.worker\\.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/workerMock.js",
|
||||
"^!!raw-loader!.*": "jest-raw-loader",
|
||||
"RecorderWorklet": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
},
|
||||
transformIgnorePatterns: ["/node_modules/(?!matrix-js-sdk).+$", "/node_modules/(?!matrix-react-sdk).+$"],
|
||||
coverageReporters: ["text-summary", "lcov"],
|
||||
testResultsProcessor: "@casualbot/jest-sonar-reporter",
|
||||
};
|
||||
|
||||
// if we're running under GHA, enable the GHA reporter
|
||||
if (env["GITHUB_ACTIONS"] !== undefined) {
|
||||
config.reporters = [["github-actions", { silent: false }], "summary"];
|
||||
}
|
||||
|
||||
export default config;
|
85
package.json
85
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.11.24",
|
||||
"version": "1.11.30",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@ -26,6 +26,9 @@
|
||||
"contribute.json"
|
||||
],
|
||||
"style": "bundle.css",
|
||||
"matrix_i18n_extra_translation_funcs": [
|
||||
"UserFriendlyError"
|
||||
],
|
||||
"scripts": {
|
||||
"i18n": "matrix-gen-i18n",
|
||||
"prunei18n": "matrix-prune-i18n",
|
||||
@ -62,17 +65,17 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react-dom": "17.0.19",
|
||||
"@types/react": "17.0.53"
|
||||
"@types/react": "17.0.58"
|
||||
},
|
||||
"dependencies": {
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz",
|
||||
"@matrix-org/react-sdk-module-api": "^0.0.4",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
|
||||
"@matrix-org/react-sdk-module-api": "^0.0.5",
|
||||
"gfm.css": "^1.1.2",
|
||||
"jsrsasign": "^10.5.25",
|
||||
"katex": "^0.16.0",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
|
||||
"matrix-widget-api": "^1.1.1",
|
||||
"matrix-widget-api": "^1.3.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"sanitize-html": "^2.3.2",
|
||||
@ -100,12 +103,12 @@
|
||||
"@sentry/webpack-plugin": "^1.18.1",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@types/flux": "^3.1.9",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/jitsi-meet": "^2.0.2",
|
||||
"@types/jsrsasign": "^10.5.4",
|
||||
"@types/modernizr": "^3.5.3",
|
||||
"@types/node": "^16",
|
||||
"@types/react": "17.0.53",
|
||||
"@types/react": "17.0.58",
|
||||
"@types/react-dom": "17.0.19",
|
||||
"@types/sanitize-html": "^2.3.1",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
@ -115,11 +118,11 @@
|
||||
"babel-jest": "^29.0.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"chokidar": "^3.5.1",
|
||||
"concurrently": "^7.0.0",
|
||||
"concurrently": "^8.0.0",
|
||||
"cpx": "^1.5.0",
|
||||
"css-loader": "^4",
|
||||
"dotenv": "^16.0.2",
|
||||
"eslint": "8.35.0",
|
||||
"eslint": "8.39.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-deprecate": "^0.7.0",
|
||||
@ -127,7 +130,7 @@
|
||||
"eslint-plugin-matrix-org": "^1.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-unicorn": "^45.0.0",
|
||||
"eslint-plugin-unicorn": "^46.0.0",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"fake-indexeddb": "^4.0.0",
|
||||
"fetch-mock-jest": "^1.5.1",
|
||||
@ -135,17 +138,17 @@
|
||||
"fs-extra": "^11.0.0",
|
||||
"html-webpack-plugin": "^4.5.2",
|
||||
"jest": "^29.0.0",
|
||||
"jest-canvas-mock": "^2.3.0",
|
||||
"jest-canvas-mock": "2.4.0",
|
||||
"jest-environment-jsdom": "^29.0.0",
|
||||
"jest-mock": "^29.0.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"json-loader": "^0.5.7",
|
||||
"loader-utils": "^3.0.0",
|
||||
"matrix-mock-request": "^2.5.0",
|
||||
"matrix-web-i18n": "^1.3.0",
|
||||
"matrix-web-i18n": "^1.4.0",
|
||||
"mini-css-extract-plugin": "^1",
|
||||
"minimist": "^1.2.6",
|
||||
"mkdirp": "^2.0.0",
|
||||
"mkdirp": "^3.0.0",
|
||||
"modernizr": "^3.12.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"optimize-css-assets-webpack-plugin": "^6.0.0",
|
||||
@ -159,20 +162,20 @@
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"postcss-scss": "^4.0.4",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"prettier": "2.8.4",
|
||||
"prettier": "2.8.8",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"rimraf": "^5.0.0",
|
||||
"semver": "^7.3.7",
|
||||
"simple-proxy-agent": "^1.1.0",
|
||||
"string-replace-loader": "3",
|
||||
"style-loader": "2",
|
||||
"stylelint": "^15.0.0",
|
||||
"stylelint-config-prettier": "^9.0.4",
|
||||
"stylelint-config-standard": "^30.0.0",
|
||||
"stylelint-scss": "^4.2.0",
|
||||
"stylelint": "^15.3.0",
|
||||
"stylelint-config-standard": "^33.0.0",
|
||||
"stylelint-scss": "^5.0.0",
|
||||
"terser-webpack-plugin": "^4.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-prune": "^0.10.3",
|
||||
"typescript": "4.9.5",
|
||||
"typescript": "5.0.4",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
@ -180,48 +183,6 @@
|
||||
"worklet-loader": "^2.0.0",
|
||||
"yaml": "^2.0.1"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"testEnvironmentOptions": {
|
||||
"url": "http://localhost/"
|
||||
},
|
||||
"testMatch": [
|
||||
"<rootDir>/test/**/*-test.[tj]s?(x)"
|
||||
],
|
||||
"setupFiles": [
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/node_modules/matrix-react-sdk/test/setupTests.js"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss|pcss)$": "<rootDir>/__mocks__/cssMock.js",
|
||||
"\\.(gif|png|ttf|woff2)$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/imageMock.js",
|
||||
"\\.svg$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/svg.js",
|
||||
"\\$webapp/i18n/languages.json": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/languages.json",
|
||||
"^react$": "<rootDir>/node_modules/react",
|
||||
"^react-dom$": "<rootDir>/node_modules/react-dom",
|
||||
"^matrix-js-sdk$": "<rootDir>/node_modules/matrix-js-sdk/src",
|
||||
"^matrix-react-sdk$": "<rootDir>/node_modules/matrix-react-sdk/src",
|
||||
"decoderWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"decoderWorker\\.min\\.wasm": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"waveWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"context-filter-polyfill": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
|
||||
"FontManager.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/FontManager.js",
|
||||
"workers/(.+)\\.worker\\.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/workerMock.js",
|
||||
"^!!raw-loader!.*": "jest-raw-loader",
|
||||
"RecorderWorklet": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!matrix-js-sdk).+$",
|
||||
"/node_modules/(?!matrix-react-sdk).+$"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"text-summary",
|
||||
"lcov"
|
||||
],
|
||||
"testResultsProcessor": "@casualbot/jest-sonar-reporter"
|
||||
},
|
||||
"@casualbot/jest-sonar-reporter": {
|
||||
"outputDirectory": "coverage",
|
||||
"outputName": "jest-sonar-report.xml",
|
||||
|
@ -3,7 +3,12 @@
|
||||
"apps": [],
|
||||
"details": [
|
||||
{
|
||||
"appID": "7J4U792NQT.im.vector.app",
|
||||
"appIDs":[
|
||||
"7J4U792NQT.im.vector.app",
|
||||
"7J4U792NQT.io.element.elementx",
|
||||
"7J4U792NQT.io.element.elementx.nightly",
|
||||
"7J4U792NQT.io.element.elementx.pr"
|
||||
],
|
||||
"paths": [
|
||||
"*"
|
||||
]
|
||||
@ -12,7 +17,10 @@
|
||||
},
|
||||
"webcredentials": {
|
||||
"apps": [
|
||||
"7J4U792NQT.im.vector.app"
|
||||
"7J4U792NQT.im.vector.app",
|
||||
"7J4U792NQT.io.element.elementx",
|
||||
"7J4U792NQT.io.element.elementx.nightly",
|
||||
"7J4U792NQT.io.element.elementx.pr"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,12 @@
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const mkdirp = require("mkdirp");
|
||||
const { mkdirpSync } = require("mkdirp");
|
||||
const fetch = require("node-fetch");
|
||||
const ProxyAgent = require("simple-proxy-agent");
|
||||
|
||||
console.log("Making webapp directory");
|
||||
mkdirp.sync("webapp");
|
||||
mkdirpSync("webapp");
|
||||
|
||||
// curl -s https://meet.element.io/libs/external_api.min.js > ./webapp/jitsi_external_api.min.js
|
||||
console.log("Downloading Jitsi script");
|
||||
|
@ -77,7 +77,7 @@ dodep matrix-org matrix-js-sdk
|
||||
|
||||
pushd matrix-js-sdk
|
||||
yarn link
|
||||
yarn install --pure-lockfile
|
||||
yarn install --frozen-lockfile
|
||||
popd
|
||||
|
||||
yarn link matrix-js-sdk
|
||||
@ -91,7 +91,7 @@ dodep matrix-org matrix-react-sdk
|
||||
pushd matrix-react-sdk
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
yarn install --pure-lockfile
|
||||
yarn install --frozen-lockfile
|
||||
popd
|
||||
|
||||
yarn link matrix-react-sdk
|
||||
|
@ -14,7 +14,7 @@ set -ex
|
||||
# for the primary repo (element-web in this case).
|
||||
|
||||
# Install dependencies, as we'll be using fetchdep.sh from matrix-react-sdk
|
||||
yarn install --pure-lockfile
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
# Pass appropriate repo to fetchdep.sh
|
||||
export PR_ORG=vector-im
|
||||
@ -24,7 +24,7 @@ export PR_REPO=element-web
|
||||
node_modules/matrix-react-sdk/scripts/fetchdep.sh matrix-org matrix-js-sdk
|
||||
pushd matrix-js-sdk
|
||||
yarn link
|
||||
yarn install --pure-lockfile
|
||||
yarn install --frozen-lockfile
|
||||
popd
|
||||
|
||||
# Also set up matrix-analytics-events so we get the latest from
|
||||
@ -32,7 +32,7 @@ popd
|
||||
node_modules/matrix-react-sdk/scripts/fetchdep.sh matrix-org matrix-analytics-events main
|
||||
pushd matrix-analytics-events
|
||||
yarn link
|
||||
yarn install --pure-lockfile
|
||||
yarn install --frozen-lockfile
|
||||
yarn build:ts
|
||||
popd
|
||||
|
||||
@ -42,7 +42,7 @@ pushd matrix-react-sdk
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
yarn link @matrix-org/analytics-events
|
||||
yarn install --pure-lockfile
|
||||
yarn install --frozen-lockfile
|
||||
popd
|
||||
|
||||
# Link the layers into element-web
|
||||
|
29
src/@types/jitsi-meet.d.ts
vendored
Normal file
29
src/@types/jitsi-meet.d.ts
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import "jitsi-meet";
|
||||
|
||||
declare module "jitsi-meet" {
|
||||
interface ExternalAPIEventCallbacks {
|
||||
errorOccurred: (e: { error: Error & { isFatal?: boolean } }) => void;
|
||||
}
|
||||
|
||||
interface JitsiMeetExternalAPI {
|
||||
executeCommand(command: "setTileView", value: boolean): void;
|
||||
}
|
||||
}
|
||||
|
||||
export as namespace Jitsi;
|
@ -17,10 +17,10 @@ limitations under the License.
|
||||
import * as React from "react";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
// directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk
|
||||
// PostCSS variables will be accessible.
|
||||
import "../../../res/css/structures/ErrorView.pcss";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface IProps {
|
||||
onAccept(): void;
|
||||
@ -30,7 +30,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
const brand = SdkConfig.get("brand");
|
||||
const mobileBuilds = SdkConfig.get("mobile_builds");
|
||||
|
||||
let ios = null;
|
||||
let ios: JSX.Element | undefined;
|
||||
const iosCustomUrl = mobileBuilds?.ios;
|
||||
if (iosCustomUrl !== null) {
|
||||
// could be undefined or a string
|
||||
@ -42,6 +42,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
<a
|
||||
href={iosCustomUrl || "https://apps.apple.com/app/vector/id1083446067"}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="mx_ClearDecoration"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/apple.svg" alt="Apple App Store" />
|
||||
@ -63,6 +64,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
<a
|
||||
href={andCustomUrl || "https://play.google.com/store/apps/details?id=im.vector.app"}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="mx_ClearDecoration"
|
||||
key="android"
|
||||
>
|
||||
@ -76,6 +78,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
<a
|
||||
href={fdroidCustomUrl || "https://f-droid.org/repository/browse/?fdid=im.vector.app"}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="mx_ClearDecoration"
|
||||
key="fdroid"
|
||||
>
|
||||
@ -88,7 +91,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
android = [];
|
||||
}
|
||||
|
||||
let mobileHeader = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
|
||||
let mobileHeader: ReactNode = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
|
||||
if (!android.length && !ios) {
|
||||
mobileHeader = null;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ const VectorAuthFooter = (): ReactElement => {
|
||||
{ text: "GitHub", url: "https://github.com/vector-im/element-web" },
|
||||
];
|
||||
|
||||
const authFooterLinks = [];
|
||||
const authFooterLinks: JSX.Element[] = [];
|
||||
for (const linkEntry of links) {
|
||||
authFooterLinks.push(
|
||||
<a href={linkEntry.url} key={linkEntry.text} target="_blank" rel="noreferrer noopener">
|
||||
|
@ -20,7 +20,7 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
import VectorAuthFooter from "./VectorAuthFooter";
|
||||
|
||||
export default class VectorAuthPage extends React.PureComponent {
|
||||
private static welcomeBackgroundUrl;
|
||||
private static welcomeBackgroundUrl?: string;
|
||||
|
||||
// cache the url as a static to prevent it changing without refreshing
|
||||
private static getWelcomeBackgroundUrl(): string {
|
||||
|
@ -49,7 +49,7 @@ export default class Favicon {
|
||||
private readonly params: IParams;
|
||||
private readonly canvas: HTMLCanvasElement;
|
||||
private readonly baseImage: HTMLImageElement;
|
||||
private context: CanvasRenderingContext2D;
|
||||
private context!: CanvasRenderingContext2D;
|
||||
private icons: HTMLLinkElement[];
|
||||
|
||||
private isReady = false;
|
||||
@ -72,14 +72,14 @@ export default class Favicon {
|
||||
// get height and width of the favicon
|
||||
this.canvas.height = this.baseImage.height > 0 ? this.baseImage.height : 32;
|
||||
this.canvas.width = this.baseImage.width > 0 ? this.baseImage.width : 32;
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.context = this.canvas.getContext("2d")!;
|
||||
this.ready();
|
||||
};
|
||||
this.baseImage.setAttribute("src", lastIcon.getAttribute("href"));
|
||||
this.baseImage.setAttribute("src", lastIcon.getAttribute("href")!);
|
||||
} else {
|
||||
this.canvas.height = this.baseImage.height = 32;
|
||||
this.canvas.width = this.baseImage.width = 32;
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.context = this.canvas.getContext("2d")!;
|
||||
this.ready();
|
||||
}
|
||||
}
|
||||
@ -152,7 +152,7 @@ export default class Favicon {
|
||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.context.drawImage(this.baseImage, 0, 0, this.canvas.width, this.canvas.height);
|
||||
this.context.beginPath();
|
||||
const fontSize = Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px";
|
||||
const fontSize = Math.floor(opt.h * (typeof opt.n === "number" && opt.n > 99 ? 0.85 : 1)) + "px";
|
||||
this.context.font = `${params.fontWeight} ${fontSize} ${params.fontFamily}`;
|
||||
this.context.textAlign = "center";
|
||||
|
||||
@ -239,7 +239,7 @@ export default class Favicon {
|
||||
const icons: HTMLLinkElement[] = [];
|
||||
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
|
||||
for (const link of links) {
|
||||
if (/(^|\s)icon(\s|$)/i.test(link.getAttribute("rel"))) {
|
||||
if (link.hasAttribute("rel") && /(^|\s)icon(\s|$)/i.test(link.getAttribute("rel")!)) {
|
||||
icons.push(link);
|
||||
}
|
||||
}
|
||||
|
3
src/i18n/strings/my.json
Normal file
3
src/i18n/strings/my.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"Invalid configuration: no default server specified.": "ဖွဲ့စည်းပုံ မမှန်ပါ။ default ဆာဗာကို သတ်မှတ်ထားခြင်း မရှိပါ။"
|
||||
}
|
@ -25,5 +25,8 @@
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Vennligst installer <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, eller <safariLink>Safari</safariLink> for den beste opplevelsen.",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s bruker avanserte nettleserfunksjoner som ikke støttes av din nåværende nettleser.",
|
||||
"Open": "Åpne",
|
||||
"Use %(brand)s on mobile": "Bruk %(brand)s på mobil"
|
||||
"Use %(brand)s on mobile": "Bruk %(brand)s på mobil",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Desentralisert, kryptert chat & samhandling basert på $matrixLogo",
|
||||
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s på %(osName)s",
|
||||
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s"
|
||||
}
|
||||
|
@ -25,5 +25,8 @@
|
||||
"Your Element is misconfigured": "Element er feilkonfigurert",
|
||||
"Failed to start": "Klarte ikkje å starta",
|
||||
"Open": "Opna",
|
||||
"Download Completed": "Nedlasting Fullført"
|
||||
"Download Completed": "Nedlasting Fullført",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Desentralisertd kryptert chatt & samarbeid som vert drive av $matrixLogo",
|
||||
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s på %(osName)s",
|
||||
"%(brand)s Desktop: %(platformName)s": "%(brand)s Skrivebord: %(platformName)s"
|
||||
}
|
||||
|
@ -26,5 +26,7 @@
|
||||
"Your Element is misconfigured": "Twój Element jest nieprawidłowo skonfigurowany",
|
||||
"Powered by Matrix": "Zasilane przez Matrix",
|
||||
"Use %(brand)s on mobile": "Użyj %(brand)s w telefonie",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Zdecentralizowany, szyfrowany czat i współpraca oparte na $matrixLogo"
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Zdecentralizowany, szyfrowany czat i współpraca oparte na $matrixLogo",
|
||||
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s na %(osName)s",
|
||||
"%(brand)s Desktop: %(platformName)s": "Komputer %(brand)s: %(platformName)s"
|
||||
}
|
||||
|
@ -25,5 +25,6 @@
|
||||
"Failed to start": "Erro ao iniciar",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s utiliza funções avançadas que não são suportadas pelo teu atual browser.",
|
||||
"Your browser can't run %(brand)s": "O teu browser não consegue executar %(brand)s",
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s no telemóvel"
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s no telemóvel",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Chat descentralizado e encriptado & colaboração alimentada por $matrixLogo"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Dismiss": "關閉",
|
||||
"Unknown device": "未知裝置",
|
||||
"Welcome to Element": "歡迎來到 Element",
|
||||
"Welcome to Element": "歡迎使用 Element",
|
||||
"Sign In": "登入",
|
||||
"Create Account": "建立帳號",
|
||||
"Explore rooms": "探索聊天室",
|
||||
@ -20,13 +20,13 @@
|
||||
"Failed to start": "啟動失敗",
|
||||
"Download Completed": "下載完成",
|
||||
"Open": "開啟",
|
||||
"Your Element is misconfigured": "您的 Element 配置錯誤",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "您的 Element 的配置中包含無效 JSON,請更正錯誤並重新加載網頁。",
|
||||
"Your Element is misconfigured": "您的 Element 設定錯誤",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "您的 Element 設定中包含無效 JSON,請修正後重新載入網頁。",
|
||||
"Your browser can't run %(brand)s": "您的瀏覽器無法執行 %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s 使用了您目前瀏覽器不支援的進階功能。",
|
||||
"Powered by Matrix": "由 Matrix 驅動",
|
||||
"Powered by Matrix": "Powered by Matrix",
|
||||
"Use %(brand)s on mobile": "在行動裝置上使用 %(brand)s",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "去中心化、加密的聊天與協作,由 $matrixLogo 驅動",
|
||||
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s:%(browserName)s 位於 %(osName)s",
|
||||
"%(brand)s Desktop: %(platformName)s": "%(brand)s 桌面:%(platformName)s"
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "由 $matrixLogo 驅動的去中心化、加密的聊天與協作工具",
|
||||
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s:%(osName)s 的 %(browserName)s",
|
||||
"%(brand)s Desktop: %(platformName)s": "%(brand)s 桌面版:%(platformName)s"
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ import "matrix-js-sdk/src/browser-index";
|
||||
|
||||
import React, { ReactElement } from "react";
|
||||
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
|
||||
import { _td, newTranslatableError } from "matrix-react-sdk/src/languageHandler";
|
||||
import { UserFriendlyError } from "matrix-react-sdk/src/languageHandler";
|
||||
import AutoDiscoveryUtils from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
|
||||
import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
|
||||
import { AutoDiscovery, ClientConfig } from "matrix-js-sdk/src/autodiscovery";
|
||||
import * as Lifecycle from "matrix-react-sdk/src/Lifecycle";
|
||||
import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConfig";
|
||||
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
@ -33,6 +33,8 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { createClient } from "matrix-js-sdk/src/matrix";
|
||||
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
|
||||
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
|
||||
import { QueryDict, encodeParams } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import { parseQs } from "./url_utils";
|
||||
import VectorBasePlatform from "./platform/VectorBasePlatform";
|
||||
@ -55,24 +57,19 @@ window.matrixLogger = logger;
|
||||
// If we're in electron, we should never pass through a file:// URL otherwise
|
||||
// the identity server will try to 302 the browser to it, which breaks horribly.
|
||||
// so in that instance, hardcode to use app.element.io for now instead.
|
||||
function makeRegistrationUrl(params: object): string {
|
||||
let url;
|
||||
function makeRegistrationUrl(params: QueryDict): string {
|
||||
let url: string;
|
||||
if (window.location.protocol === "vector:") {
|
||||
url = "https://app.element.io/#/register";
|
||||
} else {
|
||||
url = window.location.protocol + "//" + window.location.host + window.location.pathname + "#/register";
|
||||
}
|
||||
|
||||
const keys = Object.keys(params);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
if (i === 0) {
|
||||
url += "?";
|
||||
} else {
|
||||
url += "&";
|
||||
}
|
||||
const k = keys[i];
|
||||
url += k + "=" + encodeURIComponent(params[k]);
|
||||
const encodedParams = encodeParams(params);
|
||||
if (encodedParams) {
|
||||
url += "?" + encodedParams;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
@ -117,18 +114,19 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
||||
if (!hasPossibleToken && !isReturningFromSso && autoRedirect) {
|
||||
logger.log("Bypassing app load to redirect to SSO");
|
||||
const tempCli = createClient({
|
||||
baseUrl: config.validated_server_config.hsUrl,
|
||||
idBaseUrl: config.validated_server_config.isUrl,
|
||||
baseUrl: config.validated_server_config!.hsUrl,
|
||||
idBaseUrl: config.validated_server_config!.isUrl,
|
||||
});
|
||||
PlatformPeg.get().startSingleSignOn(tempCli, "sso", `/${getScreenFromLocation(window.location).screen}`);
|
||||
PlatformPeg.get()!.startSingleSignOn(tempCli, "sso", `/${getScreenFromLocation(window.location).screen}`);
|
||||
|
||||
// We return here because startSingleSignOn() will asynchronously redirect us. We don't
|
||||
// care to wait for it, and don't want to show any UI while we wait (not even half a welcome
|
||||
// page). As such, just don't even bother loading the MatrixChat component.
|
||||
return;
|
||||
return <React.Fragment />;
|
||||
}
|
||||
|
||||
const defaultDeviceName = snakedConfig.get("default_device_display_name") ?? platform.getDefaultDeviceDisplayName();
|
||||
const defaultDeviceName =
|
||||
snakedConfig.get("default_device_display_name") ?? platform?.getDefaultDeviceDisplayName();
|
||||
|
||||
return (
|
||||
<MatrixChat
|
||||
@ -146,7 +144,7 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
||||
}
|
||||
|
||||
async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||
let validatedConfig;
|
||||
let validatedConfig: ValidatedServerConfig;
|
||||
try {
|
||||
logger.log("Verifying homeserver configuration");
|
||||
|
||||
@ -168,16 +166,14 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||
const incompatibleOptions = [wkConfig, serverName, hsUrl].filter((i) => !!i);
|
||||
if (incompatibleOptions.length > 1) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw newTranslatableError(
|
||||
_td(
|
||||
throw new UserFriendlyError(
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, " +
|
||||
"or default_hs_url.",
|
||||
),
|
||||
);
|
||||
}
|
||||
if (incompatibleOptions.length < 1) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw newTranslatableError(_td("Invalid configuration: no default server specified."));
|
||||
throw new UserFriendlyError("Invalid configuration: no default server specified.");
|
||||
}
|
||||
|
||||
if (hsUrl) {
|
||||
@ -199,7 +195,7 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||
}
|
||||
}
|
||||
|
||||
let discoveryResult = null;
|
||||
let discoveryResult: ClientConfig | undefined;
|
||||
if (wkConfig) {
|
||||
logger.log("Config uses a default_server_config - validating object");
|
||||
discoveryResult = await AutoDiscovery.fromDiscoveryConfig(wkConfig);
|
||||
|
@ -18,16 +18,16 @@ import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
|
||||
// Load the config file. First try to load up a domain-specific config of the
|
||||
// form "config.$domain.json" and if that fails, fall back to config.json.
|
||||
export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOptions> {
|
||||
export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOptions | undefined> {
|
||||
if (relativeLocation !== "" && !relativeLocation.endsWith("/")) relativeLocation += "/";
|
||||
|
||||
const specificConfigPromise = getConfig(`${relativeLocation}config.${document.domain}.json`);
|
||||
const specificConfigPromise = getConfig(`${relativeLocation}config.${window.location.hostname}.json`);
|
||||
const generalConfigPromise = getConfig(relativeLocation + "config.json");
|
||||
|
||||
try {
|
||||
const configJson = await specificConfigPromise;
|
||||
// 404s succeed with an empty json config, so check that there are keys
|
||||
if (Object.keys(configJson).length === 0) {
|
||||
if (!configJson || Object.keys(configJson).length === 0) {
|
||||
throw new Error(); // throw to enter the catch
|
||||
}
|
||||
return configJson;
|
||||
@ -36,7 +36,7 @@ export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOpt
|
||||
}
|
||||
}
|
||||
|
||||
async function getConfig(configJsonFilename: string): Promise<IConfigOptions> {
|
||||
async function getConfig(configJsonFilename: string): Promise<IConfigOptions | undefined> {
|
||||
const url = new URL(configJsonFilename, window.location.href);
|
||||
url.searchParams.set("cachebuster", Date.now().toString());
|
||||
const res = await fetch(url, {
|
||||
|
@ -26,7 +26,7 @@
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'none';
|
||||
style-src 'self' 'unsafe-inline' <%= csp_extra_source %>;
|
||||
script-src 'self' 'unsafe-eval' https://www.recaptcha.net https://www.gstatic.com <%= csp_extra_source %>;
|
||||
script-src 'self' 'wasm-unsafe-eval' https://www.recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/ <%= csp_extra_source %>;
|
||||
img-src * blob: data:;
|
||||
connect-src *;
|
||||
font-src 'self' data: <%= csp_extra_source %>;
|
||||
|
@ -19,6 +19,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { extractErrorMessageFromError } from "matrix-react-sdk/src/components/views/dialogs/ErrorDialog";
|
||||
|
||||
// These are things that can run before the skin loads - be careful not to reference the react-sdk though.
|
||||
import { parseQsFromFragment } from "./url_utils";
|
||||
@ -71,7 +72,7 @@ function checkBrowserFeatures(): boolean {
|
||||
// ES2019: http://262.ecma-international.org/10.0/#sec-object.fromentries
|
||||
window.Modernizr.addTest("objectfromentries", () => typeof window.Object?.fromEntries === "function");
|
||||
|
||||
const featureList = Object.keys(window.Modernizr);
|
||||
const featureList = Object.keys(window.Modernizr) as Array<keyof ModernizrStatic>;
|
||||
|
||||
let featureComplete = true;
|
||||
for (const feature of featureList) {
|
||||
@ -194,7 +195,7 @@ async function start(): Promise<void> {
|
||||
await loadConfigPromise;
|
||||
} catch (error) {
|
||||
// Now that we've loaded the theme (CSS), display the config syntax error if needed.
|
||||
if (error.err && error.err instanceof SyntaxError) {
|
||||
if (error instanceof SyntaxError) {
|
||||
// This uses the default brand since the app config is unavailable.
|
||||
return showError(_t("Your Element is misconfigured"), [
|
||||
_t(
|
||||
@ -202,7 +203,7 @@ async function start(): Promise<void> {
|
||||
"Please correct the problem and reload the page.",
|
||||
),
|
||||
_t("The message from the parser is: %(message)s", {
|
||||
message: error.err.message || _t("Invalid JSON"),
|
||||
message: error.message || _t("Invalid JSON"),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
@ -231,7 +232,7 @@ async function start(): Promise<void> {
|
||||
// Like the compatibility page, AWOOOOOGA at the user
|
||||
// This uses the default brand since the app config is unavailable.
|
||||
await showError(_t("Your Element is misconfigured"), [
|
||||
err.translatedMessage || _t("Unexpected error preparing the app. See console for details."),
|
||||
extractErrorMessageFromError(err, _t("Unexpected error preparing the app. See console for details.")),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -240,7 +241,7 @@ start().catch((err) => {
|
||||
logger.error(err);
|
||||
// show the static error in an iframe to not lose any context / console data
|
||||
// with some basic styling to make the iframe full page
|
||||
delete document.body.style.height;
|
||||
document.body.style.removeProperty("height");
|
||||
const iframe = document.createElement("iframe");
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - typescript seems to only like the IE syntax for iframe sandboxing
|
||||
@ -254,5 +255,5 @@ start().catch((err) => {
|
||||
iframe.style.right = "0";
|
||||
iframe.style.bottom = "0";
|
||||
iframe.style.border = "0";
|
||||
document.getElementById("matrixchat").appendChild(iframe);
|
||||
document.getElementById("matrixchat")?.appendChild(iframe);
|
||||
});
|
||||
|
@ -67,11 +67,11 @@ export async function loadConfig(): Promise<void> {
|
||||
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
|
||||
//
|
||||
// Note: this isn't called twice for some wrappers, like the Jitsi wrapper.
|
||||
const platformConfig = await PlatformPeg.get().getConfig();
|
||||
const platformConfig = await PlatformPeg.get()?.getConfig();
|
||||
if (platformConfig) {
|
||||
SdkConfig.put(platformConfig);
|
||||
} else {
|
||||
SdkConfig.unset(); // clears the config (sets to empty object)
|
||||
SdkConfig.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ export function loadOlm(): Promise<void> {
|
||||
|
||||
export async function loadLanguage(): Promise<void> {
|
||||
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/ true);
|
||||
let langs = [];
|
||||
let langs: string[] = [];
|
||||
|
||||
if (!prefLang) {
|
||||
languageHandler.getLanguagesFromBrowser().forEach((l) => {
|
||||
@ -163,7 +163,7 @@ export async function showError(title: string, messages?: string[]): Promise<voi
|
||||
);
|
||||
}
|
||||
|
||||
export async function showIncompatibleBrowser(onAccept): Promise<void> {
|
||||
export async function showIncompatibleBrowser(onAccept: () => void): Promise<void> {
|
||||
const CompatibilityView = (
|
||||
await import(
|
||||
/* webpackChunkName: "compatibility-view" */
|
||||
|
@ -30,6 +30,14 @@ import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||
import { ElementWidgetCapabilities } from "matrix-react-sdk/src/stores/widgets/ElementWidgetCapabilities";
|
||||
|
||||
import type {
|
||||
JitsiMeetExternalAPIConstructor,
|
||||
ExternalAPIEventCallbacks,
|
||||
JitsiMeetExternalAPI as _JitsiMeetExternalAPI,
|
||||
AudioMuteStatusChangedEvent,
|
||||
LogEvent,
|
||||
VideoMuteStatusChangedEvent,
|
||||
} from "jitsi-meet";
|
||||
import { getVectorConfig } from "../getconfig";
|
||||
|
||||
// We have to trick webpack into loading our CSS for us.
|
||||
@ -40,7 +48,7 @@ const JITSI_OPENIDTOKEN_JWT_AUTH = "openidtoken-jwt";
|
||||
// Dev note: we use raw JS without many dependencies to reduce bundle size.
|
||||
// We do not need all of React to render a Jitsi conference.
|
||||
|
||||
declare let JitsiMeetExternalAPI: any;
|
||||
declare let JitsiMeetExternalAPI: JitsiMeetExternalAPIConstructor;
|
||||
|
||||
let inConference = false;
|
||||
|
||||
@ -52,15 +60,16 @@ let avatarUrl: string;
|
||||
let userId: string;
|
||||
let jitsiAuth: string;
|
||||
let roomId: string;
|
||||
let openIdToken: IOpenIDCredentials;
|
||||
let roomName: string;
|
||||
let startAudioOnly: boolean;
|
||||
let startWithAudioMuted: boolean | undefined;
|
||||
let startWithVideoMuted: boolean | undefined;
|
||||
let isVideoChannel: boolean;
|
||||
let supportsScreensharing: boolean;
|
||||
let language: string;
|
||||
|
||||
let widgetApi: WidgetApi;
|
||||
let meetApi: any; // JitsiMeetExternalAPI
|
||||
let widgetApi: WidgetApi | undefined;
|
||||
let meetApi: _JitsiMeetExternalAPI | undefined;
|
||||
let skipOurWelcomeScreen = false;
|
||||
|
||||
const setupCompleted = (async (): Promise<string | void> => {
|
||||
@ -80,6 +89,13 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
}
|
||||
return vals[0];
|
||||
};
|
||||
const parseBooleanOrUndefined = (value: string | undefined): boolean | undefined => {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return value === "true";
|
||||
};
|
||||
|
||||
// If we have these params, expect a widget API to be available (ie. to be in an iframe
|
||||
// inside a matrix client). Otherwise, assume we're on our own, eg. have been popped
|
||||
@ -94,12 +110,12 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
}
|
||||
|
||||
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||
let widgetApiReady: Promise<void>;
|
||||
let widgetApiReady: Promise<void> | undefined;
|
||||
if (parentUrl && widgetId) {
|
||||
const parentOrigin = new URL(qsParam("parentUrl")).origin;
|
||||
widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin);
|
||||
|
||||
widgetApiReady = new Promise<void>((resolve) => widgetApi.once("ready", resolve));
|
||||
widgetApiReady = new Promise<void>((resolve) => widgetApi!.once("ready", resolve));
|
||||
widgetApi.requestCapabilities(VideoConferenceCapabilities);
|
||||
|
||||
// jitsi cannot work in a popup if auth token is provided because widgetApi is not available there
|
||||
@ -114,7 +130,7 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
action: WidgetApiAction,
|
||||
handler: (request: IWidgetApiRequestData) => Promise<void>,
|
||||
): void => {
|
||||
widgetApi.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
widgetApi!.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
await setupCompleted;
|
||||
|
||||
@ -130,7 +146,7 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
}
|
||||
}
|
||||
|
||||
await widgetApi.transport.reply(ev.detail, response);
|
||||
await widgetApi!.transport.reply(ev.detail, response);
|
||||
});
|
||||
};
|
||||
|
||||
@ -141,7 +157,7 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
if (force === true) {
|
||||
meetApi?.dispose();
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
meetApi = undefined;
|
||||
closeConference();
|
||||
} else {
|
||||
meetApi?.executeCommand("hangup");
|
||||
@ -197,14 +213,17 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
roomId = qsParam("roomId", true);
|
||||
roomName = qsParam("roomName", true);
|
||||
startAudioOnly = qsParam("isAudioOnly", true) === "true";
|
||||
startWithAudioMuted = parseBooleanOrUndefined(qsParam("startWithAudioMuted", true));
|
||||
startWithVideoMuted = parseBooleanOrUndefined(qsParam("startWithVideoMuted", true));
|
||||
isVideoChannel = qsParam("isVideoChannel", true) === "true";
|
||||
supportsScreensharing = qsParam("supportsScreensharing", true) === "true";
|
||||
|
||||
// We've reached the point where we have to wait for the config, so do that then parse it.
|
||||
const instanceConfig = new SnakedObject<IConfigOptions>((await configPromise) ?? <IConfigOptions>{});
|
||||
const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {};
|
||||
skipOurWelcomeScreen =
|
||||
new SnakedObject<IConfigOptions["jitsi_widget"]>(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
|
||||
const jitsiConfig = instanceConfig.get("jitsi_widget");
|
||||
if (jitsiConfig) {
|
||||
skipOurWelcomeScreen = new SnakedObject(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
|
||||
}
|
||||
|
||||
// Either reveal the prejoin screen, or skip straight to Jitsi depending on the config.
|
||||
// We don't set up the call yet though as this might lead to failure without the widget API.
|
||||
@ -212,13 +231,6 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
|
||||
if (widgetApi) {
|
||||
await widgetApiReady;
|
||||
|
||||
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
// Request credentials, give callback to continue when received
|
||||
openIdToken = await widgetApi.requestOpenIDConnectToken();
|
||||
logger.log("Got OpenID Connect token");
|
||||
}
|
||||
}
|
||||
|
||||
// Now that everything should be set up, skip to the Jitsi splash screen if needed
|
||||
@ -229,12 +241,12 @@ const setupCompleted = (async (): Promise<string | void> => {
|
||||
enableJoinButton(); // always enable the button
|
||||
} catch (e) {
|
||||
logger.error("Error setting up Jitsi widget", e);
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
document.getElementById("widgetActionContainer")!.innerText = "Failed to load Jitsi widget";
|
||||
}
|
||||
})();
|
||||
|
||||
function enableJoinButton(): void {
|
||||
document.getElementById("joinButton").onclick = (): void => joinConference();
|
||||
document.getElementById("joinButton")!.onclick = (): Promise<void> => joinConference();
|
||||
}
|
||||
|
||||
function switchVisibleContainers(): void {
|
||||
@ -248,9 +260,9 @@ function switchVisibleContainers(): void {
|
||||
}
|
||||
|
||||
function toggleConferenceVisibility(inConference: boolean): void {
|
||||
document.getElementById("jitsiContainer").style.visibility = inConference ? "unset" : "hidden";
|
||||
document.getElementById("jitsiContainer")!.style.visibility = inConference ? "unset" : "hidden";
|
||||
// Video rooms have a separate UI for joining, so they should never show our join button
|
||||
document.getElementById("joinButtonContainer").style.visibility =
|
||||
document.getElementById("joinButtonContainer")!.style.visibility =
|
||||
inConference || isVideoChannel ? "hidden" : "unset";
|
||||
}
|
||||
|
||||
@ -260,11 +272,11 @@ function skipToJitsiSplashScreen(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JWT token fot jitsi openidtoken-jwt auth
|
||||
* Create a JWT token for jitsi openidtoken-jwt auth
|
||||
*
|
||||
* See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
*/
|
||||
function createJWTToken(): string {
|
||||
function createJWTToken(openIdToken: IOpenIDCredentials): string {
|
||||
// Header
|
||||
const header = { alg: "HS256", typ: "JWT" };
|
||||
// Payload
|
||||
@ -308,7 +320,7 @@ async function notifyHangup(errorMessage?: string): Promise<void> {
|
||||
|
||||
function closeConference(): void {
|
||||
switchVisibleContainers();
|
||||
document.getElementById("jitsiContainer").innerHTML = "";
|
||||
document.getElementById("jitsiContainer")!.innerHTML = "";
|
||||
|
||||
if (skipOurWelcomeScreen) {
|
||||
skipToJitsiSplashScreen();
|
||||
@ -345,17 +357,21 @@ function mapLanguage(language: string): string {
|
||||
// audio input it can find, while an input of null instructs it to start muted,
|
||||
// and a non-nullish input specifies the label of a specific device to use.
|
||||
// Same for video inputs.
|
||||
function joinConference(audioInput?: string | null, videoInput?: string | null): void {
|
||||
let jwt;
|
||||
async function joinConference(audioInput?: string | null, videoInput?: string | null): Promise<void> {
|
||||
let jwt: string | undefined;
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
const openIdToken = await widgetApi?.requestOpenIDConnectToken();
|
||||
logger.log("Got OpenID Connect token");
|
||||
|
||||
if (!openIdToken?.access_token) {
|
||||
// eslint-disable-line camelcase
|
||||
// We've failing to get a token, don't try to init conference
|
||||
logger.warn("Expected to have an OpenID credential, cannot initialize widget.");
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
document.getElementById("widgetActionContainer")!.innerText = "Failed to load Jitsi widget";
|
||||
return;
|
||||
}
|
||||
jwt = createJWTToken();
|
||||
jwt = createJWTToken(openIdToken);
|
||||
}
|
||||
|
||||
switchVisibleContainers();
|
||||
@ -369,7 +385,7 @@ function joinConference(audioInput?: string | null, videoInput?: string | null):
|
||||
const options = {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
parentNode: document.querySelector("#jitsiContainer"),
|
||||
parentNode: document.querySelector("#jitsiContainer") ?? undefined,
|
||||
roomName: conferenceId,
|
||||
devices: {
|
||||
audioInput,
|
||||
@ -388,8 +404,8 @@ function joinConference(audioInput?: string | null, videoInput?: string | null):
|
||||
configOverwrite: {
|
||||
subject: roomName,
|
||||
startAudioOnly,
|
||||
startWithAudioMuted: audioInput === null,
|
||||
startWithVideoMuted: videoInput === null,
|
||||
startWithAudioMuted: audioInput === null ? true : startWithAudioMuted,
|
||||
startWithVideoMuted: videoInput === null ? true : startWithVideoMuted,
|
||||
// Request some log levels for inclusion in rageshakes
|
||||
// Ideally we would capture all possible log levels, but this can
|
||||
// cause Jitsi Meet to try to post various circular data structures
|
||||
@ -427,13 +443,13 @@ function joinConference(audioInput?: string | null, videoInput?: string | null):
|
||||
// (regardless of video on or off)
|
||||
meetApi.on("videoConferenceJoined", onVideoConferenceJoined);
|
||||
meetApi.on("videoConferenceLeft", onVideoConferenceLeft);
|
||||
meetApi.on("readyToClose", closeConference);
|
||||
meetApi.on("readyToClose", closeConference as ExternalAPIEventCallbacks["readyToClose"]);
|
||||
meetApi.on("errorOccurred", onErrorOccurred);
|
||||
meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged);
|
||||
meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged);
|
||||
|
||||
["videoConferenceJoined", "participantJoined", "participantLeft"].forEach((event) => {
|
||||
meetApi.on(event, updateParticipants);
|
||||
(["videoConferenceJoined", "participantJoined", "participantLeft"] as const).forEach((event) => {
|
||||
meetApi!.on(event, updateParticipants);
|
||||
});
|
||||
|
||||
// Patch logs into rageshakes
|
||||
@ -449,9 +465,9 @@ const onVideoConferenceJoined = (): void => {
|
||||
// We can't just use these commands immediately after creating the
|
||||
// iframe, because there's *another* bug where they can crash Jitsi by
|
||||
// racing with its startup process.
|
||||
if (displayName) meetApi.executeCommand("displayName", displayName);
|
||||
if (displayName) meetApi?.executeCommand("displayName", displayName);
|
||||
// This doesn't have a userInfo equivalent, so has to be set via commands
|
||||
if (avatarUrl) meetApi.executeCommand("avatarUrl", avatarUrl);
|
||||
if (avatarUrl) meetApi?.executeCommand("avatarUrl", avatarUrl);
|
||||
|
||||
if (widgetApi) {
|
||||
// ignored promise because we don't care if it works
|
||||
@ -461,30 +477,30 @@ const onVideoConferenceJoined = (): void => {
|
||||
}
|
||||
|
||||
// Video rooms should start in tile mode
|
||||
if (isVideoChannel) meetApi.executeCommand("setTileView", true);
|
||||
if (isVideoChannel) meetApi?.executeCommand("setTileView", true);
|
||||
};
|
||||
|
||||
const onVideoConferenceLeft = (): void => {
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
meetApi = undefined;
|
||||
};
|
||||
|
||||
const onErrorOccurred = ({ error }): void => {
|
||||
const onErrorOccurred = ({ error }: Parameters<ExternalAPIEventCallbacks["errorOccurred"]>[0]): void => {
|
||||
if (error.isFatal) {
|
||||
// We got disconnected. Since Jitsi Meet might send us back to the
|
||||
// prejoin screen, we're forced to act as if we hung up entirely.
|
||||
notifyHangup(error.message);
|
||||
meetApi = null;
|
||||
meetApi = undefined;
|
||||
closeConference();
|
||||
}
|
||||
};
|
||||
|
||||
const onAudioMuteStatusChanged = ({ muted }): void => {
|
||||
const onAudioMuteStatusChanged = ({ muted }: AudioMuteStatusChangedEvent): void => {
|
||||
const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio;
|
||||
widgetApi?.transport.send(action, {});
|
||||
};
|
||||
|
||||
const onVideoMuteStatusChanged = ({ muted }): void => {
|
||||
const onVideoMuteStatusChanged = ({ muted }: VideoMuteStatusChangedEvent): void => {
|
||||
if (muted) {
|
||||
// Jitsi Meet always sends a "video muted" event directly before
|
||||
// hanging up, which we need to ignore by padding the timeout here,
|
||||
@ -500,8 +516,9 @@ const onVideoMuteStatusChanged = ({ muted }): void => {
|
||||
|
||||
const updateParticipants = (): void => {
|
||||
widgetApi?.transport.send(ElementWidgetActions.CallParticipants, {
|
||||
participants: meetApi.getParticipantsInfo(),
|
||||
participants: meetApi?.getParticipantsInfo(),
|
||||
});
|
||||
};
|
||||
|
||||
const onLog = ({ logLevel, args }): void => (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);
|
||||
const onLog = ({ logLevel, args }: LogEvent): void =>
|
||||
(parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);
|
||||
|
@ -31,17 +31,17 @@ function renderConfigError(message: string): void {
|
||||
}
|
||||
|
||||
async function initPage(): Promise<void> {
|
||||
document.getElementById("back_to_element_button").onclick = onBackToElementClick;
|
||||
document.getElementById("back_to_element_button")!.onclick = onBackToElementClick;
|
||||
|
||||
const config = await getVectorConfig("..");
|
||||
|
||||
// We manually parse the config similar to how validateServerConfig works because
|
||||
// calling that function pulls in roughly 4mb of JS we don't use.
|
||||
|
||||
const wkConfig = config["default_server_config"]; // overwritten later under some conditions
|
||||
const serverName = config["default_server_name"];
|
||||
const defaultHsUrl = config["default_hs_url"];
|
||||
const defaultIsUrl = config["default_is_url"];
|
||||
const wkConfig = config?.["default_server_config"]; // overwritten later under some conditions
|
||||
const serverName = config?.["default_server_name"];
|
||||
const defaultHsUrl = config?.["default_hs_url"];
|
||||
const defaultIsUrl = config?.["default_is_url"];
|
||||
|
||||
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
||||
if (incompatibleOptions.length > 1) {
|
||||
@ -54,13 +54,13 @@ async function initPage(): Promise<void> {
|
||||
return renderConfigError("Invalid configuration: no default server specified.");
|
||||
}
|
||||
|
||||
let hsUrl = "";
|
||||
let isUrl = "";
|
||||
let hsUrl: string | undefined;
|
||||
let isUrl: string | undefined;
|
||||
|
||||
if (wkConfig && wkConfig["m.homeserver"]) {
|
||||
if (typeof wkConfig?.["m.homeserver"]?.["base_url"] === "string") {
|
||||
hsUrl = wkConfig["m.homeserver"]["base_url"];
|
||||
|
||||
if (wkConfig["m.identity_server"]) {
|
||||
if (typeof wkConfig["m.identity_server"]?.["base_url"] === "string") {
|
||||
isUrl = wkConfig["m.identity_server"]["base_url"];
|
||||
}
|
||||
}
|
||||
@ -96,17 +96,19 @@ async function initPage(): Promise<void> {
|
||||
if (isUrl && !isUrl.endsWith("/")) isUrl += "/";
|
||||
|
||||
if (hsUrl !== "https://matrix.org/") {
|
||||
(document.getElementById("configure_element_button") as HTMLAnchorElement).href =
|
||||
"https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) + "&is_url=" + encodeURIComponent(isUrl);
|
||||
document.getElementById("step1_heading").innerHTML = "1: Install the app";
|
||||
document.getElementById("step2_container").style.display = "block";
|
||||
document.getElementById("hs_url").innerText = hsUrl;
|
||||
let url = "https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl);
|
||||
|
||||
if (isUrl) {
|
||||
document.getElementById("custom_is").style.display = "block";
|
||||
document.getElementById("is_url").style.display = "block";
|
||||
document.getElementById("is_url").innerText = isUrl;
|
||||
document.getElementById("custom_is")!.style.display = "block";
|
||||
document.getElementById("is_url")!.style.display = "block";
|
||||
document.getElementById("is_url")!.innerText = isUrl;
|
||||
url += "&is_url=" + encodeURIComponent(isUrl ?? "");
|
||||
}
|
||||
|
||||
(document.getElementById("configure_element_button") as HTMLAnchorElement).href = url;
|
||||
document.getElementById("step1_heading")!.innerHTML = "1: Install the app";
|
||||
document.getElementById("step2_container")!.style.display = "block";
|
||||
document.getElementById("hs_url")!.innerText = hsUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,11 +40,21 @@ import ToastStore from "matrix-react-sdk/src/stores/ToastStore";
|
||||
import GenericExpiringToast from "matrix-react-sdk/src/components/views/toasts/GenericExpiringToast";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { BreadcrumbsStore } from "matrix-react-sdk/src/stores/BreadcrumbsStore";
|
||||
import { UPDATE_EVENT } from "matrix-react-sdk/src/stores/AsyncStore";
|
||||
import { avatarUrlForRoom, getInitialLetter } from "matrix-react-sdk/src/Avatar";
|
||||
|
||||
import VectorBasePlatform from "./VectorBasePlatform";
|
||||
import { SeshatIndexManager } from "./SeshatIndexManager";
|
||||
import { IPCManager } from "./IPCManager";
|
||||
|
||||
interface SquirrelUpdate {
|
||||
releaseNotes: string;
|
||||
releaseName: string;
|
||||
releaseDate: Date;
|
||||
updateURL: string;
|
||||
}
|
||||
|
||||
const isMac = navigator.platform.toUpperCase().includes("MAC");
|
||||
|
||||
function platformFriendlyName(): string {
|
||||
@ -69,7 +79,7 @@ function platformFriendlyName(): string {
|
||||
function onAction(payload: ActionPayload): void {
|
||||
// Whitelist payload actions, no point sending most across
|
||||
if (["call_state"].includes(payload.action)) {
|
||||
window.electron.send("app_onAction", payload);
|
||||
window.electron!.send("app_onAction", payload);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,6 +105,10 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
if (!window.electron) {
|
||||
throw new Error("Cannot instantiate ElectronPlatform, window.electron is not set");
|
||||
}
|
||||
|
||||
dis.register(onAction);
|
||||
/*
|
||||
IPC Call `check_updates` returns:
|
||||
@ -125,12 +139,12 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
const key = `DOWNLOAD_TOAST_${id}`;
|
||||
|
||||
const onAccept = (): void => {
|
||||
window.electron.send("userDownloadAction", { id, open: true });
|
||||
window.electron!.send("userDownloadAction", { id, open: true });
|
||||
ToastStore.sharedInstance().dismissToast(key);
|
||||
};
|
||||
|
||||
const onDismiss = (): void => {
|
||||
window.electron.send("userDownloadAction", { id });
|
||||
window.electron!.send("userDownloadAction", { id });
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
@ -150,13 +164,29 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
});
|
||||
|
||||
this.ipc.call("startSSOFlow", this.ssoID);
|
||||
|
||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||
}
|
||||
|
||||
public async getConfig(): Promise<IConfigOptions> {
|
||||
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||
return this.ipc.call("getConfig");
|
||||
}
|
||||
|
||||
private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }): Promise<void> => {
|
||||
private onBreadcrumbsUpdate = (): void => {
|
||||
const rooms = BreadcrumbsStore.instance.rooms.slice(0, 7).map((r) => ({
|
||||
roomId: r.roomId,
|
||||
avatarUrl: avatarUrlForRoom(
|
||||
r,
|
||||
Math.floor(60 * window.devicePixelRatio),
|
||||
Math.floor(60 * window.devicePixelRatio),
|
||||
"crop",
|
||||
),
|
||||
initial: getInitialLetter(r.name),
|
||||
}));
|
||||
this.ipc.call("breadcrumbs", rooms);
|
||||
};
|
||||
|
||||
private onUpdateDownloaded = async (ev: Event, { releaseNotes, releaseName }: SquirrelUpdate): Promise<void> => {
|
||||
dis.dispatch<CheckUpdatesPayload>({
|
||||
action: Action.CheckUpdates,
|
||||
status: UpdateCheckStatus.Ready,
|
||||
@ -186,7 +216,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
if (this.notificationCount === count) return;
|
||||
super.setNotificationCount(count);
|
||||
|
||||
window.electron.send("setBadgeCount", count);
|
||||
window.electron!.send("setBadgeCount", count);
|
||||
}
|
||||
|
||||
public supportsNotifications(): boolean {
|
||||
@ -226,7 +256,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
public loudNotification(ev: MatrixEvent, room: Room): void {
|
||||
window.electron.send("loudNotification");
|
||||
window.electron!.send("loudNotification");
|
||||
}
|
||||
|
||||
public needsUrlTooltips(): boolean {
|
||||
@ -262,14 +292,14 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
|
||||
public startUpdateCheck(): void {
|
||||
super.startUpdateCheck();
|
||||
window.electron.send("check_updates");
|
||||
window.electron!.send("check_updates");
|
||||
}
|
||||
|
||||
public installUpdate(): void {
|
||||
// IPC to the main process to install the update, since quitAndInstall
|
||||
// doesn't fire the before-quit event so the main process needs to know
|
||||
// it should exit.
|
||||
window.electron.send("install_update");
|
||||
window.electron!.send("install_update");
|
||||
}
|
||||
|
||||
public getDefaultDeviceDisplayName(): string {
|
||||
@ -389,4 +419,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
await this.ipc.call("destroyPickleKey", userId, deviceId);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
public async clearStorage(): Promise<void> {
|
||||
try {
|
||||
await super.clearStorage();
|
||||
await this.ipc.call("clearStorage");
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ export class IPCManager {
|
||||
private readonly sendChannel: ElectronChannel = "ipcCall",
|
||||
private readonly recvChannel: ElectronChannel = "ipcReply",
|
||||
) {
|
||||
if (!window.electron) {
|
||||
throw new Error("Cannot instantiate ElectronPlatform, window.electron is not set");
|
||||
}
|
||||
window.electron.on(this.recvChannel, this.onIpcReply);
|
||||
}
|
||||
|
||||
@ -42,7 +45,7 @@ export class IPCManager {
|
||||
const deferred = defer<any>();
|
||||
this.pendingIpcCalls[ipcCallId] = deferred;
|
||||
// Maybe add a timeout to these? Probably not necessary.
|
||||
window.electron.send(this.sendChannel, { id: ipcCallId, name, args });
|
||||
window.electron!.send(this.sendChannel, { id: ipcCallId, name, args });
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import BaseEventIndexManager, {
|
||||
IEventAndProfile,
|
||||
IIndexStats,
|
||||
ISearchArgs,
|
||||
ILoadArgs,
|
||||
} from "matrix-react-sdk/src/indexing/BaseEventIndexManager";
|
||||
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
|
||||
|
||||
@ -75,7 +76,7 @@ export class SeshatIndexManager extends BaseEventIndexManager {
|
||||
return this.ipc.call("removeCrawlerCheckpoint", checkpoint);
|
||||
}
|
||||
|
||||
public async loadFileEvents(args): Promise<IEventAndProfile[]> {
|
||||
public async loadFileEvents(args: ILoadArgs): Promise<IEventAndProfile[]> {
|
||||
return this.ipc.call("loadFileEvents", args);
|
||||
}
|
||||
|
||||
|
@ -28,9 +28,9 @@ import Favicon from "../../favicon";
|
||||
* Vector-specific extensions to the BasePlatform template
|
||||
*/
|
||||
export default abstract class VectorBasePlatform extends BasePlatform {
|
||||
protected _favicon: Favicon;
|
||||
protected _favicon?: Favicon;
|
||||
|
||||
public async getConfig(): Promise<IConfigOptions> {
|
||||
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||
return getVectorConfig();
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,8 @@ function getNormalizedAppVersion(version: string): string {
|
||||
}
|
||||
|
||||
export default class WebPlatform extends VectorBasePlatform {
|
||||
private static readonly VERSION = process.env.VERSION!; // baked in by Webpack
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
// Register service worker if available on this platform
|
||||
@ -101,7 +103,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
public getAppVersion(): Promise<string> {
|
||||
return Promise.resolve(getNormalizedAppVersion(process.env.VERSION));
|
||||
return Promise.resolve(getNormalizedAppVersion(WebPlatform.VERSION));
|
||||
}
|
||||
|
||||
public startUpdater(): void {
|
||||
@ -113,7 +115,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
//
|
||||
// Ideally, loading an old copy would be impossible with the
|
||||
// cache-control: nocache HTTP header set, but Firefox doesn't always obey it :/
|
||||
console.log("startUpdater, current version is " + getNormalizedAppVersion(process.env.VERSION));
|
||||
console.log("startUpdater, current version is " + getNormalizedAppVersion(WebPlatform.VERSION));
|
||||
this.pollForUpdate((version: string, newVersion: string) => {
|
||||
const query = parseQs(location);
|
||||
if (query.updated) {
|
||||
@ -144,7 +146,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
): Promise<UpdateStatus> => {
|
||||
return this.getMostRecentVersion().then(
|
||||
(mostRecentVersion) => {
|
||||
const currentVersion = getNormalizedAppVersion(process.env.VERSION);
|
||||
const currentVersion = getNormalizedAppVersion(WebPlatform.VERSION);
|
||||
|
||||
if (currentVersion !== mostRecentVersion) {
|
||||
if (this.shouldShowUpdate(mostRecentVersion)) {
|
||||
|
@ -22,7 +22,7 @@ import MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixCha
|
||||
|
||||
import { parseQsFromFragment } from "./url_utils";
|
||||
|
||||
let lastLocationHashSet: string = null;
|
||||
let lastLocationHashSet: string | null = null;
|
||||
|
||||
export function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||
const fragparts = parseQsFromFragment(location);
|
||||
|
@ -25,10 +25,11 @@ import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
|
||||
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
|
||||
import dis from "matrix-react-sdk/src/dispatcher/dispatcher";
|
||||
import MockHttpBackend from "matrix-mock-request";
|
||||
import { makeType } from "matrix-react-sdk/src/utils/TypeUtils";
|
||||
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
|
||||
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
|
||||
import { QueryDict, sleep } from "matrix-js-sdk/src/utils";
|
||||
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
import { ActionPayload } from "matrix-react-sdk/src/dispatcher/payloads";
|
||||
|
||||
import "../jest-mocks";
|
||||
import WebPlatform from "../../src/vector/platform/WebPlatform";
|
||||
@ -39,29 +40,24 @@ const DEFAULT_HS_URL = "http://my_server";
|
||||
const DEFAULT_IS_URL = "http://my_is";
|
||||
|
||||
describe("loading:", function () {
|
||||
let parentDiv;
|
||||
let httpBackend;
|
||||
let httpBackend: MockHttpBackend;
|
||||
|
||||
// an Object simulating the window.location
|
||||
let windowLocation;
|
||||
let windowLocation: Location | undefined;
|
||||
|
||||
// the mounted MatrixChat
|
||||
let matrixChat: RenderResult;
|
||||
let matrixChat: RenderResult | undefined;
|
||||
|
||||
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
|
||||
let tokenLoginCompletePromise;
|
||||
let tokenLoginCompletePromise: Promise<void> | undefined;
|
||||
|
||||
beforeEach(function () {
|
||||
httpBackend = new MockHttpBackend();
|
||||
// @ts-ignore
|
||||
window.fetch = httpBackend.fetchFn;
|
||||
parentDiv = document.createElement("div");
|
||||
|
||||
// uncomment this to actually add the div to the UI, to help with
|
||||
// debugging (but slow things down)
|
||||
// document.body.appendChild(parentDiv);
|
||||
|
||||
windowLocation = null;
|
||||
matrixChat = null;
|
||||
windowLocation = undefined;
|
||||
matrixChat = undefined;
|
||||
});
|
||||
|
||||
afterEach(async function () {
|
||||
@ -81,8 +77,13 @@ describe("loading:", function () {
|
||||
* TODO: it would be nice to factor some of this stuff out of index.js so
|
||||
* that we can test it rather than our own implementation of it.
|
||||
*/
|
||||
function loadApp(opts?): void {
|
||||
opts = opts || {};
|
||||
function loadApp(
|
||||
opts: {
|
||||
queryString?: string;
|
||||
uriFragment?: string;
|
||||
config?: IConfigOptions;
|
||||
} = {},
|
||||
): void {
|
||||
const queryString = opts.queryString || "";
|
||||
const uriFragment = opts.uriFragment || "";
|
||||
|
||||
@ -92,18 +93,18 @@ describe("loading:", function () {
|
||||
toString: function (): string {
|
||||
return this.search + this.hash;
|
||||
},
|
||||
};
|
||||
} as Location;
|
||||
|
||||
function onNewScreen(screen): void {
|
||||
function onNewScreen(screen: string): void {
|
||||
console.log(Date.now() + " newscreen " + screen);
|
||||
const hash = "#/" + screen;
|
||||
windowLocation.hash = hash;
|
||||
windowLocation!.hash = hash;
|
||||
console.log(Date.now() + " browser URI now " + windowLocation);
|
||||
}
|
||||
|
||||
// Parse the given window.location and return parameters that can be used when calling
|
||||
// MatrixChat.showScreen(screen, params)
|
||||
function getScreenFromLocation(location): { screen: string; params: QueryDict } {
|
||||
function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||
const fragparts = parseQsFromFragment(location);
|
||||
return {
|
||||
screen: fragparts.location.substring(1),
|
||||
@ -113,22 +114,20 @@ describe("loading:", function () {
|
||||
|
||||
const fragParts = parseQsFromFragment(windowLocation);
|
||||
|
||||
const config = Object.assign(
|
||||
{
|
||||
const config = {
|
||||
default_hs_url: DEFAULT_HS_URL,
|
||||
default_is_url: DEFAULT_IS_URL,
|
||||
validated_server_config: makeType(ValidatedServerConfig, {
|
||||
validated_server_config: {
|
||||
hsUrl: DEFAULT_HS_URL,
|
||||
hsName: "TEST_ENVIRONMENT",
|
||||
hsNameIsDifferent: false, // yes, we lie
|
||||
isUrl: DEFAULT_IS_URL,
|
||||
}),
|
||||
embeddedPages: {
|
||||
homeUrl: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
|
||||
} as ValidatedServerConfig,
|
||||
embedded_pages: {
|
||||
home_url: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
|
||||
},
|
||||
},
|
||||
opts.config || {},
|
||||
);
|
||||
...(opts.config ?? {}),
|
||||
} as IConfigOptions;
|
||||
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
|
||||
@ -138,18 +137,16 @@ describe("loading:", function () {
|
||||
matrixChat = render(
|
||||
<MatrixChat
|
||||
onNewScreen={onNewScreen}
|
||||
config={config}
|
||||
serverConfig={config.validated_server_config}
|
||||
config={config!}
|
||||
realQueryParams={params}
|
||||
startingFragmentQueryParams={fragParts.params}
|
||||
enableGuest={true}
|
||||
onTokenLoginCompleted={resolve}
|
||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
|
||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation!)}
|
||||
makeRegistrationUrl={(): string => {
|
||||
throw new Error("Not implemented");
|
||||
}}
|
||||
/>,
|
||||
parentDiv,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -158,15 +155,15 @@ describe("loading:", function () {
|
||||
// http requests until we do.
|
||||
//
|
||||
// returns a promise resolving to the received request
|
||||
async function expectAndAwaitSync(opts?): Promise<any> {
|
||||
let syncRequest = null;
|
||||
async function expectAndAwaitSync(opts?: { isGuest?: boolean }): Promise<any> {
|
||||
let syncRequest: (typeof MockHttpBackend.prototype.requests)[number] | null = null;
|
||||
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
|
||||
versions: ["r0.3.0"],
|
||||
unstable_features: {
|
||||
"m.lazy_load_members": true,
|
||||
},
|
||||
});
|
||||
const isGuest = opts && opts.isGuest;
|
||||
const isGuest = opts?.isGuest;
|
||||
if (!isGuest) {
|
||||
// the call to create the LL filter
|
||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" });
|
||||
@ -184,7 +181,7 @@ describe("loading:", function () {
|
||||
if (syncRequest) {
|
||||
return syncRequest;
|
||||
}
|
||||
await httpBackend.flush();
|
||||
await httpBackend.flush(undefined);
|
||||
}
|
||||
throw new Error("Gave up waiting for /sync");
|
||||
}
|
||||
@ -202,18 +199,18 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(403, "Guest access is disabled");
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
return awaitWelcomeComponent(matrixChat);
|
||||
})
|
||||
.then(() => {
|
||||
return waitFor(() => expect(windowLocation.hash).toEqual("#/welcome"));
|
||||
return waitFor(() => expect(windowLocation?.hash).toEqual("#/welcome"));
|
||||
});
|
||||
});
|
||||
|
||||
@ -235,11 +232,11 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(403, "Guest access is disabled");
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
@ -249,7 +246,7 @@ describe("loading:", function () {
|
||||
return moveFromWelcomeToLogin(matrixChat);
|
||||
})
|
||||
.then(() => {
|
||||
return completeLogin(matrixChat);
|
||||
return completeLogin(matrixChat!);
|
||||
})
|
||||
.then(() => {
|
||||
// once the sync completes, we should have a room view
|
||||
@ -257,7 +254,7 @@ describe("loading:", function () {
|
||||
})
|
||||
.then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
||||
expect(windowLocation?.hash).toEqual("#/room/!room:id");
|
||||
|
||||
// and the localstorage should have been updated
|
||||
expect(localStorage.getItem("mx_user_id")).toEqual("@user:id");
|
||||
@ -294,11 +291,11 @@ describe("loading:", function () {
|
||||
|
||||
throw new Error(`Unexpected HTTP request to ${req}`);
|
||||
}
|
||||
return completeLogin(matrixChat);
|
||||
return completeLogin(matrixChat!);
|
||||
})
|
||||
.then(() => {
|
||||
expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy();
|
||||
expect(windowLocation.hash).toEqual("#/home");
|
||||
expect(matrixChat?.container.querySelector(".mx_HomePage")).toBeTruthy();
|
||||
expect(windowLocation?.hash).toEqual("#/home");
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -319,7 +316,7 @@ describe("loading:", function () {
|
||||
it("shows the last known room by default", function () {
|
||||
loadApp();
|
||||
|
||||
return awaitLoggedIn(matrixChat)
|
||||
return awaitLoggedIn(matrixChat!)
|
||||
.then(() => {
|
||||
// we are logged in - let the sync complete
|
||||
return expectAndAwaitSync();
|
||||
@ -330,7 +327,7 @@ describe("loading:", function () {
|
||||
})
|
||||
.then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(windowLocation.hash).toEqual("#/room/!last_room:id");
|
||||
expect(windowLocation?.hash).toEqual("#/room/!last_room:id");
|
||||
});
|
||||
});
|
||||
|
||||
@ -339,7 +336,7 @@ describe("loading:", function () {
|
||||
|
||||
loadApp();
|
||||
|
||||
return awaitLoggedIn(matrixChat)
|
||||
return awaitLoggedIn(matrixChat!)
|
||||
.then(() => {
|
||||
// we are logged in - let the sync complete
|
||||
return expectAndAwaitSync();
|
||||
@ -347,8 +344,8 @@ describe("loading:", function () {
|
||||
.then(() => {
|
||||
// once the sync completes, we should have a home page
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy();
|
||||
expect(windowLocation.hash).toEqual("#/home");
|
||||
expect(matrixChat?.container.querySelector(".mx_HomePage")).toBeTruthy();
|
||||
expect(windowLocation?.hash).toEqual("#/home");
|
||||
});
|
||||
});
|
||||
|
||||
@ -357,7 +354,7 @@ describe("loading:", function () {
|
||||
uriFragment: "#/room/!room:id",
|
||||
});
|
||||
|
||||
return awaitLoggedIn(matrixChat)
|
||||
return awaitLoggedIn(matrixChat!)
|
||||
.then(() => {
|
||||
// we are logged in - let the sync complete
|
||||
return expectAndAwaitSync();
|
||||
@ -368,7 +365,7 @@ describe("loading:", function () {
|
||||
})
|
||||
.then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
||||
expect(windowLocation?.hash).toEqual("#/room/!room:id");
|
||||
});
|
||||
});
|
||||
|
||||
@ -404,17 +401,17 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat);
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
})
|
||||
.then(() => {
|
||||
// we are logged in - let the sync complete
|
||||
@ -423,8 +420,8 @@ describe("loading:", function () {
|
||||
.then(() => {
|
||||
// once the sync completes, we should have a welcome page
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy();
|
||||
expect(windowLocation.hash).toEqual("#/welcome");
|
||||
expect(matrixChat?.container.querySelector(".mx_Welcome")).toBeTruthy();
|
||||
expect(windowLocation?.hash).toEqual("#/welcome");
|
||||
});
|
||||
});
|
||||
|
||||
@ -441,17 +438,17 @@ describe("loading:", function () {
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat);
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
})
|
||||
.then(() => {
|
||||
return expectAndAwaitSync({ isGuest: true });
|
||||
@ -461,8 +458,8 @@ describe("loading:", function () {
|
||||
|
||||
// once the sync completes, we should have a welcome page
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy();
|
||||
expect(windowLocation.hash).toEqual("#/welcome");
|
||||
expect(matrixChat?.container.querySelector(".mx_Welcome")).toBeTruthy();
|
||||
expect(windowLocation?.hash).toEqual("#/welcome");
|
||||
expect(MatrixClientPeg.get().baseUrl).toEqual(DEFAULT_HS_URL);
|
||||
expect(MatrixClientPeg.get().idBaseUrl).toEqual(DEFAULT_IS_URL);
|
||||
});
|
||||
@ -481,17 +478,17 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat);
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
})
|
||||
.then(() => {
|
||||
return expectAndAwaitSync({ isGuest: true });
|
||||
@ -502,7 +499,7 @@ describe("loading:", function () {
|
||||
})
|
||||
.then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
expect(windowLocation.hash).toEqual("#/room/!room:id");
|
||||
expect(windowLocation?.hash).toEqual("#/room/!room:id");
|
||||
});
|
||||
});
|
||||
|
||||
@ -514,7 +511,7 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
@ -522,9 +519,9 @@ describe("loading:", function () {
|
||||
});
|
||||
|
||||
return httpBackend
|
||||
.flush()
|
||||
.flush(undefined)
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat);
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
})
|
||||
.then(() => {
|
||||
// we got a sync spinner - let the sync complete
|
||||
@ -532,7 +529,7 @@ describe("loading:", function () {
|
||||
})
|
||||
.then(async () => {
|
||||
// once the sync completes, we should have a home page
|
||||
await waitFor(() => matrixChat.container.querySelector(".mx_HomePage"));
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_HomePage"));
|
||||
|
||||
// we simulate a click on the 'login' button by firing off
|
||||
// the relevant dispatch.
|
||||
@ -554,7 +551,7 @@ describe("loading:", function () {
|
||||
await screen.findByRole("main");
|
||||
screen.getAllByText("Sign in");
|
||||
|
||||
expect(windowLocation.hash).toEqual("#/login");
|
||||
expect(windowLocation?.hash).toEqual("#/login");
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -585,7 +582,7 @@ describe("loading:", function () {
|
||||
access_token: "access_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
// at this point, MatrixChat should fire onTokenLoginCompleted, which
|
||||
@ -612,11 +609,7 @@ describe("loading:", function () {
|
||||
// When we switch to the login component, it'll hit the login endpoint
|
||||
// for proof of life and to get flows. We'll only give it one option.
|
||||
httpBackend.when("GET", "/login").respond(200, { flows: [{ type: "m.login.password" }] });
|
||||
httpBackend.flush(); // We already would have tried the GET /login request
|
||||
|
||||
// Give the component some time to finish processing the login flows before
|
||||
// continuing.
|
||||
await sleep(100);
|
||||
httpBackend.flush(undefined); // We already would have tried the GET /login request
|
||||
|
||||
httpBackend
|
||||
.when("POST", "/login")
|
||||
@ -631,12 +624,17 @@ describe("loading:", function () {
|
||||
device_id: "DEVICE_ID",
|
||||
access_token: "access_token",
|
||||
});
|
||||
fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_username"), { target: { value: "user" } });
|
||||
fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_password"), { target: { value: "pass" } });
|
||||
|
||||
// Give the component some time to finish processing the login flows before continuing.
|
||||
await waitFor(() => expect(matrixChat?.container.querySelector("#mx_LoginForm_username")).toBeTruthy());
|
||||
|
||||
// Enter login details
|
||||
fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_username")!, { target: { value: "user" } });
|
||||
fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_password")!, { target: { value: "pass" } });
|
||||
fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" }));
|
||||
|
||||
return httpBackend
|
||||
.flush()
|
||||
.flush(undefined)
|
||||
.then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
return sleep(1);
|
||||
@ -661,7 +659,7 @@ async function awaitLoggedIn(matrixChat: RenderResult): Promise<void> {
|
||||
if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const onAction = ({ action }): void => {
|
||||
const onAction = ({ action }: ActionPayload): void => {
|
||||
if (action !== "on_logged_in") {
|
||||
return;
|
||||
}
|
||||
@ -674,19 +672,19 @@ async function awaitLoggedIn(matrixChat: RenderResult): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
async function awaitRoomView(matrixChat: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat.container.querySelector(".mx_RoomView"));
|
||||
async function awaitRoomView(matrixChat?: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_RoomView"));
|
||||
}
|
||||
|
||||
async function awaitLoginComponent(matrixChat: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat.container.querySelector(".mx_AuthPage"));
|
||||
async function awaitLoginComponent(matrixChat?: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_AuthPage"));
|
||||
}
|
||||
|
||||
async function awaitWelcomeComponent(matrixChat: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat.container.querySelector(".mx_Welcome"));
|
||||
async function awaitWelcomeComponent(matrixChat?: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_Welcome"));
|
||||
}
|
||||
|
||||
function moveFromWelcomeToLogin(matrixChat: RenderResult): Promise<void> {
|
||||
function moveFromWelcomeToLogin(matrixChat?: RenderResult): Promise<void> {
|
||||
dis.dispatch({ action: "start_login" });
|
||||
return awaitLoginComponent(matrixChat);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ describe("Favicon", () => {
|
||||
it("should create a link element if one doesn't yet exist", () => {
|
||||
const favicon = new Favicon();
|
||||
expect(favicon).toBeTruthy();
|
||||
const link = window.document.querySelector("link");
|
||||
const link = window.document.querySelector("link")!;
|
||||
expect(link.rel).toContain("icon");
|
||||
});
|
||||
|
||||
|
@ -21,7 +21,6 @@ import { getVectorConfig } from "../../../src/vector/getconfig";
|
||||
fetchMock.config.overwriteRoutes = true;
|
||||
|
||||
describe("getVectorConfig()", () => {
|
||||
const prevDocumentDomain = document.domain;
|
||||
const elementDomain = "app.element.io";
|
||||
const now = 1234567890;
|
||||
const specificConfig = {
|
||||
@ -32,7 +31,10 @@ describe("getVectorConfig()", () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
document.domain = elementDomain;
|
||||
Object.defineProperty(window, "location", {
|
||||
value: { href: `https://${elementDomain}`, hostname: elementDomain },
|
||||
writable: true,
|
||||
});
|
||||
|
||||
// stable value for cachebuster
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
@ -41,7 +43,6 @@ describe("getVectorConfig()", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
document.domain = prevDocumentDomain;
|
||||
jest.spyOn(Date, "now").mockRestore();
|
||||
});
|
||||
|
||||
@ -107,6 +108,6 @@ describe("getVectorConfig()", () => {
|
||||
|
||||
// We can't assert it'll be a SyntaxError as node-fetch behaves differently
|
||||
// https://github.com/wheresrhys/fetch-mock/issues/270
|
||||
await expect(getVectorConfig()).rejects.toThrow("Unexpected token } in JSON at position 19");
|
||||
await expect(getVectorConfig()).rejects.toThrow("in JSON at position 19");
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,7 @@ import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform";
|
||||
import { Action } from "matrix-react-sdk/src/dispatcher/actions";
|
||||
import dispatcher from "matrix-react-sdk/src/dispatcher/dispatcher";
|
||||
import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake";
|
||||
import { BreadcrumbsStore } from "matrix-react-sdk/src/stores/BreadcrumbsStore";
|
||||
|
||||
import ElectronPlatform from "../../../../src/vector/platform/ElectronPlatform";
|
||||
|
||||
@ -43,12 +44,10 @@ describe("ElectronPlatform", () => {
|
||||
const userId = "@alice:server.org";
|
||||
const deviceId = "device-id";
|
||||
|
||||
window.electron = mockElectron;
|
||||
beforeEach(() => {
|
||||
window.electron = mockElectron;
|
||||
jest.clearAllMocks();
|
||||
delete window.navigator;
|
||||
window.navigator = { userAgent: defaultUserAgent } as unknown as Navigator;
|
||||
Object.defineProperty(window, "navigator", { value: { userAgent: defaultUserAgent }, writable: true });
|
||||
});
|
||||
|
||||
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
|
||||
@ -56,7 +55,7 @@ describe("ElectronPlatform", () => {
|
||||
|
||||
it("flushes rageshake before quitting", () => {
|
||||
new ElectronPlatform();
|
||||
const [event, handler] = getElectronEventHandlerCall("before-quit");
|
||||
const [event, handler] = getElectronEventHandlerCall("before-quit")!;
|
||||
// correct event bound
|
||||
expect(event).toBeTruthy();
|
||||
|
||||
@ -68,7 +67,7 @@ describe("ElectronPlatform", () => {
|
||||
|
||||
it("dispatches view settings action on preferences event", () => {
|
||||
new ElectronPlatform();
|
||||
const [event, handler] = getElectronEventHandlerCall("preferences");
|
||||
const [event, handler] = getElectronEventHandlerCall("preferences")!;
|
||||
// correct event bound
|
||||
expect(event).toBeTruthy();
|
||||
|
||||
@ -80,7 +79,7 @@ describe("ElectronPlatform", () => {
|
||||
describe("updates", () => {
|
||||
it("dispatches on check updates action", () => {
|
||||
new ElectronPlatform();
|
||||
const [event, handler] = getElectronEventHandlerCall("check_updates");
|
||||
const [event, handler] = getElectronEventHandlerCall("check_updates")!;
|
||||
// correct event bound
|
||||
expect(event).toBeTruthy();
|
||||
|
||||
@ -93,7 +92,7 @@ describe("ElectronPlatform", () => {
|
||||
|
||||
it("dispatches on check updates action when update not available", () => {
|
||||
new ElectronPlatform();
|
||||
const [, handler] = getElectronEventHandlerCall("check_updates");
|
||||
const [, handler] = getElectronEventHandlerCall("check_updates")!;
|
||||
|
||||
handler({}, false);
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
@ -138,8 +137,7 @@ describe("ElectronPlatform", () => {
|
||||
["Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: SunOS"],
|
||||
["custom user agent", "Element Desktop: Unknown"],
|
||||
])("%s = %s", (userAgent, result) => {
|
||||
delete window.navigator;
|
||||
window.navigator = { userAgent } as unknown as Navigator;
|
||||
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
|
||||
const platform = new ElectronPlatform();
|
||||
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
||||
});
|
||||
@ -260,4 +258,20 @@ describe("ElectronPlatform", () => {
|
||||
expect(mockElectron.send).toHaveBeenCalledWith("install_update");
|
||||
});
|
||||
});
|
||||
|
||||
describe("breacrumbs", () => {
|
||||
it("should send breadcrumb updates over the IPC", () => {
|
||||
const spy = jest.spyOn(BreadcrumbsStore.instance, "on");
|
||||
new ElectronPlatform();
|
||||
const cb = spy.mock.calls[0][1];
|
||||
cb();
|
||||
|
||||
expect(mockElectron.send).toHaveBeenCalledWith(
|
||||
"ipcCall",
|
||||
expect.objectContaining({
|
||||
name: "breadcrumbs",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -33,7 +33,6 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("registers service worker", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - mocking readonly object
|
||||
navigator.serviceWorker = { register: jest.fn() };
|
||||
new WebPlatform();
|
||||
@ -41,10 +40,7 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("should call reload on window location object", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
reload: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
|
||||
|
||||
const platform = new WebPlatform();
|
||||
expect(window.location.reload).not.toHaveBeenCalled();
|
||||
@ -53,10 +49,7 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("should call reload to install update", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
reload: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
|
||||
|
||||
const platform = new WebPlatform();
|
||||
expect(window.location.reload).not.toHaveBeenCalled();
|
||||
@ -73,10 +66,8 @@ describe("WebPlatform", () => {
|
||||
"develop.element.io: Chrome on macOS",
|
||||
],
|
||||
])("%s & %s = %s", (url, userAgent, result) => {
|
||||
delete window.navigator;
|
||||
window.navigator = { userAgent } as unknown as Navigator;
|
||||
delete window.location;
|
||||
window.location = { href: url } as unknown as Location;
|
||||
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
|
||||
Object.defineProperty(window, "location", { value: { href: url }, writable: true });
|
||||
const platform = new WebPlatform();
|
||||
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
||||
});
|
||||
@ -88,14 +79,12 @@ describe("WebPlatform", () => {
|
||||
permission: "notGranted",
|
||||
};
|
||||
beforeEach(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Notification = mockNotification;
|
||||
mockNotification.permission = "notGranted";
|
||||
});
|
||||
|
||||
it("supportsNotifications returns false when platform does not support notifications", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Notification = undefined;
|
||||
expect(new WebPlatform().supportsNotifications()).toBe(false);
|
||||
@ -132,7 +121,8 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.VERSION = envVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = envVersion;
|
||||
});
|
||||
|
||||
it("should return true from canSelfUpdate()", async () => {
|
||||
@ -142,18 +132,21 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("getAppVersion returns normalized app version", async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = prodVersion;
|
||||
const platform = new WebPlatform();
|
||||
|
||||
const version = await platform.getAppVersion();
|
||||
expect(version).toEqual(prodVersion);
|
||||
|
||||
process.env.VERSION = `v${prodVersion}`;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = `v${prodVersion}`;
|
||||
const version2 = await platform.getAppVersion();
|
||||
// v prefix removed
|
||||
expect(version2).toEqual(prodVersion);
|
||||
|
||||
process.env.VERSION = `version not like semver`;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = `version not like semver`;
|
||||
const notSemverVersion = await platform.getAppVersion();
|
||||
expect(notSemverVersion).toEqual(`version not like semver`);
|
||||
});
|
||||
@ -163,7 +156,8 @@ describe("WebPlatform", () => {
|
||||
"should return not available and call showNoUpdate when current version " +
|
||||
"matches most recent version",
|
||||
async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = prodVersion;
|
||||
fetchMock.getOnce("/version", prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
@ -178,7 +172,8 @@ describe("WebPlatform", () => {
|
||||
);
|
||||
|
||||
it("should strip v prefix from versions before comparing", async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = prodVersion;
|
||||
fetchMock.getOnce("/version", `v${prodVersion}`);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
@ -195,7 +190,8 @@ describe("WebPlatform", () => {
|
||||
it(
|
||||
"should return ready and call showUpdate when current version " + "differs from most recent version",
|
||||
async () => {
|
||||
process.env.VERSION = "0.0.0"; // old version
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = "0.0.0"; // old version
|
||||
fetchMock.getOnce("/version", prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
@ -210,7 +206,8 @@ describe("WebPlatform", () => {
|
||||
);
|
||||
|
||||
it("should return ready without showing update when user registered in last 24", async () => {
|
||||
process.env.VERSION = "0.0.0"; // old version
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = "0.0.0"; // old version
|
||||
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
|
||||
fetchMock.getOnce("/version", prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
@ -18,24 +18,28 @@ import { onNewScreen } from "../../../src/vector/routing";
|
||||
|
||||
describe("onNewScreen", () => {
|
||||
it("should replace history if stripping via fields", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
} as unknown as Location;
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
onNewScreen("room/!room:server");
|
||||
expect(window.location.assign).not.toHaveBeenCalled();
|
||||
expect(window.location.replace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not replace history if changing rooms", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room1:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
} as unknown as Location;
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
onNewScreen("room/!room2:server");
|
||||
expect(window.location.assign).toHaveBeenCalled();
|
||||
expect(window.location.replace).not.toHaveBeenCalled();
|
||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
|
||||
|
||||
describe("url_utils.ts", function () {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const location: Location = {
|
||||
hash: "",
|
||||
|
@ -14,7 +14,20 @@
|
||||
"outDir": "./lib",
|
||||
"declaration": true,
|
||||
"jsx": "react",
|
||||
"lib": ["es2019", "dom", "dom.iterable"]
|
||||
"lib": ["es2020", "dom", "dom.iterable"],
|
||||
"alwaysStrict": true,
|
||||
"strictBindCallApply": true,
|
||||
"noImplicitThis": true
|
||||
},
|
||||
"include": ["./src/**/*.ts", "./src/**/*.tsx", "./test/**/*.ts", "./test/**/*.tsx"]
|
||||
"include": [
|
||||
"./node_modules/matrix-js-sdk/src/@types/*.d.ts",
|
||||
"./node_modules/matrix-react-sdk/src/@types/diff-dom.d.ts",
|
||||
"./node_modules/matrix-react-sdk/src/@types/opus-recorder.d.ts",
|
||||
"./node_modules/matrix-react-sdk/src/@types/png-chunks-extract.d.ts",
|
||||
"./node_modules/matrix-react-sdk/src/@types/sanitize-html.d.ts",
|
||||
"./src/**/*.ts",
|
||||
"./src/**/*.tsx",
|
||||
"./test/**/*.ts",
|
||||
"./test/**/*.tsx"
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"jsx": "preserve",
|
||||
"declaration": false,
|
||||
"outDir": "./lib/module_system",
|
||||
"lib": ["es2019"],
|
||||
"lib": ["es2020"],
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["./module_system/**/*.ts"]
|
||||
|
@ -99,8 +99,8 @@ module.exports = (env, argv) => {
|
||||
|
||||
const development = {};
|
||||
if (devMode) {
|
||||
// High quality, embedded source maps for dev builds
|
||||
development["devtool"] = "eval-source-map";
|
||||
// Embedded source maps for dev builds, can't use eval-source-map due to CSP
|
||||
development["devtool"] = "inline-source-map";
|
||||
} else {
|
||||
if (process.env.CI_PACKAGE) {
|
||||
// High quality source maps in separate .map files which include the source. This doesn't bulk up the .js
|
||||
|
Loading…
Reference in New Issue
Block a user