Compare commits
200 Commits
v1.11.26
...
t3chguy/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83cd189984 | ||
|
|
0a5173f258 | ||
|
|
a68c535eb8 | ||
|
|
11a8fb31a4 | ||
|
|
bb52c1311a | ||
|
|
23bfce81a4 | ||
|
|
122d4d3503 | ||
|
|
88c5395349 | ||
|
|
d01eabd340 | ||
|
|
d7a98fe392 | ||
|
|
9457af27f6 | ||
|
|
cc5914a7dc | ||
|
|
1e7d001cd3 | ||
|
|
6e73a2b01c | ||
|
|
8274ad6394 | ||
|
|
df8f8415bf | ||
|
|
e902a0bd59 | ||
|
|
81397ffbf4 | ||
|
|
96cf7053bc | ||
|
|
b25a7ef505 | ||
|
|
99b68eedae | ||
|
|
aa1538ea09 | ||
|
|
1941097fed | ||
|
|
b633be382e | ||
|
|
9860f4befc | ||
|
|
349f7ee27a | ||
|
|
17c65f1a0c | ||
|
|
1fb03c82fe | ||
|
|
f00c742ebc | ||
|
|
9e6467c684 | ||
|
|
c0e070934a | ||
|
|
5f9baed93e | ||
|
|
2bcfb257ea | ||
|
|
87a2060282 | ||
|
|
09bfb8ad2a | ||
|
|
024ad5311f | ||
|
|
71c491db62 | ||
|
|
0ae3cfdcdd | ||
|
|
15ef05791f | ||
|
|
152923306f | ||
|
|
86ab757571 | ||
|
|
a722b71b6c | ||
|
|
6e8f76e93c | ||
|
|
ed15578eed | ||
|
|
f5452fb097 | ||
|
|
2b17090855 | ||
|
|
afab95288e | ||
|
|
59b6285458 | ||
|
|
1709b3e9ba | ||
|
|
3eddc939ce | ||
|
|
e558661a77 | ||
|
|
506a1b6dc7 | ||
|
|
0035580aef | ||
|
|
f59d4c9574 | ||
|
|
ba5e8670a4 | ||
|
|
50d44864b0 | ||
|
|
2dc54ec80b | ||
|
|
f5b8bccb65 | ||
|
|
a2da1eb79d | ||
|
|
40f95a6c06 | ||
|
|
ece117b33f | ||
|
|
66c86a6397 | ||
|
|
a0791ff48f | ||
|
|
1a04705960 | ||
|
|
2dc3f523aa | ||
|
|
ac33b4a428 | ||
|
|
5557487ef2 | ||
|
|
fa4d1addd3 | ||
|
|
77074cce44 | ||
|
|
e447f6d6dd | ||
|
|
22996fd55a | ||
|
|
b3b834cab2 | ||
|
|
b31aa17253 | ||
|
|
f7c9bdcd97 | ||
|
|
fc49190efe | ||
|
|
acc9b4c1d4 | ||
|
|
447b9dc661 | ||
|
|
38dbd66474 | ||
|
|
3015b90a6e | ||
|
|
a1176ce66d | ||
|
|
c0ba077d06 | ||
|
|
586309e851 | ||
|
|
decbd0aa04 | ||
|
|
cdabefa285 | ||
|
|
8b1d3a1265 | ||
|
|
dcb9dec749 | ||
|
|
b9fd456bf4 | ||
|
|
c1371fee27 | ||
|
|
a4b10a8766 | ||
|
|
80b9f7baab | ||
|
|
d26d56e579 | ||
|
|
ddc65d5cff | ||
|
|
22e3ebd89a | ||
|
|
8a54f74589 | ||
|
|
1ebe574ab1 | ||
|
|
0714d74956 | ||
|
|
6cfa1eea7b | ||
|
|
892ee3a707 | ||
|
|
2df33458a1 | ||
|
|
27dc1fb1ef | ||
|
|
88ade4a995 | ||
|
|
7899cd3077 | ||
|
|
3182508238 | ||
|
|
1237e73c1b | ||
|
|
04f2a55500 | ||
|
|
04cf98d0e5 | ||
|
|
1a34ded0a1 | ||
|
|
79de2f6262 | ||
|
|
508d562040 | ||
|
|
12a380a08e | ||
|
|
407891073d | ||
|
|
8df1d67a83 | ||
|
|
6d241c8ce0 | ||
|
|
34662d13c8 | ||
|
|
a79fba39de | ||
|
|
7b191b90df | ||
|
|
472518fcc9 | ||
|
|
4cf671d005 | ||
|
|
ddc2932541 | ||
|
|
ea6056fa59 | ||
|
|
b3c88f7a16 | ||
|
|
95f166ad61 | ||
|
|
1a7c5a294b | ||
|
|
569e5cb0c1 | ||
|
|
f2160391c2 | ||
|
|
62fa62dc3a | ||
|
|
e0aa7754b1 | ||
|
|
89054a50ac | ||
|
|
f833c57348 | ||
|
|
1a63a7ff2e | ||
|
|
a66df9d434 | ||
|
|
c0cd678542 | ||
|
|
b9837cc771 | ||
|
|
9faa625551 | ||
|
|
ebda5f4d31 | ||
|
|
145564719f | ||
|
|
166d88058c | ||
|
|
60d5ab427a | ||
|
|
bcc5bd8e54 | ||
|
|
b9b52c8c06 | ||
|
|
3210ff2e9c | ||
|
|
7cbd8e04c2 | ||
|
|
86c2cb7649 | ||
|
|
84242fc81f | ||
|
|
48e845575a | ||
|
|
39061f9b8c | ||
|
|
41d025bf48 | ||
|
|
581beee0b2 | ||
|
|
9ec10a274e | ||
|
|
c848295b8b | ||
|
|
b472f34d63 | ||
|
|
dfaeb6368b | ||
|
|
f033becebb | ||
|
|
f10171b6bc | ||
|
|
dcc0cb74fe | ||
|
|
389f55c261 | ||
|
|
1716a7f9ca | ||
|
|
2575322360 | ||
|
|
ab19f5654c | ||
|
|
b9b0b096a4 | ||
|
|
b391109469 | ||
|
|
50f8be4a62 | ||
|
|
d35f3df621 | ||
|
|
4121f72593 | ||
|
|
6a1a47aae1 | ||
|
|
4440315cfb | ||
|
|
b2a7d65fc9 | ||
|
|
c8b3f5d512 | ||
|
|
21a2a028a5 | ||
|
|
77e7bad4f1 | ||
|
|
292449369e | ||
|
|
e7be53fe85 | ||
|
|
310b478b75 | ||
|
|
86ab455b54 | ||
|
|
a66f5e7530 | ||
|
|
7059420be8 | ||
|
|
afe2ac2ae6 | ||
|
|
8122b85567 | ||
|
|
4a18ab6d1b | ||
|
|
714ba20b41 | ||
|
|
93d2112bfe | ||
|
|
e8bfd38865 | ||
|
|
f43e6fddfc | ||
|
|
b2f8875175 | ||
|
|
eb31df7d1d | ||
|
|
2b8a819d3d | ||
|
|
a36b76faf0 | ||
|
|
f9edd3668f | ||
|
|
f5bb7a2513 | ||
|
|
9371737104 | ||
|
|
764e1a01cc | ||
|
|
3d7d82ab2a | ||
|
|
9b571e12a7 | ||
|
|
bc785e9860 | ||
|
|
e2a9da1020 | ||
|
|
c85eb81ed0 | ||
|
|
23d496d0e8 | ||
|
|
6a4986ee06 | ||
|
|
296d1e2b75 | ||
|
|
2bfa2edd60 |
@@ -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
|
||||
|
||||
19
.github/workflows/build_develop.yml
vendored
19
.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,20 +68,25 @@ 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"
|
||||
done
|
||||
echo "/develop.tar.gz $R2_PUBLIC_URL/develop.tar.gz 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:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
|
||||
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@579f64ca0abced29dbbc44ab4c6a0b9e33ab3588 # 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"],
|
||||
};
|
||||
|
||||
182
CHANGELOG.md
182
CHANGELOG.md
@@ -1,9 +1,185 @@
|
||||
Changes in [1.11.26](https://github.com/vector-im/element-web/releases/tag/v1.11.26) (2023-03-28)
|
||||
Changes in [1.11.31](https://github.com/vector-im/element-web/releases/tag/v1.11.31) (2023-05-10)
|
||||
=================================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Improve Content-Security-Policy ([\#25210](https://github.com/vector-im/element-web/pull/25210)).
|
||||
* Add UIFeature.locationSharing to hide location sharing ([\#10727](https://github.com/matrix-org/matrix-react-sdk/pull/10727)).
|
||||
* Memoize field validation results ([\#10714](https://github.com/matrix-org/matrix-react-sdk/pull/10714)).
|
||||
* Commands for plain text editor ([\#10567](https://github.com/matrix-org/matrix-react-sdk/pull/10567)). Contributed by @alunturner.
|
||||
* Allow 16 lines of text in the rich text editors ([\#10670](https://github.com/matrix-org/matrix-react-sdk/pull/10670)). Contributed by @alunturner.
|
||||
* Bail out of `RoomSettingsDialog` when room is not found ([\#10662](https://github.com/matrix-org/matrix-react-sdk/pull/10662)). Contributed by @kerryarchibald.
|
||||
* Element-R: Populate device list for right-panel ([\#10671](https://github.com/matrix-org/matrix-react-sdk/pull/10671)). Contributed by @florianduros.
|
||||
* Make existing and new issue URLs configurable ([\#10710](https://github.com/matrix-org/matrix-react-sdk/pull/10710)). Fixes #24424.
|
||||
* Fix usages of ARIA tabpanel ([\#10628](https://github.com/matrix-org/matrix-react-sdk/pull/10628)). Fixes #25016.
|
||||
* Element-R: Starting a DMs with a user ([\#10673](https://github.com/matrix-org/matrix-react-sdk/pull/10673)). Contributed by @florianduros.
|
||||
* ARIA Accessibility improvements ([\#10675](https://github.com/matrix-org/matrix-react-sdk/pull/10675)).
|
||||
* ARIA Accessibility improvements ([\#10674](https://github.com/matrix-org/matrix-react-sdk/pull/10674)).
|
||||
* Add arrow key controls to emoji and reaction pickers ([\#10637](https://github.com/matrix-org/matrix-react-sdk/pull/10637)). Fixes #17189.
|
||||
* Translate credits in help about section ([\#10676](https://github.com/matrix-org/matrix-react-sdk/pull/10676)).
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix: reveal images when image previews are disabled ([\#10781](https://github.com/matrix-org/matrix-react-sdk/pull/10781)). Fixes #25271. Contributed by @kerryarchibald.
|
||||
* Fix autocomplete not resetting properly on message send ([\#10741](https://github.com/matrix-org/matrix-react-sdk/pull/10741)). Fixes #25170.
|
||||
* Fix start_sso not working with guests disabled ([\#10720](https://github.com/matrix-org/matrix-react-sdk/pull/10720)). Fixes #16624.
|
||||
* Fix soft crash with Element call widgets ([\#10684](https://github.com/matrix-org/matrix-react-sdk/pull/10684)).
|
||||
* Send correct receipt when marking a room as read ([\#10730](https://github.com/matrix-org/matrix-react-sdk/pull/10730)). Fixes #25207.
|
||||
* Offload some more waveform processing onto a worker ([\#9223](https://github.com/matrix-org/matrix-react-sdk/pull/9223)). Fixes #19756.
|
||||
* Consolidate login errors ([\#10722](https://github.com/matrix-org/matrix-react-sdk/pull/10722)). Fixes #17520.
|
||||
* Fix all rooms search generating permalinks to wrong room id ([\#10625](https://github.com/matrix-org/matrix-react-sdk/pull/10625)). Fixes #25115.
|
||||
* Posthog properly handle Analytics ID changing from under us ([\#10702](https://github.com/matrix-org/matrix-react-sdk/pull/10702)). Fixes #25187.
|
||||
* Fix Clock being read as an absolute time rather than duration ([\#10706](https://github.com/matrix-org/matrix-react-sdk/pull/10706)). Fixes #22582.
|
||||
* Properly translate errors in `ChangePassword.tsx` so they show up translated to the user but not in our logs ([\#10615](https://github.com/matrix-org/matrix-react-sdk/pull/10615)). Fixes #9597. Contributed by @MadLittleMods.
|
||||
* Honour feature toggles in guest mode ([\#10651](https://github.com/matrix-org/matrix-react-sdk/pull/10651)). Fixes #24513. Contributed by @andybalaam.
|
||||
* Fix default content in devtools event sender ([\#10699](https://github.com/matrix-org/matrix-react-sdk/pull/10699)). Contributed by @tulir.
|
||||
* Fix a crash when a call ends while you're in it ([\#10681](https://github.com/matrix-org/matrix-react-sdk/pull/10681)). Fixes #25153.
|
||||
* Fix lack of screen reader indication when triggering auto complete ([\#10664](https://github.com/matrix-org/matrix-react-sdk/pull/10664)). Fixes #11011.
|
||||
* Fix typing tile duplicating users ([\#10678](https://github.com/matrix-org/matrix-react-sdk/pull/10678)). Fixes #25165.
|
||||
* Fix wrong room topic tooltip position ([\#10667](https://github.com/matrix-org/matrix-react-sdk/pull/10667)). Fixes #25158.
|
||||
* Fix create subspace dialog not working ([\#10652](https://github.com/matrix-org/matrix-react-sdk/pull/10652)). Fixes #24882.
|
||||
|
||||
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
|
||||
* Changes for matrix-js-sdk v24.0.0
|
||||
* Changes for matrix-react-sdk v3.69.0
|
||||
* (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)
|
||||
=================================================================================================
|
||||
|
||||
@@ -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.
|
||||
@@ -15,7 +15,7 @@ WORKDIR /src
|
||||
|
||||
COPY . /src
|
||||
RUN dos2unix /src/scripts/docker-link-repos.sh && bash /src/scripts/docker-link-repos.sh
|
||||
RUN yarn --network-timeout=100000 install
|
||||
RUN yarn --network-timeout=200000 install
|
||||
|
||||
RUN dos2unix /src/scripts/docker-package.sh && bash /src/scripts/docker-package.sh
|
||||
|
||||
@@ -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;
|
||||
93
package.json
93
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.11.26",
|
||||
"version": "1.11.31",
|
||||
"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",
|
||||
@@ -58,21 +61,22 @@
|
||||
"lint:style": "stylelint \"res/css/**/*.pcss\"",
|
||||
"test": "jest",
|
||||
"coverage": "yarn test --coverage",
|
||||
"analyse:unused-exports": "node ./scripts/analyse_unused_exports.js"
|
||||
"analyse:unused-exports": "node ./scripts/analyse_unused_exports.js",
|
||||
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp"
|
||||
},
|
||||
"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": "24.0.0",
|
||||
"matrix-react-sdk": "3.69.0",
|
||||
"matrix-widget-api": "^1.1.1",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
|
||||
"matrix-widget-api": "^1.3.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"sanitize-html": "^2.3.2",
|
||||
@@ -100,12 +104,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 +119,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.40.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-deprecate": "^0.7.0",
|
||||
@@ -127,7 +131,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": "^47.0.0",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"fake-indexeddb": "^4.0.0",
|
||||
"fetch-mock-jest": "^1.5.1",
|
||||
@@ -135,17 +139,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,69 +163,28 @@
|
||||
"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-bundle-analyzer": "^4.8.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"worker-loader": "^3.0.0",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ we don't have an account and should hide them. No account == no guest account ei
|
||||
|
||||
<div class="mx_Parent">
|
||||
<a href="https://element.io" target="_blank" rel="noopener">
|
||||
<img src="welcome/images/logo.svg" alt="" class="mx_Logo" />
|
||||
<img src="$logoUrl" alt="" class="mx_Logo" />
|
||||
</a>
|
||||
<h1 class="mx_Header_title">_t("Welcome to Element")</h1>
|
||||
<!-- XXX: Our translations system isn't smart enough to recognize variables in the HTML, so we manually do it -->
|
||||
@@ -181,11 +181,6 @@ we don't have an account and should hide them. No account == no guest account ei
|
||||
<div class="mx_ButtonLabel">_t("Create Account")</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- The comments below are meant to be used by Ansible as a quick way
|
||||
to strip out the marked content when desired.
|
||||
See https://github.com/vector-im/element-web/issues/8622.
|
||||
TODO: Strip out these comments and rely on the guest flag -->
|
||||
<!-- BEGIN Ansible: Remove these lines when guest access is disabled -->
|
||||
<div class="mx_ButtonRow mx_WelcomePage_guestFunctions">
|
||||
<div>
|
||||
<a href="#/directory" class="mx_ButtonParent mx_SecondaryButton mx_Button_iconRoomDirectory">
|
||||
@@ -193,6 +188,5 @@ we don't have an account and should hide them. No account == no guest account ei
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END Ansible: Remove these lines when guest access is disabled -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 200C155.228 200 200 155.228 200 100C200 44.7715 155.228 0 100 0C44.7715 0 0 44.7715 0 100C0 155.228 44.7715 200 100 200Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M81.7169 46.5946C81.7169 42.5581 84.9959 39.2859 89.0408 39.2859C116.456 39.2859 138.681 61.4642 138.681 88.8225C138.681 92.859 135.401 96.1312 131.357 96.1312C127.312 96.1312 124.033 92.859 124.033 88.8225C124.033 69.5372 108.366 53.9033 89.0408 53.9033C84.9959 53.9033 81.7169 50.6311 81.7169 46.5946Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M153.39 81.5137C157.435 81.5137 160.714 84.7859 160.714 88.8224C160.714 116.181 138.49 138.359 111.075 138.359C107.03 138.359 103.751 135.087 103.751 131.05C103.751 127.014 107.03 123.742 111.075 123.742C130.4 123.742 146.066 108.108 146.066 88.8224C146.066 84.7859 149.345 81.5137 153.39 81.5137Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.398 153.405C118.398 157.442 115.119 160.714 111.074 160.714C83.6592 160.714 61.4347 138.536 61.4347 111.177C61.4347 107.141 64.7138 103.869 68.7587 103.869C72.8035 103.869 76.0826 107.141 76.0826 111.177C76.0826 130.463 91.7489 146.097 111.074 146.097C115.119 146.097 118.398 149.369 118.398 153.405Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.6097 118.486C42.5648 118.486 39.2858 115.214 39.2858 111.178C39.2858 83.8193 61.5102 61.6409 88.9255 61.6409C92.9704 61.6409 96.2494 64.9132 96.2494 68.9497C96.2494 72.9862 92.9704 76.2584 88.9255 76.2584C69.6 76.2584 53.9337 91.8922 53.9337 111.178C53.9337 115.214 50.6546 118.486 46.6097 118.486Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -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"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Dismiss": "Zamknij",
|
||||
"Dismiss": "Pomiń",
|
||||
"Unknown device": "Nieznane urządzenie",
|
||||
"Welcome to Element": "Witamy w Element",
|
||||
"Create Account": "Utwórz konto",
|
||||
@@ -11,20 +11,22 @@
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Błędna konfiguracja. Akceptowalne wartości to: default_server_config, default_server_name, default_hs_url.",
|
||||
"Invalid configuration: no default server specified.": "Błędna konfiguracja: nie wybrano domyślnego serwera.",
|
||||
"Go to your browser to complete Sign In": "Aby dokończyć proces rejestracji, przejdź do swojej przeglądarki",
|
||||
"Unable to load config file: please refresh the page to try again.": "Nie udało się załadować pliku konfiguracyjnego: odśwież stronę aby spróbować ponownie.",
|
||||
"Unable to load config file: please refresh the page to try again.": "Nie udało się załadować pliku konfiguracyjnego: odśwież stronę, aby spróbować ponownie.",
|
||||
"Unsupported browser": "Niewspierana przeglądarka",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Zainstaluj <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, lub <safariLink>Safari</safariLink> w celu zapewnienia najlepszego działania.",
|
||||
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Możesz kontynuować używając obecnej przeglądarki, lecz niektóre lub wszystkie funkcje mogą nie działać oraz wygląd aplikacji może być niepoprawny.",
|
||||
"I understand the risks and wish to continue": "Rozumiem ryzyko i chcę kontynuować",
|
||||
"Go to element.io": "Przejdź do element.io",
|
||||
"Failed to start": "Nie udało się wystartować",
|
||||
"Download Completed": "Pobieranie Zakończone",
|
||||
"Download Completed": "Pobieranie zakończone",
|
||||
"Open": "Otwórz",
|
||||
"Your browser can't run %(brand)s": "Twoja przeglądarka nie obsługuje %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s używa zaawansowanych funkcji które nie są dostępne w obecnej przeglądarce.",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Twoja konfiguracja Elementa zawiera niepoprawny JSON. Rozwiąż problem i odśwież stronę.",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s używa funkcji zaawansowanych, które nie są dostępne w Twojej przeglądarce.",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Twoja konfiguracja Elementa zawiera nieprawidłowy JSON. Rozwiąż problem i odśwież stronę.",
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
"The message from the parser is: %(message)s": "Thông báo của trình xử lý là: %(message)s",
|
||||
"Invalid JSON": "JSON không hợp lệ",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Thiết lập không hợp lệ: chỉ có thể điền một trong số sau default_server_config, default_server_name, hoặc default_hs_url.",
|
||||
"Invalid configuration: no default server specified.": "Cấu hình không hợp lệ: chưa chỉ định máy chủ mặc định.",
|
||||
"Invalid configuration: no default server specified.": "Thiết lập không hợp lệ: chưa chỉ định máy chủ mặc định.",
|
||||
"Sign In": "Đăng nhập",
|
||||
"Create Account": "Tạo tài khoản",
|
||||
"Explore rooms": "Khám phá phòng chat",
|
||||
"Explore rooms": "Khám phá các phòng",
|
||||
"Download Completed": "Tải xuống hoàn tất",
|
||||
"Go to element.io": "Đi đến element.io",
|
||||
"I understand the risks and wish to continue": "Tôi hiểu các rủi ro và muốn tiếp tục",
|
||||
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Bạn có thể tiếp tục sử dụng trình duyệt hiện tại, tuy nhiên một số hoặc tất cả tính năng có thể sẽ không hoạt động và trải nghiệm ứng dụng có thể sẽ không được tốt.",
|
||||
"Go to element.io": "Đến element.io",
|
||||
"I understand the risks and wish to continue": "Tôi hiểu rủi ro và muốn tiếp tục",
|
||||
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Bạn có thể tiếp tục sử dụng trình duyệt hiện tại, tuy nhiên các tính năng có thể sẽ không hoạt động và trải nghiệm ứng dụng có thể sẽ không được tốt.",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Hãy cài đặt <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, hoặc <safariLink>Safari</safariLink> để có trải nghiệm tốt nhất.",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s sử dụng một số tính năng nâng cao mà trình duyệt của bạn không thể đáp ứng.",
|
||||
"Your browser can't run %(brand)s": "Trình duyệt của bạn không thể chạy %(brand)s",
|
||||
@@ -21,10 +21,12 @@
|
||||
"Go to your browser to complete Sign In": "Mở trình duyệt web để hoàn thành đăng nhập",
|
||||
"Open": "Mở",
|
||||
"Unable to load config file: please refresh the page to try again.": "Không thể tải tệp cấu hình: hãy tải lại trang để thử lại.",
|
||||
"Failed to start": "Khởi động thất bại",
|
||||
"Failed to start": "Không khởi động được",
|
||||
"Use %(brand)s on mobile": "Sử dụng %(brand)s trên di động",
|
||||
"Powered by Matrix": "Được chạy trên giao thức Matrix",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Thiết lập Element của bạn chứa JSON không hợp lệ. Vui lòng sửa vấn đề và tải lại trang.",
|
||||
"Your Element is misconfigured": "Element của bạn bị thiết lập sai",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Dịch vụ chat & liên lạc đã được mã hóa, phi tập trung. Được cung cấp bởi $matrixLogo"
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Thiết lập Element của bạn đang chứa mã JSON không hợp lệ. Vui lòng sửa lại và tải lại trang.",
|
||||
"Your Element is misconfigured": "Element đang bị thiết lập sai",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Dịch vụ nhắn tin & liên lạc được mã hóa, phi tập trung. Được cung cấp bởi $matrixLogo",
|
||||
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s trên %(osName)s",
|
||||
"%(brand)s Desktop: %(platformName)s": "%(brand)s máy tính: %(platformName)s"
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, " +
|
||||
"or default_hs_url.",
|
||||
),
|
||||
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,22 @@ 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`);
|
||||
// Handle trailing dot FQDNs
|
||||
let domain = window.location.hostname.trimEnd();
|
||||
if (domain[domain.length - 1] === ".") {
|
||||
domain = domain.slice(0, -1);
|
||||
}
|
||||
|
||||
const specificConfigPromise = getConfig(`${relativeLocation}config.${domain}.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 +42,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 %>;
|
||||
@@ -93,6 +93,10 @@
|
||||
<source src="media/busy.ogg" type="audio/ogg" />
|
||||
<source src="media/busy.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<audio id="errorAudio">
|
||||
<source src="media/error.ogg" type="audio/ogg" />
|
||||
<source src="media/error.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<audio id="remoteAudio"></audio>
|
||||
<!-- let CSS themes pass constants to the app -->
|
||||
<div id="mx_theme_accentColor"></div><div id="mx_theme_secondaryAccentColor"></div><div id="mx_theme_tertiaryAccentColor"></div>
|
||||
|
||||
@@ -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,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
if (this.notificationCount === count) return;
|
||||
super.setNotificationCount(count);
|
||||
|
||||
window.electron.send("setBadgeCount", count);
|
||||
window.electron!.send("setBadgeCount", count);
|
||||
// Windows doesn't support app badgeCount so we have to call this too
|
||||
navigator.setAppBadge?.(count).catch((e) => {
|
||||
logger.error("Failed to update app badge", e);
|
||||
});
|
||||
}
|
||||
|
||||
public supportsNotifications(): boolean {
|
||||
@@ -226,7 +260,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 +296,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 +423,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(
|
||||
{
|
||||
default_hs_url: DEFAULT_HS_URL,
|
||||
default_is_url: DEFAULT_IS_URL,
|
||||
validated_server_config: makeType(ValidatedServerConfig, {
|
||||
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==",
|
||||
},
|
||||
const config = {
|
||||
default_hs_url: DEFAULT_HS_URL,
|
||||
default_is_url: DEFAULT_IS_URL,
|
||||
validated_server_config: {
|
||||
hsUrl: DEFAULT_HS_URL,
|
||||
hsName: "TEST_ENVIRONMENT",
|
||||
hsNameIsDifferent: false, // yes, we lie
|
||||
isUrl: DEFAULT_IS_URL,
|
||||
} 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 = {
|
||||
hash: "#/room/!room:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
},
|
||||
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 = {
|
||||
hash: "#/room/!room1:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room1:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
},
|
||||
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: "",
|
||||
|
||||
@@ -13,7 +13,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
|
||||
@@ -207,8 +207,10 @@ module.exports = (env, argv) => {
|
||||
// Same goes for js/react-sdk - we don't need two copies.
|
||||
"matrix-js-sdk": path.resolve(__dirname, "node_modules/matrix-js-sdk"),
|
||||
"matrix-react-sdk": path.resolve(__dirname, "node_modules/matrix-react-sdk"),
|
||||
// and sanitize-html
|
||||
// and sanitize-html & matrix-events-sdk & matrix-widget-api
|
||||
"sanitize-html": path.resolve(__dirname, "node_modules/sanitize-html"),
|
||||
"matrix-events-sdk": path.resolve(__dirname, "node_modules/matrix-events-sdk"),
|
||||
"matrix-widget-api": path.resolve(__dirname, "node_modules/matrix-widget-api"),
|
||||
|
||||
// Define a variable so the i18n stuff can load
|
||||
"$webapp": path.resolve(__dirname, "webapp"),
|
||||
|
||||
Reference in New Issue
Block a user