Compare commits
130 Commits
release-v1
...
kittykat-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de8b99de3d | ||
|
|
143632adb0 | ||
|
|
7b904cd89b | ||
|
|
7ade21ad9b | ||
|
|
900df8ce16 | ||
|
|
d50454ece6 | ||
|
|
4292bb1c28 | ||
|
|
320c58dbfc | ||
|
|
9d830f4837 | ||
|
|
269ffe385a | ||
|
|
ed1ecde348 | ||
|
|
26077db644 | ||
|
|
6232c3a471 | ||
|
|
1bca364a03 | ||
|
|
89691b19fd | ||
|
|
44e01823a3 | ||
|
|
6741ee61d8 | ||
|
|
a9d1fd7cdf | ||
|
|
75522c771e | ||
|
|
cce899bf69 | ||
|
|
09562bccf6 | ||
|
|
0b23334ed2 | ||
|
|
6cc71e1f13 | ||
|
|
a302dba006 | ||
|
|
65edff3821 | ||
|
|
5946f1cd87 | ||
|
|
5d0deff937 | ||
|
|
40a114a80a | ||
|
|
33caca80e2 | ||
|
|
f698ce7e79 | ||
|
|
538b1a2294 | ||
|
|
fd3289f67e | ||
|
|
d7f2eba310 | ||
|
|
73189872b3 | ||
|
|
cad20f3e0f | ||
|
|
ee0d29f4e7 | ||
|
|
39eee10c57 | ||
|
|
e9a7b3349b | ||
|
|
2cd0bb7312 | ||
|
|
9c36adc4df | ||
|
|
9d05e01907 | ||
|
|
2a38ccbd77 | ||
|
|
22f011bdbf | ||
|
|
f15f5a59de | ||
|
|
8cd3294192 | ||
|
|
adf4a7fb21 | ||
|
|
b17149ab39 | ||
|
|
9a6de19e9d | ||
|
|
822e262a93 | ||
|
|
847e6b03cb | ||
|
|
4b6d9a067b | ||
|
|
9df3774886 | ||
|
|
0ed179d94c | ||
|
|
039141f91c | ||
|
|
3e3f279469 | ||
|
|
0d18e4ea8e | ||
|
|
31636dcbaa | ||
|
|
1af4f75688 | ||
|
|
fc6c859976 | ||
|
|
bf9d69cd1b | ||
|
|
f103492e50 | ||
|
|
35a9852b42 | ||
|
|
ccfd1b5d95 | ||
|
|
ef7dab792d | ||
|
|
4a789991c8 | ||
|
|
6d3c49d866 | ||
|
|
394907fb7a | ||
|
|
ec81bd5715 | ||
|
|
c4593091d6 | ||
|
|
070f4735cd | ||
|
|
c21ba402ad | ||
|
|
88f25d7444 | ||
|
|
d86e897ba6 | ||
|
|
4738dc9da2 | ||
|
|
0f88d86c89 | ||
|
|
f4480c9968 | ||
|
|
267179aac0 | ||
|
|
1e445a75a9 | ||
|
|
a620ff2b6a | ||
|
|
ce5210d35b | ||
|
|
03cce754d4 | ||
|
|
d6d02631ab | ||
|
|
a1d1b9105e | ||
|
|
8ec574017c | ||
|
|
bf0a05798c | ||
|
|
2fa98d7ad6 | ||
|
|
fc23d3f669 | ||
|
|
ebfa4c4b09 | ||
|
|
6726a1d3e9 | ||
|
|
3de75c20c1 | ||
|
|
70dabba32d | ||
|
|
8f1ab66b04 | ||
|
|
9f53b83bdb | ||
|
|
caf6a1ce62 | ||
|
|
ee3b9c3310 | ||
|
|
b3b8e2e0a2 | ||
|
|
a6a1e1fffd | ||
|
|
dd82f4b47d | ||
|
|
1519d5672e | ||
|
|
787d4c4135 | ||
|
|
72afc206d3 | ||
|
|
9518e61eae | ||
|
|
dffc6acc1a | ||
|
|
64ed6ebdad | ||
|
|
73df4077da | ||
|
|
e873e84130 | ||
|
|
ecda0a1073 | ||
|
|
9414af4dca | ||
|
|
4d277a6bd9 | ||
|
|
e1d4817d54 | ||
|
|
0cf8abcc6f | ||
|
|
293ece5cec | ||
|
|
c12f51c7ab | ||
|
|
3b109fbaeb | ||
|
|
fc1db94c80 | ||
|
|
796aeff168 | ||
|
|
c10735ad04 | ||
|
|
a271516c8b | ||
|
|
2047c09d61 | ||
|
|
7708d0584b | ||
|
|
017dfb6606 | ||
|
|
a4f9e7adad | ||
|
|
688dbc6cba | ||
|
|
2032668e3a | ||
|
|
f1e5b95554 | ||
|
|
f03200f8e6 | ||
|
|
1070081217 | ||
|
|
3d15650bd6 | ||
|
|
f92b157117 | ||
|
|
53ce8388f1 |
@@ -1,3 +1,5 @@
|
||||
src/vector/modernizr.js
|
||||
# Legacy skinning file that some people might still have
|
||||
src/component-index.js
|
||||
# Auto-generated file
|
||||
src/modules.ts
|
||||
|
||||
@@ -18,11 +18,13 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
overrides: [{
|
||||
files: ["src/**/*.{ts,tsx}"],
|
||||
files: ["src/**/*.{ts,tsx}", "module_system/**/*.{ts,tsx}"],
|
||||
extends: [
|
||||
"plugin:matrix-org/typescript",
|
||||
"plugin:matrix-org/react",
|
||||
],
|
||||
// NOTE: These rules are frozen and new rules should not be added here.
|
||||
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/
|
||||
rules: {
|
||||
// Things we do that break the ideal style
|
||||
"prefer-promise-reject-errors": "off",
|
||||
|
||||
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,11 +1,18 @@
|
||||
<!-- Please read https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md before submitting your pull request -->
|
||||
<!-- Thanks for submitting a PR! Please ensure the following requirements are met in order for us to review your PR -->
|
||||
|
||||
<!-- Include a Sign-Off as described in https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md#sign-off -->
|
||||
## Checklist
|
||||
|
||||
<!-- To specify text for the changelog entry (otherwise the PR title will be used):
|
||||
Notes:
|
||||
* [ ] Tests written for new code (and old code if feasible)
|
||||
* [ ] Linter and other CI checks pass
|
||||
* [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-web/blob/develop/CONTRIBUTING.md))
|
||||
|
||||
<!--
|
||||
If you would like to specify text for the changelog entry other than your PR title, add the following:
|
||||
|
||||
Notes: Add super cool feature
|
||||
|
||||
For PRs which *only* affect the desktop version, please use:
|
||||
|
||||
Changelog entries will also appear in element-desktop. For PRs that *only* affect the desktop version:
|
||||
Notes: none
|
||||
element-desktop notes: <notes>
|
||||
element-desktop notes: Add super cool feature
|
||||
-->
|
||||
|
||||
6
.github/renovate.json
vendored
Normal file
6
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"github>matrix-org/renovate-config-element-web"
|
||||
]
|
||||
}
|
||||
30
.github/workflows/backport.yml
vendored
Normal file
30
.github/workflows/backport.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport
|
||||
runs-on: ubuntu-latest
|
||||
# Only react to merged PRs for security reasons.
|
||||
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
|
||||
if: >
|
||||
github.event.pull_request.merged
|
||||
&& (
|
||||
github.event.action == 'closed'
|
||||
|| (
|
||||
github.event.action == 'labeled'
|
||||
&& contains(github.event.label.name, 'backport')
|
||||
)
|
||||
)
|
||||
steps:
|
||||
- uses: tibdex/backport@v2
|
||||
with:
|
||||
labels_template: "<%= JSON.stringify(labels) %>"
|
||||
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
|
||||
github_token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build and Package
|
||||
name: Build
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
@@ -22,5 +22,5 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Build & Package
|
||||
run: "./scripts/ci_package.sh"
|
||||
- name: Build
|
||||
run: "yarn build"
|
||||
|
||||
22
.github/workflows/build_develop.yml
vendored
22
.github/workflows/build_develop.yml
vendored
@@ -6,6 +6,9 @@ on:
|
||||
branches: [ develop ]
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
name: "Build & Upload source maps to Sentry"
|
||||
@@ -29,5 +32,22 @@ jobs:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: sentry
|
||||
SENTRY_ORG: element
|
||||
SENTRY_PROJECT: riot-web
|
||||
|
||||
- run: mv dist/element-*.tar.gz webapp.tar.gz
|
||||
|
||||
- name: Wait for other steps to succeed
|
||||
uses: lewagon/wait-on-check-action@v1.0.0
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
running-workflow-name: 'Build & Upload source maps to Sentry'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
wait-interval: 10
|
||||
check-regexp: ^((?!SonarQube|issues|board).)*$
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: webapp
|
||||
path: webapp.tar.gz
|
||||
retention-days: 1
|
||||
|
||||
59
.github/workflows/dockerhub.yaml
vendored
Normal file
59
.github/workflows/dockerhub.yaml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Dockerhub
|
||||
on:
|
||||
workflow_dispatch: { }
|
||||
push:
|
||||
tags: [ v* ]
|
||||
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 }}
|
||||
jobs:
|
||||
buildx:
|
||||
name: Docker Buildx
|
||||
runs-on: ubuntu-latest
|
||||
environment: dockerhub
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
vectorim/element-web
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Update repo description
|
||||
uses: peter-evans/dockerhub-description@v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
repository: vectorim/element-web
|
||||
22
.github/workflows/issue_closed.yml
vendored
22
.github/workflows/issue_closed.yml
vendored
@@ -10,6 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
id: main
|
||||
with:
|
||||
# PAT needed as the GITHUB_TOKEN won't be able to see cross-references from other orgs (matrix-org)
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
@@ -127,13 +128,7 @@ jobs:
|
||||
|
||||
// Duplicate was closed with wrong reason, fix it
|
||||
if (stateReason === "COMPLETED") {
|
||||
await github.graphql(`mutation($id:ID!) {
|
||||
closeIssue(input: { issueId:$id, stateReason:NOT_PLANNED }) {
|
||||
clientMutationId
|
||||
}
|
||||
}`, {
|
||||
id: context.payload.issue.node_id,
|
||||
});
|
||||
core.setOutput("closeAsNotPlanned", "true");
|
||||
}
|
||||
} else {
|
||||
// This issue was closed, close all related rageshakes
|
||||
@@ -146,3 +141,16 @@ jobs:
|
||||
});
|
||||
}
|
||||
}
|
||||
- uses: actions/github-script@v5
|
||||
name: Close duplicate as Not Planned
|
||||
if: steps.main.outputs.closeAsNotPlanned
|
||||
with:
|
||||
# We do this step separately, and with the default token so as to not re-trigger this workflow when re-closing
|
||||
script: |
|
||||
await github.graphql(`mutation($id:ID!) {
|
||||
closeIssue(input: { issueId:$id, stateReason:NOT_PLANNED }) {
|
||||
clientMutationId
|
||||
}
|
||||
}`, {
|
||||
id: context.payload.issue.node_id,
|
||||
});
|
||||
|
||||
57
.github/workflows/static_analysis.yaml
vendored
57
.github/workflows/static_analysis.yaml
vendored
@@ -57,9 +57,60 @@ jobs:
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install --pure-lockfile"
|
||||
# Needs branch matching as it inherits .stylelintrc.js from matrix-react-sdk
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn run lint:style"
|
||||
|
||||
analyse_dead_code:
|
||||
name: "Analyse Dead Code"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Deps
|
||||
run: "scripts/layered.sh"
|
||||
|
||||
- name: Dead Code Analysis
|
||||
run: "yarn run analyse:unused-exports"
|
||||
|
||||
tsc-strict:
|
||||
name: Typescript Strict Error Checker
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
checks: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Get diff lines
|
||||
id: diff
|
||||
uses: Equip-Collaboration/diff-line-numbers@v1.0.0
|
||||
with:
|
||||
include: '["\\.tsx?$"]'
|
||||
|
||||
- name: Detecting files changed
|
||||
id: files
|
||||
uses: futuratrepadeira/changed-files@v3.2.1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pattern: '^.*\.tsx?$'
|
||||
|
||||
- uses: t3chguy/typescript-check-action@main
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
use-check: false
|
||||
check-fail-mode: added
|
||||
output-behaviour: annotate
|
||||
ts-extra-args: '--strict'
|
||||
files-changed: ${{ steps.files.outputs.files_updated }}
|
||||
files-added: ${{ steps.files.outputs.files_created }}
|
||||
files-deleted: ${{ steps.files.outputs.files_deleted }}
|
||||
line-numbers: ${{ steps.diff.outputs.lineNumbers }}
|
||||
|
||||
35
.github/workflows/triage-labelled.yml
vendored
35
.github/workflows/triage-labelled.yml
vendored
@@ -44,14 +44,7 @@ jobs:
|
||||
name: P1 X-Needs-Design to Design project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
|
||||
(contains(github.event.issue.labels.*.name, 'S-Critical') &&
|
||||
(contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
||||
contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
|
||||
contains(github.event.issue.labels.*.name, 'S-Major') &&
|
||||
contains(github.event.issue.labels.*.name, 'O-Frequent') ||
|
||||
contains(github.event.issue.labels.*.name, 'A11y') &&
|
||||
contains(github.event.issue.labels.*.name, 'O-Frequent'))
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Design')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
@@ -100,6 +93,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') ||
|
||||
(contains(github.event.issue.labels.*.name, 'A-Threads') &&
|
||||
(contains(github.event.issue.labels.*.name, 'S-Major') ||
|
||||
contains(github.event.issue.labels.*.name, 'S-Critical'))) ||
|
||||
contains(github.event.issue.labels.*.name, 'Team: Delight') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-NewUserJourney')
|
||||
steps:
|
||||
@@ -119,6 +115,29 @@ jobs:
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc1HvQ"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
search_issues_to_board:
|
||||
name: Search issues to project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) {
|
||||
projectNextItem {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PN_kwDOAM0swc4ADtaO"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
move_voice-message_issues:
|
||||
name: A-Voice Messages to voice message board
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,3 +24,6 @@ electron/pub
|
||||
.vscode/
|
||||
.env
|
||||
/coverage
|
||||
# Auto-generated file
|
||||
/src/modules.ts
|
||||
/build_config.yaml
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
// Copied from react-sdk
|
||||
// TODO: Only keep one copy of this for synchronization purposes
|
||||
module.exports = {
|
||||
"extends": "stylelint-config-standard",
|
||||
"plugins": [
|
||||
"stylelint-scss",
|
||||
],
|
||||
"rules": {
|
||||
"indentation": 4,
|
||||
"comment-empty-line-before": null,
|
||||
"declaration-empty-line-before": null,
|
||||
"length-zero-no-unit": null,
|
||||
"rule-empty-line-before": null,
|
||||
"color-hex-length": null,
|
||||
"max-empty-lines": null,
|
||||
"number-no-trailing-zeros": null,
|
||||
"number-leading-zero": null,
|
||||
"selector-list-comma-newline-after": null,
|
||||
"at-rule-no-unknown": null,
|
||||
"no-descending-specificity": null,
|
||||
"scss/at-rule-no-unknown": [true, {
|
||||
// https://github.com/vector-im/element-web/issues/10544
|
||||
"ignoreAtRules": ["define-mixin"],
|
||||
}],
|
||||
}
|
||||
...require("matrix-react-sdk/.stylelintrc.js"),
|
||||
};
|
||||
|
||||
186
CHANGELOG.md
186
CHANGELOG.md
@@ -1,3 +1,189 @@
|
||||
Changes in [1.11.4](https://github.com/vector-im/element-web/releases/tag/v1.11.4) (2022-08-31)
|
||||
===============================================================================================
|
||||
|
||||
## 🔒 Security
|
||||
* Fixes for [CVE-2022-36059](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D36059) and [CVE-2022-36060](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=CVE%2D2022%2D36060)
|
||||
|
||||
Learn more about what we've been up to at https://element.io/blog/element-web-desktop-1-11-4-a-security-update-deferred-dms-and-more/
|
||||
Find more details of the vulnerabilities at https://matrix.org/blog/2022/08/31/security-releases-matrix-js-sdk-19-4-0-and-matrix-react-sdk-3-53-0
|
||||
|
||||
## ✨ Features
|
||||
* Device manager - scroll to filtered list from security recommendations ([\#9227](https://github.com/matrix-org/matrix-react-sdk/pull/9227)). Contributed by @kerryarchibald.
|
||||
* Device manager - updated dropdown style in filtered device list ([\#9226](https://github.com/matrix-org/matrix-react-sdk/pull/9226)). Contributed by @kerryarchibald.
|
||||
* Device manager - device type and verification icons on device tile ([\#9197](https://github.com/matrix-org/matrix-react-sdk/pull/9197)). Contributed by @kerryarchibald.
|
||||
* Ignore unreads in low priority rooms in the space panel ([\#6518](https://github.com/matrix-org/matrix-react-sdk/pull/6518)). Fixes #16836.
|
||||
* Release message right-click context menu out of labs ([\#8613](https://github.com/matrix-org/matrix-react-sdk/pull/8613)).
|
||||
* Device manager - expandable session details in device list ([\#9188](https://github.com/matrix-org/matrix-react-sdk/pull/9188)). Contributed by @kerryarchibald.
|
||||
* Device manager - device list filtering ([\#9181](https://github.com/matrix-org/matrix-react-sdk/pull/9181)). Contributed by @kerryarchibald.
|
||||
* Device manager - add verification details to session details ([\#9187](https://github.com/matrix-org/matrix-react-sdk/pull/9187)). Contributed by @kerryarchibald.
|
||||
* Device manager - current session expandable details ([\#9185](https://github.com/matrix-org/matrix-react-sdk/pull/9185)). Contributed by @kerryarchibald.
|
||||
* Device manager - security recommendations section ([\#9179](https://github.com/matrix-org/matrix-react-sdk/pull/9179)). Contributed by @kerryarchibald.
|
||||
* The Welcome Home Screen: Return Button ([\#9089](https://github.com/matrix-org/matrix-react-sdk/pull/9089)). Fixes #22917. Contributed by @justjanne.
|
||||
* Device manager - label devices as inactive ([\#9175](https://github.com/matrix-org/matrix-react-sdk/pull/9175)). Contributed by @kerryarchibald.
|
||||
* Device manager - other sessions list ([\#9155](https://github.com/matrix-org/matrix-react-sdk/pull/9155)). Contributed by @kerryarchibald.
|
||||
* Implement MSC3846: Allowing widgets to access TURN servers ([\#9061](https://github.com/matrix-org/matrix-react-sdk/pull/9061)).
|
||||
* Allow widgets to send/receive to-device messages ([\#8885](https://github.com/matrix-org/matrix-react-sdk/pull/8885)).
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Add super cool feature ([\#9222](https://github.com/matrix-org/matrix-react-sdk/pull/9222)). Contributed by @gefgu.
|
||||
* Make use of js-sdk roomNameGenerator to handle i18n for generated room names ([\#9209](https://github.com/matrix-org/matrix-react-sdk/pull/9209)). Fixes #21369.
|
||||
* Fix progress bar regression throughout the app ([\#9219](https://github.com/matrix-org/matrix-react-sdk/pull/9219)). Fixes #23121.
|
||||
* Reuse empty string & space string logic for event types in devtools ([\#9218](https://github.com/matrix-org/matrix-react-sdk/pull/9218)). Fixes #23115.
|
||||
* Reduce amount of requests done by the onboarding task list ([\#9194](https://github.com/matrix-org/matrix-react-sdk/pull/9194)). Fixes #23085. Contributed by @justjanne.
|
||||
* Avoid hardcoding branding in user onboarding ([\#9206](https://github.com/matrix-org/matrix-react-sdk/pull/9206)). Fixes #23111. Contributed by @justjanne.
|
||||
* End jitsi call when member is banned ([\#8879](https://github.com/matrix-org/matrix-react-sdk/pull/8879)). Contributed by @maheichyk.
|
||||
* Fix context menu being opened when clicking message action bar buttons ([\#9200](https://github.com/matrix-org/matrix-react-sdk/pull/9200)). Fixes #22279 and #23100.
|
||||
* Add gap between checkbox and text in report dialog following the same pattern (8px) used in the gap between the two buttons. It fixes vector-im/element-web#23060 ([\#9195](https://github.com/matrix-org/matrix-react-sdk/pull/9195)). Contributed by @gefgu.
|
||||
* Fix url preview AXE and layout issue & add percy test ([\#9189](https://github.com/matrix-org/matrix-react-sdk/pull/9189)). Fixes #23083.
|
||||
* Wrap long space names ([\#9201](https://github.com/matrix-org/matrix-react-sdk/pull/9201)). Fixes #23095.
|
||||
* Attempt to fix `Failed to execute 'removeChild' on 'Node'` ([\#9196](https://github.com/matrix-org/matrix-react-sdk/pull/9196)).
|
||||
* Fix soft crash around space hierarchy changing between spaces ([\#9191](https://github.com/matrix-org/matrix-react-sdk/pull/9191)). Fixes matrix-org/element-web-rageshakes#14613.
|
||||
* Fix soft crash around room view store metrics ([\#9190](https://github.com/matrix-org/matrix-react-sdk/pull/9190)). Fixes matrix-org/element-web-rageshakes#14361.
|
||||
* Fix the same person appearing multiple times when searching for them. ([\#9177](https://github.com/matrix-org/matrix-react-sdk/pull/9177)). Fixes #22851.
|
||||
* Fix space panel subspace indentation going missing ([\#9167](https://github.com/matrix-org/matrix-react-sdk/pull/9167)). Fixes #23049.
|
||||
* Fix invisible power levels tile when showing hidden events ([\#9162](https://github.com/matrix-org/matrix-react-sdk/pull/9162)). Fixes #23013.
|
||||
* Space panel accessibility improvements ([\#9157](https://github.com/matrix-org/matrix-react-sdk/pull/9157)). Fixes #22995.
|
||||
* Fix inverted logic for showing UserWelcomeTop component ([\#9164](https://github.com/matrix-org/matrix-react-sdk/pull/9164)). Fixes #23037.
|
||||
|
||||
Changes in [1.11.3](https://github.com/vector-im/element-web/releases/tag/v1.11.3) (2022-08-16)
|
||||
===============================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Improve auth aria attributes and semantics ([\#22948](https://github.com/vector-im/element-web/pull/22948)).
|
||||
* Device manager - New device tile info design ([\#9122](https://github.com/matrix-org/matrix-react-sdk/pull/9122)). Contributed by @kerryarchibald.
|
||||
* Device manager generic settings subsection component ([\#9147](https://github.com/matrix-org/matrix-react-sdk/pull/9147)). Contributed by @kerryarchibald.
|
||||
* Migrate the hidden read receipts flag to new "send read receipts" option ([\#9141](https://github.com/matrix-org/matrix-react-sdk/pull/9141)).
|
||||
* Live location sharing - share location at most every 5 seconds ([\#9148](https://github.com/matrix-org/matrix-react-sdk/pull/9148)). Contributed by @kerryarchibald.
|
||||
* Increase max length of voice messages to 15m ([\#9133](https://github.com/matrix-org/matrix-react-sdk/pull/9133)). Fixes #18620.
|
||||
* Move pin drop out of labs ([\#9135](https://github.com/matrix-org/matrix-react-sdk/pull/9135)).
|
||||
* Start DM on first message ([\#8612](https://github.com/matrix-org/matrix-react-sdk/pull/8612)). Fixes #14736.
|
||||
* Remove "Add Space" button from RoomListHeader when user cannot create spaces ([\#9129](https://github.com/matrix-org/matrix-react-sdk/pull/9129)).
|
||||
* The Welcome Home Screen: Dedicated Download Apps Dialog ([\#9120](https://github.com/matrix-org/matrix-react-sdk/pull/9120)). Fixes #22921. Contributed by @justjanne.
|
||||
* The Welcome Home Screen: "Submit Feedback" pane ([\#9090](https://github.com/matrix-org/matrix-react-sdk/pull/9090)). Fixes #22918. Contributed by @justjanne.
|
||||
* New User Onboarding Task List ([\#9083](https://github.com/matrix-org/matrix-react-sdk/pull/9083)). Fixes #22919. Contributed by @justjanne.
|
||||
* Add support for disabling spell checking ([\#8604](https://github.com/matrix-org/matrix-react-sdk/pull/8604)). Fixes #21901.
|
||||
* Live location share - leave maximised map open when beacons expire ([\#9098](https://github.com/matrix-org/matrix-react-sdk/pull/9098)). Contributed by @kerryarchibald.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Some slash-commands (`/myroomnick`) have temporarily been disabled before the first message in a DM is sent. ([\#9193](https://github.com/matrix-org/matrix-react-sdk/pull/9193)).
|
||||
* Use stable reference for active tab in tabbedView ([\#9145](https://github.com/matrix-org/matrix-react-sdk/pull/9145)). Contributed by @kerryarchibald.
|
||||
* Fix pillification sometimes doubling up ([\#9152](https://github.com/matrix-org/matrix-react-sdk/pull/9152)). Fixes #23036.
|
||||
* Fix highlights not being applied to plaintext messages ([\#9126](https://github.com/matrix-org/matrix-react-sdk/pull/9126)). Fixes #22787.
|
||||
* Fix dismissing edit composer when change was undone ([\#9109](https://github.com/matrix-org/matrix-react-sdk/pull/9109)). Fixes #22932.
|
||||
* 1-to-1 DM rooms with bots now act like DM rooms instead of multi-user-rooms before ([\#9124](https://github.com/matrix-org/matrix-react-sdk/pull/9124)). Fixes #22894.
|
||||
* Apply inline start padding to selected lines on modern layout only ([\#9006](https://github.com/matrix-org/matrix-react-sdk/pull/9006)). Fixes #22768. Contributed by @luixxiul.
|
||||
* Peek into world-readable rooms from spotlight ([\#9115](https://github.com/matrix-org/matrix-react-sdk/pull/9115)). Fixes #22862.
|
||||
* Use default styling on nested numbered lists due to MD being sensitive ([\#9110](https://github.com/matrix-org/matrix-react-sdk/pull/9110)). Fixes #22935.
|
||||
* Fix replying using chat effect commands ([\#9101](https://github.com/matrix-org/matrix-react-sdk/pull/9101)). Fixes #22824.
|
||||
|
||||
Changes in [1.11.2](https://github.com/vector-im/element-web/releases/tag/v1.11.2) (2022-08-03)
|
||||
===============================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Live location share - focus on user location on list item click ([\#9051](https://github.com/matrix-org/matrix-react-sdk/pull/9051)). Contributed by @kerryarchibald.
|
||||
* Live location sharing - don't trigger unread counts for beacon location events ([\#9071](https://github.com/matrix-org/matrix-react-sdk/pull/9071)). Contributed by @kerryarchibald.
|
||||
* Support for sending voice messages as replies and in threads ([\#9097](https://github.com/matrix-org/matrix-react-sdk/pull/9097)). Fixes #22031.
|
||||
* Add `Reply in thread` button to the right-click message context-menu ([\#9004](https://github.com/matrix-org/matrix-react-sdk/pull/9004)). Fixes #22745.
|
||||
* Starred_Messages_Feature_Contd_II/Outreachy ([\#9086](https://github.com/matrix-org/matrix-react-sdk/pull/9086)).
|
||||
* Use "frequently used emojis" for autocompletion in composer ([\#8998](https://github.com/matrix-org/matrix-react-sdk/pull/8998)). Fixes #18978. Contributed by @grimhilt.
|
||||
* Improve clickability of view source event toggle button ([\#9068](https://github.com/matrix-org/matrix-react-sdk/pull/9068)). Fixes #21856. Contributed by @luixxiul.
|
||||
* Improve clickability of "collapse" link button on bubble layout ([\#9037](https://github.com/matrix-org/matrix-react-sdk/pull/9037)). Fixes #22864. Contributed by @luixxiul.
|
||||
* Starred_Messages_Feature/Outreachy ([\#8842](https://github.com/matrix-org/matrix-react-sdk/pull/8842)).
|
||||
* Implement Use Case Selection screen ([\#8984](https://github.com/matrix-org/matrix-react-sdk/pull/8984)). Contributed by @justjanne.
|
||||
* Live location share - handle insufficient permissions in location sharing ([\#9047](https://github.com/matrix-org/matrix-react-sdk/pull/9047)). Contributed by @kerryarchibald.
|
||||
* Improve _FilePanel.scss ([\#9031](https://github.com/matrix-org/matrix-react-sdk/pull/9031)). Contributed by @luixxiul.
|
||||
* Improve spotlight accessibility by adding context menus ([\#8907](https://github.com/matrix-org/matrix-react-sdk/pull/8907)). Fixes #20875 and #22675. Contributed by @justjanne.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Replace mask-images with svg components in MessageActionBar ([\#9088](https://github.com/matrix-org/matrix-react-sdk/pull/9088)). Fixes #22912. Contributed by @kerryarchibald.
|
||||
* Unbreak in-app permalink tooltips ([\#9087](https://github.com/matrix-org/matrix-react-sdk/pull/9087)). Fixes #22874.
|
||||
* Show a back button when viewing a space member ([\#9095](https://github.com/matrix-org/matrix-react-sdk/pull/9095)). Fixes #22898.
|
||||
* Align the right edge of info tile lines with normal ones on IRC layout ([\#9058](https://github.com/matrix-org/matrix-react-sdk/pull/9058)). Fixes #22871. Contributed by @luixxiul.
|
||||
* Prevent email verification from overriding existing sessions ([\#9075](https://github.com/matrix-org/matrix-react-sdk/pull/9075)). Fixes #22881. Contributed by @justjanne.
|
||||
* Fix wrong buttons being used when exploring public rooms ([\#9062](https://github.com/matrix-org/matrix-react-sdk/pull/9062)). Fixes #22862.
|
||||
* Re-add padding to generic event list summary on IRC layout ([\#9063](https://github.com/matrix-org/matrix-react-sdk/pull/9063)). Fixes #22869. Contributed by @luixxiul.
|
||||
* Joining federated rooms via the spotlight search should no longer cause a "No known servers" error. ([\#9055](https://github.com/matrix-org/matrix-react-sdk/pull/9055)). Fixes #22845. Contributed by @Half-Shot.
|
||||
|
||||
Changes in [1.11.1](https://github.com/vector-im/element-web/releases/tag/v1.11.1) (2022-07-26)
|
||||
===============================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Enable URL tooltips on hover for Element Desktop ([\#22286](https://github.com/vector-im/element-web/pull/22286)). Fixes undefined/element-web#6532.
|
||||
* Hide screenshare button in video rooms on Desktop ([\#9045](https://github.com/matrix-org/matrix-react-sdk/pull/9045)).
|
||||
* Add a developer command to reset Megolm and Olm sessions ([\#9044](https://github.com/matrix-org/matrix-react-sdk/pull/9044)).
|
||||
* add spaces to TileErrorBoundary ([\#9012](https://github.com/matrix-org/matrix-react-sdk/pull/9012)). Contributed by @HarHarLinks.
|
||||
* Location sharing - add localised strings to map ([\#9025](https://github.com/matrix-org/matrix-react-sdk/pull/9025)). Fixes #21443. Contributed by @kerryarchibald.
|
||||
* Added trim to ignore whitespaces in email check ([\#9027](https://github.com/matrix-org/matrix-react-sdk/pull/9027)). Contributed by @ankur12-1610.
|
||||
* Improve _GenericEventListSummary.scss ([\#9005](https://github.com/matrix-org/matrix-react-sdk/pull/9005)). Contributed by @luixxiul.
|
||||
* Live location share - tiles without tile server (PSG-591) ([\#8962](https://github.com/matrix-org/matrix-react-sdk/pull/8962)). Contributed by @kerryarchibald.
|
||||
* Add option to display tooltip on link hover ([\#8394](https://github.com/matrix-org/matrix-react-sdk/pull/8394)). Fixes #21907.
|
||||
* Support a module API surface for custom functionality ([\#8246](https://github.com/matrix-org/matrix-react-sdk/pull/8246)).
|
||||
* Adjust encryption copy when creating a video room ([\#8989](https://github.com/matrix-org/matrix-react-sdk/pull/8989)). Fixes #22737.
|
||||
* Add bidirectonal isolation for pills ([\#8985](https://github.com/matrix-org/matrix-react-sdk/pull/8985)). Contributed by @sha-265.
|
||||
* Delabs `Show current avatar and name for users in message history` ([\#8764](https://github.com/matrix-org/matrix-react-sdk/pull/8764)). Fixes #22336.
|
||||
* Live location share - open latest location in map site ([\#8981](https://github.com/matrix-org/matrix-react-sdk/pull/8981)). Contributed by @kerryarchibald.
|
||||
* Improve LinkPreviewWidget ([\#8881](https://github.com/matrix-org/matrix-react-sdk/pull/8881)). Fixes #22634. Contributed by @luixxiul.
|
||||
* Render HTML topics in rooms on space home ([\#8939](https://github.com/matrix-org/matrix-react-sdk/pull/8939)).
|
||||
* Hide timestamp on event tiles being edited on every layout ([\#8956](https://github.com/matrix-org/matrix-react-sdk/pull/8956)). Contributed by @luixxiul.
|
||||
* Introduce new copy icon ([\#8942](https://github.com/matrix-org/matrix-react-sdk/pull/8942)).
|
||||
* Allow finding group DMs by members in spotlight ([\#8922](https://github.com/matrix-org/matrix-react-sdk/pull/8922)). Fixes #22564. Contributed by @justjanne.
|
||||
* Live location share - explicitly stop beacons replaced beacons ([\#8933](https://github.com/matrix-org/matrix-react-sdk/pull/8933)). Contributed by @kerryarchibald.
|
||||
* Remove unpin from widget kebab menu ([\#8924](https://github.com/matrix-org/matrix-react-sdk/pull/8924)).
|
||||
* Live location share - redact related locations on beacon redaction ([\#8926](https://github.com/matrix-org/matrix-react-sdk/pull/8926)). Contributed by @kerryarchibald.
|
||||
* Live location share - disallow message pinning ([\#8928](https://github.com/matrix-org/matrix-react-sdk/pull/8928)). Contributed by @kerryarchibald.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Remove the ability to hide yourself in video rooms ([\#22806](https://github.com/vector-im/element-web/pull/22806)). Fixes #22805.
|
||||
* Unbreak in-app permalink tooltips ([\#9100](https://github.com/matrix-org/matrix-react-sdk/pull/9100)).
|
||||
* Add space for the stroke on message editor on IRC layout ([\#9030](https://github.com/matrix-org/matrix-react-sdk/pull/9030)). Fixes #22785. Contributed by @luixxiul.
|
||||
* Fix pinned messages not re-linkifying on edit ([\#9042](https://github.com/matrix-org/matrix-react-sdk/pull/9042)). Fixes #22726.
|
||||
* Don't unnecessarily persist the host signup dialog ([\#9043](https://github.com/matrix-org/matrix-react-sdk/pull/9043)). Fixes #22778.
|
||||
* Fix URL previews causing messages to become unrenderable ([\#9028](https://github.com/matrix-org/matrix-react-sdk/pull/9028)). Fixes #22766.
|
||||
* Fix event list summaries including invalid events ([\#9041](https://github.com/matrix-org/matrix-react-sdk/pull/9041)). Fixes #22790.
|
||||
* Correct accessibility labels for unread rooms in spotlight ([\#9003](https://github.com/matrix-org/matrix-react-sdk/pull/9003)). Contributed by @justjanne.
|
||||
* Enable search strings highlight on bubble layout ([\#9032](https://github.com/matrix-org/matrix-react-sdk/pull/9032)). Fixes #22786. Contributed by @luixxiul.
|
||||
* Unbreak URL preview for formatted links with tooltips ([\#9022](https://github.com/matrix-org/matrix-react-sdk/pull/9022)). Fixes #22764.
|
||||
* Re-add margin to tiles based on EventTileBubble ([\#9015](https://github.com/matrix-org/matrix-react-sdk/pull/9015)). Fixes #22772. Contributed by @luixxiul.
|
||||
* Fix Shortcut prompt for Search showing in minimized Roomlist ([\#9014](https://github.com/matrix-org/matrix-react-sdk/pull/9014)). Fixes #22739. Contributed by @justjanne.
|
||||
* Fix avatar position on event info line for hidden events on a thread ([\#9019](https://github.com/matrix-org/matrix-react-sdk/pull/9019)). Fixes #22777. Contributed by @luixxiul.
|
||||
* Fix lost padding of event tile info line ([\#9009](https://github.com/matrix-org/matrix-react-sdk/pull/9009)). Fixes #22754 and #22759. Contributed by @luixxiul.
|
||||
* Align verification bubble with normal event tiles on IRC layout ([\#9001](https://github.com/matrix-org/matrix-react-sdk/pull/9001)). Fixes #22758. Contributed by @luixxiul.
|
||||
* Ensure timestamp on generic event list summary is not hidden from TimelineCard ([\#9000](https://github.com/matrix-org/matrix-react-sdk/pull/9000)). Fixes #22755. Contributed by @luixxiul.
|
||||
* Fix headings margin on security user settings tab ([\#8826](https://github.com/matrix-org/matrix-react-sdk/pull/8826)). Contributed by @luixxiul.
|
||||
* Fix timestamp position on file panel ([\#8976](https://github.com/matrix-org/matrix-react-sdk/pull/8976)). Fixes #22718. Contributed by @luixxiul.
|
||||
* Stop using :not() pseudo class for mx_GenericEventListSummary ([\#8944](https://github.com/matrix-org/matrix-react-sdk/pull/8944)). Fixes #22602. Contributed by @luixxiul.
|
||||
* Don't show the same user twice in Spotlight ([\#8978](https://github.com/matrix-org/matrix-react-sdk/pull/8978)). Fixes #22697.
|
||||
* Align the right edge of expand / collapse link buttons of generic event list summary in bubble layout with a variable ([\#8992](https://github.com/matrix-org/matrix-react-sdk/pull/8992)). Fixes #22743. Contributed by @luixxiul.
|
||||
* Display own avatars on search results panel in bubble layout ([\#8990](https://github.com/matrix-org/matrix-react-sdk/pull/8990)). Contributed by @luixxiul.
|
||||
* Fix text flow of thread summary content on threads list ([\#8991](https://github.com/matrix-org/matrix-react-sdk/pull/8991)). Fixes #22738. Contributed by @luixxiul.
|
||||
* Fix the size of the clickable area of images ([\#8987](https://github.com/matrix-org/matrix-react-sdk/pull/8987)). Fixes #22282.
|
||||
* Fix font size of MessageTimestamp on TimelineCard ([\#8950](https://github.com/matrix-org/matrix-react-sdk/pull/8950)). Contributed by @luixxiul.
|
||||
* Improve security room settings tab style rules ([\#8844](https://github.com/matrix-org/matrix-react-sdk/pull/8844)). Fixes #22575. Contributed by @luixxiul.
|
||||
* Align E2E icon and avatar of info tile in compact modern layout ([\#8965](https://github.com/matrix-org/matrix-react-sdk/pull/8965)). Fixes #22652. Contributed by @luixxiul.
|
||||
* Fix clickable area of general event list summary toggle ([\#8979](https://github.com/matrix-org/matrix-react-sdk/pull/8979)). Fixes #22722. Contributed by @luixxiul.
|
||||
* Fix resizing room topic ([\#8966](https://github.com/matrix-org/matrix-react-sdk/pull/8966)). Fixes #22689.
|
||||
* Dismiss the search dialogue when starting a DM ([\#8967](https://github.com/matrix-org/matrix-react-sdk/pull/8967)). Fixes #22700.
|
||||
* Fix "greyed out" text style inconsistency on search result panel ([\#8974](https://github.com/matrix-org/matrix-react-sdk/pull/8974)). Contributed by @luixxiul.
|
||||
* Add top padding to EventTilePreview loader ([\#8977](https://github.com/matrix-org/matrix-react-sdk/pull/8977)). Fixes #22719. Contributed by @luixxiul.
|
||||
* Fix read receipts group position on TimelineCard in compact modern/group layout ([\#8971](https://github.com/matrix-org/matrix-react-sdk/pull/8971)). Fixes #22715. Contributed by @luixxiul.
|
||||
* Fix calls on homeservers without the unstable thirdparty endpoints. ([\#8931](https://github.com/matrix-org/matrix-react-sdk/pull/8931)). Fixes #21680. Contributed by @deepbluev7.
|
||||
* Enable ReplyChain text to be expanded on IRC layout ([\#8959](https://github.com/matrix-org/matrix-react-sdk/pull/8959)). Fixes #22709. Contributed by @luixxiul.
|
||||
* Fix hidden timestamp on message edit history dialog ([\#8955](https://github.com/matrix-org/matrix-react-sdk/pull/8955)). Fixes #22701. Contributed by @luixxiul.
|
||||
* Enable ReplyChain text to be expanded on bubble layout ([\#8958](https://github.com/matrix-org/matrix-react-sdk/pull/8958)). Fixes #22709. Contributed by @luixxiul.
|
||||
* Fix expand/collapse state wrong in metaspaces ([\#8952](https://github.com/matrix-org/matrix-react-sdk/pull/8952)). Fixes #22632.
|
||||
* Location (live) share replies now provide a fallback content ([\#8949](https://github.com/matrix-org/matrix-react-sdk/pull/8949)).
|
||||
* Fix space settings not opening for script-created spaces ([\#8957](https://github.com/matrix-org/matrix-react-sdk/pull/8957)). Fixes #22703.
|
||||
* Respect `filename` field on `m.file` events ([\#8951](https://github.com/matrix-org/matrix-react-sdk/pull/8951)).
|
||||
* Fix PlatformSettingsHandler always returning true due to returning a Promise ([\#8954](https://github.com/matrix-org/matrix-react-sdk/pull/8954)). Fixes #22616.
|
||||
* Improve high-contrast support for spotlight ([\#8948](https://github.com/matrix-org/matrix-react-sdk/pull/8948)). Fixes #22481. Contributed by @justjanne.
|
||||
* Fix wrong assertions that all media events have a mimetype ([\#8946](https://github.com/matrix-org/matrix-react-sdk/pull/8946)). Fixes matrix-org/element-web-rageshakes#13727.
|
||||
* Make invite dialogue fixed height ([\#8934](https://github.com/matrix-org/matrix-react-sdk/pull/8934)). Fixes #22659.
|
||||
* Fix all megolm error reported as unknown ([\#8916](https://github.com/matrix-org/matrix-react-sdk/pull/8916)).
|
||||
* Remove line-height declarations from _ReplyTile.scss ([\#8932](https://github.com/matrix-org/matrix-react-sdk/pull/8932)). Fixes #22687. Contributed by @luixxiul.
|
||||
* Reduce video rooms log spam ([\#8913](https://github.com/matrix-org/matrix-react-sdk/pull/8913)).
|
||||
* Correct new search input’s rounded corners ([\#8921](https://github.com/matrix-org/matrix-react-sdk/pull/8921)). Fixes #22576. Contributed by @justjanne.
|
||||
* Align unread notification dot on threads list in compact modern=group layout ([\#8911](https://github.com/matrix-org/matrix-react-sdk/pull/8911)). Fixes #22677. Contributed by @luixxiul.
|
||||
|
||||
Changes in [1.11.0](https://github.com/vector-im/element-web/releases/tag/v1.11.0) (2022-07-05)
|
||||
===============================================================================================
|
||||
|
||||
|
||||
285
CONTRIBUTING.md
285
CONTRIBUTING.md
@@ -1,4 +1,283 @@
|
||||
Contributing code to Element
|
||||
============================
|
||||
Contributing code to Element Web
|
||||
================================
|
||||
|
||||
Element follows the same pattern as the [matrix-js-sdk](https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md).
|
||||
Everyone is welcome to contribute code to Element Web, provided that they are
|
||||
willing to license their contributions under the same license as the project
|
||||
itself. We follow a simple 'inbound=outbound' model for contributions: the act
|
||||
of submitting an 'inbound' contribution means that the contributor agrees to
|
||||
license the code under the same terms as the project's overall 'outbound'
|
||||
license - in this case, Apache Software License v2 (see
|
||||
[LICENSE](LICENSE)).
|
||||
|
||||
How to contribute
|
||||
-----------------
|
||||
|
||||
The preferred and easiest way to contribute changes to the project is to fork
|
||||
it on github, and then create a pull request to ask us to pull your changes
|
||||
into our repo (https://help.github.com/articles/using-pull-requests/)
|
||||
|
||||
We use GitHub's pull request workflow to review the contribution, and either
|
||||
ask you to make any refinements needed or merge it and make them ourselves.
|
||||
|
||||
Things that should go into your PR description:
|
||||
* A changelog entry in the `Notes` section (see below)
|
||||
* References to any bugs fixed by the change (in GitHub's `Fixes` notation)
|
||||
* Describe the why and what is changing in the PR description so it's easy for
|
||||
onlookers and reviewers to onboard and context switch. This information is
|
||||
also helpful when we come back to look at this in 6 months and ask "why did
|
||||
we do it like that?" we have a chance of finding out.
|
||||
* Why didn't it work before? Why does it work now? What use cases does it
|
||||
unlock?
|
||||
* If you find yourself adding information on how the code works or why you
|
||||
chose to do it the way you did, make sure this information is instead
|
||||
written as comments in the code itself.
|
||||
* Sometimes a PR can change considerably as it is developed. In this case,
|
||||
the description should be updated to reflect the most recent state of
|
||||
the PR. (It can be helpful to retain the old content under a suitable
|
||||
heading, for additional context.)
|
||||
* Include both **before** and **after** screenshots to easily compare and discuss
|
||||
what's changing.
|
||||
* Include a step-by-step testing strategy so that a reviewer can check out the
|
||||
code locally and easily get to the point of testing your change.
|
||||
* Add comments to the diff for the reviewer that might help them to understand
|
||||
why the change is necessary or how they might better understand and review it.
|
||||
|
||||
We rely on information in pull request to populate the information that goes into
|
||||
the changelogs our users see, both for Element Web itself and other projects on
|
||||
which it is based. This is picked up from both labels on the pull request and
|
||||
the `Notes:` annotation in the description. By default, the PR title will be
|
||||
used for the changelog entry, but you can specify more options, as follows.
|
||||
|
||||
To add a longer, more detailed description of the change for the changelog:
|
||||
|
||||
|
||||
*Fix llama herding bug*
|
||||
|
||||
```
|
||||
Notes: Fix a bug (https://github.com/matrix-org/notaproject/issues/123) where the 'Herd' button would not herd more than 8 Llamas if the moon was in the waxing gibbous phase
|
||||
```
|
||||
|
||||
For some PRs, it's not useful to have an entry in the user-facing changelog (this is
|
||||
the default for PRs labelled with `T-Task`):
|
||||
|
||||
*Remove outdated comment from `Ungulates.ts`*
|
||||
```
|
||||
Notes: none
|
||||
```
|
||||
|
||||
Sometimes, you're fixing a bug in a downstream project, in which case you want
|
||||
an entry in that project's changelog. You can do that too:
|
||||
|
||||
*Fix another herding bug*
|
||||
```
|
||||
Notes: Fix a bug where the `herd()` function would only work on Tuesdays
|
||||
element-web notes: Fix a bug where the 'Herd' button only worked on Tuesdays
|
||||
```
|
||||
|
||||
This example is for Element Web. You can specify:
|
||||
* matrix-react-sdk
|
||||
* element-web
|
||||
* element-desktop
|
||||
|
||||
If your PR introduces a breaking change, use the `Notes` section in the same
|
||||
way, additionally adding the `X-Breaking-Change` label (see below). There's no need
|
||||
to specify in the notes that it's a breaking change - this will be added
|
||||
automatically based on the label - but remember to tell the developer how to
|
||||
migrate:
|
||||
|
||||
*Remove legacy class*
|
||||
|
||||
```
|
||||
Notes: Remove legacy `Camelopard` class. `Giraffe` should be used instead.
|
||||
```
|
||||
|
||||
Other metadata can be added using labels.
|
||||
* `X-Breaking-Change`: A breaking change - adding this label will mean the change causes a *major* version bump.
|
||||
* `T-Enhancement`: A new feature - adding this label will mean the change causes a *minor* version bump.
|
||||
* `T-Defect`: A bug fix (in either code or docs).
|
||||
* `T-Task`: No user-facing changes, eg. code comments, CI fixes, refactors or tests. Won't have a changelog entry unless you specify one.
|
||||
|
||||
If you don't have permission to add labels, your PR reviewer(s) can work with you
|
||||
to add them: ask in the PR description or comments.
|
||||
|
||||
We use continuous integration, and all pull requests get automatically tested:
|
||||
if your change breaks the build, then the PR will show that there are failed
|
||||
checks, so please check back after a few minutes.
|
||||
|
||||
Tests
|
||||
-----
|
||||
Your PR should include tests.
|
||||
|
||||
For new user facing features in `matrix-js-sdk`, `matrix-react-sdk` or `element-web`, you
|
||||
must include:
|
||||
|
||||
1. Comprehensive unit tests written in Jest. These are located in `/test`.
|
||||
2. "happy path" end-to-end tests.
|
||||
These are located in `/cypress/e2e` in `matrix-react-sdk`, and
|
||||
are run using `element-web`. Ideally, you would also include tests for edge
|
||||
and error cases.
|
||||
|
||||
Unit tests are expected even when the feature is in labs. It's good practice
|
||||
to write tests alongside the code as it ensures the code is testable from
|
||||
the start, and gives you a fast feedback loop while you're developing the
|
||||
functionality. End-to-end tests should be added prior to the feature
|
||||
leaving labs, but don't have to be present from the start (although it might
|
||||
be beneficial to have some running early, so you can test things faster).
|
||||
|
||||
For bugs in those repos, your change must include at least one unit test or
|
||||
end-to-end test; which is best depends on what sort of test most concisely
|
||||
exercises the area.
|
||||
|
||||
Changes to must be accompanied by unit tests written in Jest.
|
||||
These are located in `/spec/` in `matrix-js-sdk` or `/test/` in `element-web`
|
||||
and `matrix-react-sdk`.
|
||||
|
||||
When writing unit tests, please aim for a high level of test coverage
|
||||
for new code - 80% or greater. If you cannot achieve that, please document
|
||||
why it's not possible in your PR.
|
||||
|
||||
Some sections of code are not sensible to add coverage for, such as those
|
||||
which explicitly inhibit noisy logging for tests. Which can be hidden using
|
||||
an istanbul magic comment as [documented here][1]. See example:
|
||||
```javascript
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
logger.error("Log line that is noisy enough in tests to want to skip");
|
||||
}
|
||||
```
|
||||
|
||||
Tests validate that your change works as intended and also document
|
||||
concisely what is being changed. Ideally, your new tests fail
|
||||
prior to your change, and succeed once it has been applied. You may
|
||||
find this simpler to achieve if you write the tests first.
|
||||
|
||||
If you're spiking some code that's experimental and not being used to support
|
||||
production features, exceptions can be made to requirements for tests.
|
||||
Note that tests will still be required in order to ship the feature, and it's
|
||||
strongly encouraged to think about tests early in the process, as adding
|
||||
tests later will become progressively more difficult.
|
||||
|
||||
If you're not sure how to approach writing tests for your change, ask for help
|
||||
in [#element-dev](https://matrix.to/#/#element-dev:matrix.org).
|
||||
|
||||
Code style
|
||||
----------
|
||||
Element Web aims to target TypeScript/ES6. All new files should be written in
|
||||
TypeScript and existing files should use ES6 principles where possible.
|
||||
|
||||
Members should not be exported as a default export in general - it causes problems
|
||||
with the architecture of the SDK (index file becomes less clear) and could
|
||||
introduce naming problems (as default exports get aliased upon import). In
|
||||
general, avoid using `export default`.
|
||||
|
||||
The remaining code style is documented in [code_style.md](./code_style.md).
|
||||
Contributors are encouraged to it and follow the principles set out there.
|
||||
|
||||
Please ensure your changes match the cosmetic style of the existing project,
|
||||
and ***never*** mix cosmetic and functional changes in the same commit, as it
|
||||
makes it horribly hard to review otherwise.
|
||||
|
||||
Attribution
|
||||
-----------
|
||||
Everyone who contributes anything to Matrix is welcome to be listed in the
|
||||
AUTHORS.rst file for the project in question. Please feel free to include a
|
||||
change to AUTHORS.rst in your pull request to list yourself and a short
|
||||
description of the area(s) you've worked on. Also, we sometimes have swag to
|
||||
give away to contributors - if you feel that Matrix-branded apparel is missing
|
||||
from your life, please mail us your shipping address to matrix at matrix.org
|
||||
and we'll try to fix it :)
|
||||
|
||||
Sign off
|
||||
--------
|
||||
In order to have a concrete record that your contribution is intentional
|
||||
and you agree to license it under the same terms as the project's license, we've
|
||||
adopted the same lightweight approach that the Linux Kernel
|
||||
(https://www.kernel.org/doc/Documentation/SubmittingPatches), Docker
|
||||
(https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other
|
||||
projects use: the DCO (Developer Certificate of Origin:
|
||||
http://developercertificate.org/). This is a simple declaration that you wrote
|
||||
the contribution or otherwise have the right to contribute it to Matrix:
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
If you agree to this for your contribution, then all that's needed is to
|
||||
include the line in your commit or pull request comment:
|
||||
|
||||
```
|
||||
Signed-off-by: Your Name <your@email.example.org>
|
||||
```
|
||||
|
||||
We accept contributions under a legally identifiable name, such as your name on
|
||||
government documentation or common-law names (names claimed by legitimate usage
|
||||
or repute). Unfortunately, we cannot accept anonymous contributions at this
|
||||
time.
|
||||
|
||||
Git allows you to add this signoff automatically when using the `-s` flag to
|
||||
`git commit`, which uses the name and email set in your `user.name` and
|
||||
`user.email` git configs.
|
||||
|
||||
If you forgot to sign off your commits before making your pull request and are
|
||||
on Git 2.17+ you can mass signoff using rebase:
|
||||
|
||||
```
|
||||
git rebase --signoff origin/develop
|
||||
```
|
||||
|
||||
Review expectations
|
||||
===================
|
||||
|
||||
See https://github.com/vector-im/element-meta/wiki/Review-process
|
||||
|
||||
|
||||
Merge Strategy
|
||||
==============
|
||||
|
||||
The preferred method for merging pull requests is squash merging to keep the
|
||||
commit history trim, but it is up to the discretion of the team member merging
|
||||
the change. We do not support rebase merges due to `allchange` being unable to
|
||||
handle them. When merging make sure to leave the default commit title, or
|
||||
at least leave the PR number at the end in brackets like by default.
|
||||
When stacking pull requests, you may wish to do the following:
|
||||
|
||||
1. Branch from develop to your branch (branch1), push commits onto it and open a pull request
|
||||
2. Branch from your base branch (branch1) to your work branch (branch2), push commits and open a pull request configuring the base to be branch1, saying in the description that it is based on your other PR.
|
||||
3. Merge the first PR using a merge commit otherwise your stacked PR will need a rebase. Github will automatically adjust the base branch of your other PR to be develop.
|
||||
|
||||
|
||||
[1]: https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md
|
||||
|
||||
@@ -27,9 +27,6 @@ FROM nginx:alpine
|
||||
|
||||
COPY --from=builder /src/webapp /app
|
||||
|
||||
# Insert wasm type into Nginx mime.types file so they load correctly.
|
||||
RUN sed -i '3i\ \ \ \ application/wasm wasm\;' /etc/nginx/mime.types
|
||||
|
||||
# Override default nginx config
|
||||
COPY /nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
|
||||
25
build_config.sample.yaml
Normal file
25
build_config.sample.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
# A sample build_config.yaml to supply to Element Web's build pipeline,
|
||||
# enabling custom functionality at compile time. Copy this file to
|
||||
# `build_config.yaml` in the same directory to use, as you would with
|
||||
# `config.json`.
|
||||
#
|
||||
# Note: The vast majority of users DO NOT need this. If you are looking
|
||||
# to build your own Element Web as seen on app.element.io or similar then
|
||||
# this is not required.
|
||||
#
|
||||
# This config file does become required if you are looking to add runtime
|
||||
# functionality to Element Web, such as customisation endpoints and modules.
|
||||
#
|
||||
# Over time we might expand this config to better support some use cases.
|
||||
# Watch the release notes for features which might impact this config.
|
||||
|
||||
# The modules to install. See ./docs/modules.md for more information on
|
||||
# what modules are.
|
||||
#
|
||||
# The values of this are provided to `yarn add` for inclusion.
|
||||
modules:
|
||||
# An example of pulling a module from NPM
|
||||
- "@vector-im/element-web-ilag-module@^0.0.1"
|
||||
|
||||
# An example of pulling a module from github
|
||||
- "github:vector-im/element-web-ilag-module#main"
|
||||
455
code_style.md
Normal file
455
code_style.md
Normal file
@@ -0,0 +1,455 @@
|
||||
# Element Web/Desktop code style guide
|
||||
|
||||
This code style applies to projects which the element-web team directly maintains or is reasonably
|
||||
adjacent to. As of writing, these are:
|
||||
|
||||
* element-desktop
|
||||
* element-web
|
||||
* matrix-react-sdk
|
||||
* matrix-js-sdk
|
||||
|
||||
Other projects might extend this code style for increased strictness. For example, matrix-events-sdk
|
||||
has stricter code organization to reduce the maintenance burden. These projects will declare their code
|
||||
style within their own repos.
|
||||
|
||||
Note that some requirements will be layer-specific. Where the requirements don't make sense for the
|
||||
project, they are used to the best of their ability, used in spirit, or ignored if not applicable,
|
||||
in that order.
|
||||
|
||||
## Guiding principles
|
||||
|
||||
1. We want the lint rules to feel natural for most team members. No one should have to think too much
|
||||
about the linter.
|
||||
2. We want to stay relatively close to [industry standards](https://google.github.io/styleguide/tsguide.html)
|
||||
to make onboarding easier.
|
||||
3. We describe what good code looks like rather than point out bad examples. We do this to avoid
|
||||
excessively punishing people for writing code which fails the linter.
|
||||
4. When something isn't covered by the style guide, we come up with a reasonable rule rather than
|
||||
claim that it "passes the linter". We update the style guide and linter accordingly.
|
||||
5. While we aim to improve readability, understanding, and other aspects of the code, we deliberately
|
||||
do not let solely our personal preferences drive decisions.
|
||||
6. We aim to have an understandable guide.
|
||||
|
||||
## Coding practices
|
||||
|
||||
1. Lint rules enforce decisions made by this guide. The lint rules and this guide are kept in
|
||||
perfect sync.
|
||||
2. Commit messages are descriptive for the changes. When the project supports squash merging,
|
||||
only the squashed commit needs to have a descriptive message.
|
||||
3. When there is disagreement with a code style approved by the linter, a PR is opened against
|
||||
the lint rules rather than making exceptions on the responsible code PR.
|
||||
4. Rules which are intentionally broken (via eslint-ignore, @ts-ignore, etc) have a comment
|
||||
included in the immediate vicinity for why. Determination of whether this is valid applies at
|
||||
code review time.
|
||||
5. When editing a file, nearby code is updated to meet the modern standards. "Nearby" is subjective,
|
||||
but should be whatever is reasonable at review time. Such an example might be to update the
|
||||
class's code style, but not the file's.
|
||||
1. These changes should be minor enough to include in the same commit without affecting a code
|
||||
reviewer's job.
|
||||
|
||||
## All code
|
||||
|
||||
Unless otherwise specified, the following applies to all code:
|
||||
|
||||
1. 120 character limit per line. Match existing code in the file if it is using a lower guide.
|
||||
2. A tab/indentation is 4 spaces.
|
||||
3. Newlines are Unix.
|
||||
4. A file has a single empty line at the end.
|
||||
5. Lines are trimmed of all excess whitespace, including blank lines.
|
||||
6. Long lines are broken up for readability.
|
||||
|
||||
## TypeScript / JavaScript {#typescript-javascript}
|
||||
|
||||
1. Write TypeScript. Turn JavaScript into TypeScript when working in the area.
|
||||
2. Use named exports.
|
||||
3. Break long lines to appear as follows:
|
||||
|
||||
```typescript
|
||||
// Function arguments
|
||||
function doThing(
|
||||
arg1: string,
|
||||
arg2: string,
|
||||
arg3: string,
|
||||
): boolean {
|
||||
return !!arg1
|
||||
&& !!arg2
|
||||
&& !!arg3;
|
||||
}
|
||||
|
||||
// Calling a function
|
||||
doThing(
|
||||
"String 1",
|
||||
"String 2",
|
||||
"String 3",
|
||||
);
|
||||
|
||||
// Reduce line verbosity when possible/reasonable
|
||||
doThing(
|
||||
"String1", "String 2",
|
||||
"A much longer string 3",
|
||||
);
|
||||
|
||||
// Chaining function calls
|
||||
something.doThing()
|
||||
.doOtherThing()
|
||||
.doMore()
|
||||
.somethingElse(it =>
|
||||
useIt(it)
|
||||
);
|
||||
```
|
||||
4. Use semicolons for block/line termination.
|
||||
1. Except when defining interfaces, classes, and non-arrow functions specifically.
|
||||
5. When a statement's body is a single line, it may be written without curly braces, so long as the body is placed on
|
||||
the same line as the statement.
|
||||
|
||||
```typescript
|
||||
if (x) doThing();
|
||||
```
|
||||
6. Blocks for `if`, `for`, `switch` and so on must have a space surrounding the condition, but not
|
||||
within the condition.
|
||||
|
||||
```typescript
|
||||
if (x) {
|
||||
doThing();
|
||||
}
|
||||
```
|
||||
7. Mixing of logical operands requires brackets to explicitly define boolean logic.
|
||||
|
||||
```typescript
|
||||
if ((a > b && b > c) || (d < e)) return true;
|
||||
```
|
||||
8. Ternaries use the same rules as `if` statements, plus the following:
|
||||
|
||||
```typescript
|
||||
// Single line is acceptable
|
||||
const val = a > b ? doThing() : doOtherThing();
|
||||
|
||||
// Multiline is also okay
|
||||
const val = a > b
|
||||
? doThing()
|
||||
: doOtherThing();
|
||||
|
||||
// Use brackets when using multiple conditions.
|
||||
// Maximum 3 conditions, prefer 2 or less.
|
||||
const val = (a > b && b > c) ? doThing() : doOtherThing();
|
||||
```
|
||||
9. lowerCamelCase is used for function and variable naming.
|
||||
10. UpperCamelCase is used for general naming.
|
||||
11. Interface names should not be marked with an uppercase `I`.
|
||||
12. One variable declaration per line.
|
||||
13. If a variable is not receiving a value on declaration, its type must be defined.
|
||||
|
||||
```typescript
|
||||
let errorMessage: Optional<string>;
|
||||
```
|
||||
14. Objects, arrays, enums and so on must have each line terminated with a comma:
|
||||
|
||||
```typescript
|
||||
const obj = {
|
||||
prop: 1,
|
||||
else: 2,
|
||||
};
|
||||
|
||||
const arr = [
|
||||
"one",
|
||||
"two",
|
||||
];
|
||||
|
||||
enum Thing {
|
||||
Foo,
|
||||
Bar,
|
||||
}
|
||||
|
||||
doThing(
|
||||
"arg1",
|
||||
"arg2",
|
||||
);
|
||||
```
|
||||
15. Objects can use shorthand declarations, including mixing of types.
|
||||
|
||||
```typescript
|
||||
{
|
||||
room,
|
||||
prop: this.prop,
|
||||
}
|
||||
// ... or ...
|
||||
{ room, prop: this.prop }
|
||||
```
|
||||
16. Object keys should always be non-strings when possible.
|
||||
|
||||
```typescript
|
||||
{
|
||||
property: "value",
|
||||
"m.unavoidable": true,
|
||||
[EventType.RoomMessage]: true,
|
||||
}
|
||||
```
|
||||
17. Explicitly cast to a boolean.
|
||||
|
||||
```typescript
|
||||
!!stringVar || Boolean(stringVar)
|
||||
```
|
||||
18. Use `switch` statements when checking against more than a few enum-like values.
|
||||
19. Use `const` for constants, `let` for mutability.
|
||||
20. 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.
|
||||
21. Declare member visibility (public/private/protected).
|
||||
22. 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.
|
||||
23. Prefer readonly members over getters backed by a variable, unless an internal setter is required.
|
||||
24. 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:
|
||||
|
||||
```typescript
|
||||
interface MyObject {
|
||||
hasString: boolean;
|
||||
}
|
||||
|
||||
type Options = MyObject | string;
|
||||
|
||||
function doThing(arg: Options) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
25. Variables/properties which are `public static` should also be `readonly` when possible.
|
||||
26. Interface and type properties are terminated with semicolons, not commas.
|
||||
27. Prefer arrow formatting when declaring functions for interfaces/types:
|
||||
|
||||
```typescript
|
||||
interface Test {
|
||||
myCallback: (arg: string) => Promise<void>;
|
||||
}
|
||||
```
|
||||
28. Prefer a type definition over an inline type. For example, define an interface.
|
||||
29. 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.
|
||||
30. `import` should be used instead of `require`, as `require` does not have types.
|
||||
31. Export only what can be reused.
|
||||
32. 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
|
||||
takes an argument that is more often not required than required. An example where the
|
||||
`?` operator is inappropriate is when taking a room ID: typically the caller should
|
||||
supply the room ID if it knows it, otherwise deliberately acknowledge that it doesn't
|
||||
have one with `null`.
|
||||
|
||||
```typescript
|
||||
function doThingWithRoom(
|
||||
thing: string,
|
||||
room: Optional<string>, // require the caller to specify
|
||||
) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
33. 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.
|
||||
34. Bulk functions can be declared in a single file, though named as "foo-utils.ts" or "utils/foo.ts".
|
||||
35. Imports are grouped by external module imports first, then by internal imports.
|
||||
36. File ordering is not strict, but should generally follow this sequence:
|
||||
1. Licence header
|
||||
2. Imports
|
||||
3. Constants
|
||||
4. Enums
|
||||
5. Interfaces
|
||||
6. Functions
|
||||
7. Classes
|
||||
1. Public/protected/private static properties
|
||||
2. Public/protected/private properties
|
||||
3. Constructors
|
||||
4. Public/protected/private getters & setters
|
||||
5. Protected and abstract functions
|
||||
6. Public/private functions
|
||||
7. Public/protected/private static functions
|
||||
37. Variable names should be noticeably unique from their types. For example, "str: string" instead
|
||||
of "string: string".
|
||||
38. 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"';
|
||||
```
|
||||
39. Prefer async-await to promise-chaining
|
||||
|
||||
```typescript
|
||||
async function () {
|
||||
const result = await anotherAsyncFunction();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## React
|
||||
|
||||
Inheriting all the rules of TypeScript, the following additionally apply:
|
||||
|
||||
1. Types for lifecycle functions are not required (render, componentDidMount, and so on).
|
||||
2. Class components must always have a `Props` interface declared immediately above them. It can be
|
||||
empty if the component accepts no props.
|
||||
3. Class components should have an `State` interface declared immediately above them, but after `Props`.
|
||||
4. Props and State should not be exported. Use `React.ComponentProps<typeof ComponentNameHere>`
|
||||
instead.
|
||||
5. One component per file, except when a component is a utility component specifically for the "primary"
|
||||
component. The utility component should not be exported.
|
||||
6. Exported constants, enums, interfaces, functions, etc must be separate from files containing components
|
||||
or stores.
|
||||
7. Stores should use a singleton pattern with a static instance property:
|
||||
|
||||
```typescript
|
||||
class FooStore {
|
||||
public static readonly instance = new FooStore();
|
||||
|
||||
// or if the instance can't be created eagerly:
|
||||
private static _instance: FooStore;
|
||||
public static get instance(): FooStore {
|
||||
if (!FooStore._instance) {
|
||||
FooStore._instance = new FooStore();
|
||||
}
|
||||
return FooStore._instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
8. Stores must support using an alternative MatrixClient and dispatcher instance.
|
||||
9. Utilities which require JSX must be split out from utilities which do not. This is to prevent import
|
||||
cycles during runtime where components accidentally include more of the app than they intended.
|
||||
10. Interdependence between stores should be kept to a minimum. Break functions and constants out to utilities
|
||||
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.
|
||||
12. Break components over multiple lines like so:
|
||||
|
||||
```typescript
|
||||
function render() {
|
||||
return <Component
|
||||
prop1="test"
|
||||
prop2={this.state.variable}
|
||||
/>;
|
||||
|
||||
// or
|
||||
|
||||
return (
|
||||
<Component
|
||||
prop1="test"
|
||||
prop2={this.state.variable}
|
||||
/>
|
||||
);
|
||||
|
||||
// or if children are needed (infer parens usage)
|
||||
|
||||
return <Component
|
||||
prop1="test"
|
||||
prop2={this.state.variable}
|
||||
>{ _t("Short string here") }</Component>;
|
||||
|
||||
|
||||
|
||||
return <Component
|
||||
prop1="test"
|
||||
prop2={this.state.variable}
|
||||
>
|
||||
{ _t("Longer string here") }
|
||||
</Component>;
|
||||
}
|
||||
```
|
||||
13. Curly braces within JSX should be padded with a space, however properties on those components should not.
|
||||
See above code example.
|
||||
14. Functions used as properties should either be defined on the class or stored in a variable. They should not
|
||||
be inline unless mocking/short-circuiting the value.
|
||||
15. Prefer hooks (functional components) over class components. Be consistent with the existing area if unsure
|
||||
which should be used.
|
||||
1. Unless the component is considered a "structure", in which case use classes.
|
||||
16. Write more views than structures. Structures are chunks of functionality like MatrixChat while views are
|
||||
isolated components.
|
||||
17. Components should serve a single, or near-single, purpose.
|
||||
18. Prefer to derive information from component properties rather than establish state.
|
||||
19. Do not use `React.Component::forceUpdate`.
|
||||
|
||||
## Stylesheets (\*.pcss = PostCSS + Plugins)
|
||||
|
||||
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.
|
||||
4. Keep indentation/nesting to a minimum. Maximum suggested nesting is 5 layers.
|
||||
5. Use the whole class name instead of shortcuts:
|
||||
|
||||
```scss
|
||||
.mx_MyFoo {
|
||||
& .mx_MyFoo_avatar { // instead of &_avatar
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
6. Break multiple selectors over multiple lines this way:
|
||||
|
||||
```scss
|
||||
.mx_MyFoo,
|
||||
.mx_MyBar,
|
||||
.mx_MyFooBar {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
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:
|
||||
|
||||
```scss
|
||||
.mx_MyFoo {
|
||||
width: calc(100% - 12px); // 12px for read receipts
|
||||
top: -2px; // visually centred vertically
|
||||
z-index: 10; // above user avatar, but below dialogs
|
||||
}
|
||||
```
|
||||
9. Avoid the use of `!important`. If necessary, add a comment.
|
||||
|
||||
## Tests
|
||||
|
||||
1. Tests must be written in TypeScript.
|
||||
2. Jest mocks are declared below imports, but above everything else.
|
||||
3. Use the following convention template:
|
||||
|
||||
```typescript
|
||||
// Describe the class, component, or file name.
|
||||
describe("FooComponent", () => {
|
||||
// all test inspecific variables go here
|
||||
|
||||
beforeEach(() => {
|
||||
// exclude if not used.
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// exclude if not used.
|
||||
});
|
||||
|
||||
// Use "it should..." terminology
|
||||
it("should call the correct API", async () => {
|
||||
// test-specific variables go here
|
||||
|
||||
// function calls/state changes go here
|
||||
|
||||
// expectations go here
|
||||
});
|
||||
});
|
||||
|
||||
// If the file being tested is a utility class:
|
||||
describe("foo-utils", () => {
|
||||
describe("firstUtilFunction", () => {
|
||||
it("should...", async () => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
|
||||
describe("secondUtilFunction", () => {
|
||||
it("should...", async () => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
22
docs/labs.md
22
docs/labs.md
@@ -108,10 +108,6 @@ To enable message previews for reactions in DMs only, enable `feature_roomlist_p
|
||||
Allows users to receive encrypted messages by creating a device that is stored
|
||||
encrypted on the server, as described in [MSC2697](https://github.com/matrix-org/matrix-doc/pull/2697).
|
||||
|
||||
## Hidden read receipts (`feature_hidden_read_receipts`)
|
||||
|
||||
Enables sending hidden read receipts as per [MSC2285](https://github.com/matrix-org/matrix-doc/pull/2285)
|
||||
|
||||
## Breadcrumbs v2 (`feature_breadcrumbs_v2`)
|
||||
|
||||
Instead of showing the horizontal list of breadcrumbs under the filter field, the new UX is an interactive context menu
|
||||
@@ -146,14 +142,6 @@ If no right panel state is known for the room or it was closed on the last room
|
||||
visit, it will default to the room member list. Otherwise, the saved card last
|
||||
used in that room is shown.
|
||||
|
||||
## Show current profile of users on historical messages (`feature_use_only_current_profiles`)
|
||||
|
||||
An experimental flag to determine how the app would behave if a user's current display
|
||||
name and avatar (profile) were shown on historical messages instead of the profile details
|
||||
at the time when the message was sent.
|
||||
|
||||
When enabled, historical messages will use the current profile for the sender.
|
||||
|
||||
## Pin drop location sharing (`feature_location_share_pin_drop`) [In Development]
|
||||
|
||||
Enables sharing a pin drop location to the timeline.
|
||||
@@ -170,12 +158,6 @@ Threads can be access by clicking their summary below the root event on the room
|
||||
|
||||
This feature might work in degraded mode if the homeserver a user is connected to does not advertise support for the unstable feature `org.matrix.msc3440` when calling the `/versions` API endpoint.
|
||||
|
||||
## Right-click Message Context Menu (`feature_message_right_click_context_menu`)
|
||||
|
||||
Enables showing a right-click context menu when right-clicking messages in the
|
||||
timeline. This menu shows options that can usually be found in the message
|
||||
action bar or in the message options.
|
||||
|
||||
## Video rooms (`feature_video_rooms`)
|
||||
|
||||
Enables support for creating and joining video rooms, which are persistent video chats that users can jump in and out of.
|
||||
@@ -188,3 +170,7 @@ Enables rendering of MD / HTML in room topics.
|
||||
|
||||
Enables exploring public spaces in the new search dialog. Requires the server to
|
||||
have [MSC3827](https://github.com/matrix-org/matrix-spec-proposals/pull/3827) enabled.
|
||||
|
||||
## Favourite Messages (`feature_favourite_messages`) [In Development]
|
||||
|
||||
Enables users to bookmark a message or content for a later reference.
|
||||
|
||||
48
docs/modules.md
Normal file
48
docs/modules.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Module system
|
||||
|
||||
The module system in Element Web is a way to add or modify functionality of Element Web itself, bundled at compile time
|
||||
for the app. This means that modules are loaded as part of the `yarn build` process but have an effect on user experience
|
||||
at runtime.
|
||||
|
||||
## Installing modules
|
||||
|
||||
If you already have a module you want to install, such as our [ILAG Module](https://github.com/vector-im/element-web-ilag-module),
|
||||
then copy `build_config.sample.yaml` to `build_config.yaml` in the same directory. In your new `build_config.yaml` simply
|
||||
add the reference to the module as described by the sample file, using the same syntax you would for `yarn add`:
|
||||
|
||||
```yaml
|
||||
modules:
|
||||
# Our module happens to be published on NPM, so we use that syntax to reference it.
|
||||
- "@vector-im/element-web-ilag-module@latest"
|
||||
```
|
||||
|
||||
Then build the app as you normally would: `yarn build` or `yarn dist` (if compatible on your platform). If you are building
|
||||
the Docker image then ensure your `build_config.yaml` ends up in the build directory. Usually this works fine if you use
|
||||
the current directory as the build context (the `.` in `docker build -t my-element-web .`).
|
||||
|
||||
## Writing modules
|
||||
|
||||
While writing modules is meant to be easy, not everything is possible yet. For modules which want to do something we haven't
|
||||
exposed in the module API, the module API will need to be updated. This means a PR to both the
|
||||
[`matrix-react-sdk`](https://github.com/matrix-org/matrix-react-sdk) and [`matrix-react-sdk-module-api`](https://github.com/matrix-org/matrix-react-sdk-module-api).
|
||||
|
||||
Once your change to the module API is accepted, the `@matrix-org/react-sdk-module-api` dependency gets updated at the
|
||||
`matrix-react-sdk` and `element-web` layers (usually by us, the maintainers) to ensure your module can operate.
|
||||
|
||||
If you're not adding anything to the module API, or your change was accepted per above, then start off with a clone of
|
||||
our [ILAG module](https://github.com/vector-im/element-web-ilag-module) which will give you a general idea for what the
|
||||
structure of a module is and how it works.
|
||||
|
||||
The following requirements are key for any module:
|
||||
1. The module must depend on `@matrix-org/react-sdk-module-api` (usually as a dev dependency).
|
||||
2. The module's `main` entrypoint must have a `default` export for the `RuntimeModule` instance, supporting a constructor
|
||||
which takes a single parameter: a `ModuleApi` instance. This instance is passed to `super()`.
|
||||
3. The module must be deployed in a way where `yarn add` can access it, as that is how the build system will try to
|
||||
install it. Note that while this is often NPM, it can also be a GitHub/GitLab repo or private NPM registry.
|
||||
|
||||
... and that's pretty much it. As with any code, please be responsible and call things in line with the documentation.
|
||||
Both `RuntimeModule` and `ModuleApi` have extensive documentation to describe what is proper usage and how to set things
|
||||
up.
|
||||
|
||||
If you have any questions then please visit [#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) on
|
||||
Matrix and we'll help as best we can.
|
||||
@@ -11,11 +11,11 @@ As of March 2022, skins are not fully supported; Element is the only available s
|
||||
To define a theme for Element:
|
||||
|
||||
1. Pick a name, e.g. `teal`. at time of writing we have `light` and `dark`.
|
||||
2. Fork `src/skins/vector/css/themes/dark.scss` to be `teal.scss`
|
||||
3. Fork `src/skins/vector/css/themes/_base.scss` to be `_teal.scss`
|
||||
4. Override variables in `_teal.scss` as desired. You may wish to delete ones
|
||||
which don't differ from `_base.scss`, to make it clear which are being
|
||||
overridden. If every single colour is being changed (as per `_dark.scss`)
|
||||
2. Fork `src/skins/vector/css/themes/dark.pcss` to be `teal.pcss`
|
||||
3. Fork `src/skins/vector/css/themes/_base.pcss` to be `_teal.pcss`
|
||||
4. Override variables in `_teal.pcss` as desired. You may wish to delete ones
|
||||
which don't differ from `_base.pcss`, to make it clear which are being
|
||||
overridden. If every single colour is being changed (as per `_dark.pcss`)
|
||||
then you might as well keep them all.
|
||||
5. Add the theme to the list of entrypoints in webpack.config.js
|
||||
6. Add the theme to the list of themes in matrix-react-sdk's UserSettings.js
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# download and unpack a element-web tarball.
|
||||
#
|
||||
# Allows `bundles` to be extracted to a common directory, and a link to
|
||||
# config.json to be added.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import shutil
|
||||
import glob
|
||||
|
||||
try:
|
||||
# python3
|
||||
from urllib.request import urlretrieve
|
||||
except ImportError:
|
||||
# python2
|
||||
from urllib import urlretrieve
|
||||
|
||||
class DeployException(Exception):
|
||||
pass
|
||||
|
||||
def create_relative_symlink(linkname, target):
|
||||
relpath = os.path.relpath(target, os.path.dirname(linkname))
|
||||
print ("Symlink %s -> %s" % (linkname, relpath))
|
||||
os.symlink(relpath, linkname)
|
||||
|
||||
|
||||
def move_bundles(source, dest):
|
||||
"""Move the contents of the 'bundles' directory to a common dir
|
||||
|
||||
We check that we will not be overwriting anything before we proceed.
|
||||
|
||||
Args:
|
||||
source (str): path to 'bundles' within the extracted tarball
|
||||
dest (str): target common directory
|
||||
"""
|
||||
|
||||
if not os.path.isdir(dest):
|
||||
os.mkdir(dest)
|
||||
|
||||
# build a map from source to destination, checking for non-existence as we go.
|
||||
renames = {}
|
||||
for f in os.listdir(source):
|
||||
dst = os.path.join(dest, f)
|
||||
if os.path.exists(dst):
|
||||
print (
|
||||
"Skipping bundle. The bundle includes '%s' which we have previously deployed."
|
||||
% f
|
||||
)
|
||||
else:
|
||||
renames[os.path.join(source, f)] = dst
|
||||
|
||||
for (src, dst) in renames.iteritems():
|
||||
print ("Move %s -> %s" % (src, dst))
|
||||
os.rename(src, dst)
|
||||
|
||||
class Deployer:
|
||||
def __init__(self):
|
||||
self.packages_path = "."
|
||||
self.bundles_path = None
|
||||
self.should_clean = False
|
||||
# filename -> symlink path e.g 'config.localhost.json' => '../localhost/config.json'
|
||||
self.symlink_paths = {}
|
||||
self.verify_signature = True
|
||||
|
||||
def deploy(self, tarball, extract_path):
|
||||
"""Download a tarball if necessary, and unpack it
|
||||
|
||||
Returns:
|
||||
(str) the path to the unpacked deployment
|
||||
"""
|
||||
print("Deploying %s to %s" % (tarball, extract_path))
|
||||
|
||||
name_str = os.path.basename(tarball).replace(".tar.gz", "")
|
||||
extracted_dir = os.path.join(extract_path, name_str)
|
||||
if os.path.exists(extracted_dir):
|
||||
raise DeployException('Cannot unpack %s: %s already exists' % (
|
||||
tarball, extracted_dir))
|
||||
|
||||
downloaded = False
|
||||
if tarball.startswith("http://") or tarball.startswith("https://"):
|
||||
tarball = self.download_and_verify(tarball)
|
||||
print("Downloaded file: %s" % tarball)
|
||||
downloaded = True
|
||||
|
||||
try:
|
||||
with tarfile.open(tarball) as tar:
|
||||
tar.extractall(extract_path)
|
||||
finally:
|
||||
if self.should_clean and downloaded:
|
||||
os.remove(tarball)
|
||||
|
||||
print ("Extracted into: %s" % extracted_dir)
|
||||
|
||||
if self.symlink_paths:
|
||||
for link_path, file_path in self.symlink_paths.iteritems():
|
||||
create_relative_symlink(
|
||||
target=file_path,
|
||||
linkname=os.path.join(extracted_dir, link_path)
|
||||
)
|
||||
|
||||
if self.bundles_path:
|
||||
extracted_bundles = os.path.join(extracted_dir, 'bundles')
|
||||
move_bundles(source=extracted_bundles, dest=self.bundles_path)
|
||||
|
||||
# replace the extracted_bundles dir (which may not be empty if some
|
||||
# bundles were skipped) with a symlink to the common dir.
|
||||
shutil.rmtree(extracted_bundles)
|
||||
create_relative_symlink(
|
||||
target=self.bundles_path,
|
||||
linkname=extracted_bundles,
|
||||
)
|
||||
return extracted_dir
|
||||
|
||||
def download_and_verify(self, url):
|
||||
tarball = self.download_file(url)
|
||||
|
||||
if self.verify_signature:
|
||||
sigfile = self.download_file(url + ".asc")
|
||||
subprocess.check_call(["gpg", "--verify", sigfile, tarball])
|
||||
|
||||
return tarball
|
||||
|
||||
def download_file(self, url):
|
||||
if not os.path.isdir(self.packages_path):
|
||||
os.mkdir(self.packages_path)
|
||||
local_filename = os.path.join(self.packages_path,
|
||||
url.split('/')[-1])
|
||||
sys.stdout.write("Downloading %s -> %s..." % (url, local_filename))
|
||||
sys.stdout.flush()
|
||||
urlretrieve(url, local_filename)
|
||||
print ("Done")
|
||||
return local_filename
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser("Deploy a Riot build on a web server.")
|
||||
parser.add_argument(
|
||||
"-p", "--packages-dir", default="./packages", help=(
|
||||
"The directory to download the tarball into. (Default: '%(default)s')"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e", "--extract-path", default="./deploys", help=(
|
||||
"The location to extract .tar.gz files to. (Default: '%(default)s')"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b", "--bundles-dir", nargs='?', default="./bundles", help=(
|
||||
"A directory to move the contents of the 'bundles' directory to. A \
|
||||
symlink to the bundles directory will also be written inside the \
|
||||
extracted tarball. Example: './bundles'. \
|
||||
(Default: '%(default)s')"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--clean", action="store_true", default=False, help=(
|
||||
"Remove .tar.gz files after they have been downloaded and extracted. \
|
||||
(Default: %(default)s)"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"--include", nargs='*', default=['./config*.json'], help=(
|
||||
"Symlink these files into the root of the deployed tarball. \
|
||||
Useful for config files and home pages. Supports glob syntax. \
|
||||
(Default: '%(default)s')"
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"tarball", help=(
|
||||
"filename of tarball, or URL to download."
|
||||
),
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
deployer = Deployer()
|
||||
deployer.packages_path = args.packages_dir
|
||||
deployer.bundles_path = args.bundles_dir
|
||||
deployer.should_clean = args.clean
|
||||
|
||||
for include in args.include:
|
||||
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
|
||||
|
||||
deployer.deploy(args.tarball, args.extract_path)
|
||||
33
module_system/BuildConfig.ts
Normal file
33
module_system/BuildConfig.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2022 New Vector Ltd.
|
||||
|
||||
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 * as YAML from "yaml";
|
||||
import * as fs from "fs";
|
||||
|
||||
export type BuildConfig = {
|
||||
// Dev note: make everything here optional for user safety. Invalid
|
||||
// configs are very possible.
|
||||
|
||||
// The module references to include in the build.
|
||||
modules?: string[];
|
||||
};
|
||||
|
||||
export function readBuildConfig(): BuildConfig {
|
||||
if (fs.existsSync("./build_config.yaml")) {
|
||||
return YAML.parse(fs.readFileSync("./build_config.yaml", "utf-8"));
|
||||
}
|
||||
return {}; // no config
|
||||
}
|
||||
191
module_system/installer.ts
Normal file
191
module_system/installer.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
Copyright 2022 New Vector Ltd.
|
||||
|
||||
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 * as fs from "fs";
|
||||
import * as childProcess from "child_process";
|
||||
import * as semver from "semver";
|
||||
|
||||
import { BuildConfig } from "./BuildConfig";
|
||||
|
||||
// This expects to be run from ./scripts/install.ts
|
||||
|
||||
const moduleApiDepName = "@matrix-org/react-sdk-module-api";
|
||||
|
||||
const MODULES_TS_HEADER = `
|
||||
/*
|
||||
* THIS FILE IS AUTO-GENERATED
|
||||
* You can edit it you like, but your changes will be overwritten,
|
||||
* so you'd just be trying to swim upstream like a salmon.
|
||||
* You are not a salmon.
|
||||
*/
|
||||
|
||||
import { RuntimeModule } from "@matrix-org/react-sdk-module-api/lib/RuntimeModule";
|
||||
`;
|
||||
const MODULES_TS_DEFINITIONS = `
|
||||
export const INSTALLED_MODULES: RuntimeModule[] = [];
|
||||
`;
|
||||
|
||||
export function installer(config: BuildConfig): void {
|
||||
if (!config.modules?.length) {
|
||||
// nothing to do
|
||||
writeModulesTs(MODULES_TS_HEADER + MODULES_TS_DEFINITIONS);
|
||||
return;
|
||||
}
|
||||
|
||||
let exitCode = 0;
|
||||
|
||||
// We cheat a bit and store the current package.json and lockfile so we can safely
|
||||
// run `yarn add` without creating extra committed files for people. We restore
|
||||
// these files by simply overwriting them when we're done.
|
||||
const packageDeps = readCurrentPackageDetails();
|
||||
|
||||
// Record which optional dependencies there are currently, if any, so we can exclude
|
||||
// them from our "must be a module" assumption later on.
|
||||
const currentOptDeps = getOptionalDepNames(packageDeps.packageJson);
|
||||
|
||||
try {
|
||||
// Install the modules with yarn
|
||||
const yarnAddRef = config.modules.join(" ");
|
||||
callYarnAdd(yarnAddRef); // install them all at once
|
||||
|
||||
// Grab the optional dependencies again and exclude what was there already. Everything
|
||||
// else must be a module, we assume.
|
||||
const pkgJsonStr = fs.readFileSync("./package.json", "utf-8");
|
||||
const optionalDepNames = getOptionalDepNames(pkgJsonStr);
|
||||
const installedModules = optionalDepNames.filter(d => !currentOptDeps.includes(d));
|
||||
|
||||
// Ensure all the modules are compatible. We check them all and report at the end to
|
||||
// try and save the user some time debugging this sort of failure.
|
||||
const ourApiVersion = getTopLevelDependencyVersion(moduleApiDepName);
|
||||
const incompatibleNames: string[] = [];
|
||||
for (const moduleName of installedModules) {
|
||||
const modApiVersion = getModuleApiVersionFor(moduleName);
|
||||
if (!isModuleVersionCompatible(ourApiVersion, modApiVersion)) {
|
||||
incompatibleNames.push(moduleName);
|
||||
}
|
||||
}
|
||||
if (incompatibleNames.length > 0) {
|
||||
console.error(
|
||||
"The following modules are not compatible with this version of element-web. Please update the module " +
|
||||
"references and try again.",
|
||||
JSON.stringify(incompatibleNames, null, 4), // stringify to get prettier/complete output
|
||||
);
|
||||
exitCode = 1;
|
||||
return; // hit the finally{} block before exiting
|
||||
}
|
||||
|
||||
// If we reach here, everything seems fine. Write modules.ts and log some output
|
||||
// Note: we compile modules.ts in two parts for developer friendliness if they
|
||||
// happen to look at it.
|
||||
console.log("The following modules have been installed: ", installedModules);
|
||||
let modulesTsHeader = MODULES_TS_HEADER;
|
||||
let modulesTsDefs = MODULES_TS_DEFINITIONS;
|
||||
let index = 0;
|
||||
for (const moduleName of installedModules) {
|
||||
const importName = `Module${++index}`;
|
||||
modulesTsHeader += `import ${importName} from "${moduleName}";\n`;
|
||||
modulesTsDefs += `INSTALLED_MODULES.push(${importName});\n`;
|
||||
}
|
||||
writeModulesTs(modulesTsHeader + modulesTsDefs);
|
||||
console.log("Done installing modules");
|
||||
} finally {
|
||||
// Always restore package details (or at least try to)
|
||||
writePackageDetails(packageDeps);
|
||||
|
||||
if (exitCode > 0) {
|
||||
process.exit(exitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RawDependencies = {
|
||||
lockfile: string;
|
||||
packageJson: string;
|
||||
};
|
||||
|
||||
function readCurrentPackageDetails(): RawDependencies {
|
||||
return {
|
||||
lockfile: fs.readFileSync("./yarn.lock", "utf-8"),
|
||||
packageJson: fs.readFileSync("./package.json", "utf-8"),
|
||||
};
|
||||
}
|
||||
|
||||
function writePackageDetails(deps: RawDependencies) {
|
||||
fs.writeFileSync("./yarn.lock", deps.lockfile, "utf-8");
|
||||
fs.writeFileSync("./package.json", deps.packageJson, "utf-8");
|
||||
}
|
||||
|
||||
function callYarnAdd(dep: string) {
|
||||
// Add the module to the optional dependencies section just in case something
|
||||
// goes wrong in restoring the original package details.
|
||||
childProcess.execSync(`yarn add -O ${dep}`, {
|
||||
env: process.env,
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
});
|
||||
}
|
||||
|
||||
function getOptionalDepNames(pkgJsonStr: string): string[] {
|
||||
return Object.keys(JSON.parse(pkgJsonStr)?.['optionalDependencies'] ?? {});
|
||||
}
|
||||
|
||||
function findDepVersionInPackageJson(dep: string, pkgJsonStr: string): string {
|
||||
const pkgJson = JSON.parse(pkgJsonStr);
|
||||
const packages = {
|
||||
...(pkgJson['optionalDependencies'] ?? {}),
|
||||
...(pkgJson['devDependencies'] ?? {}),
|
||||
...(pkgJson['dependencies'] ?? {}),
|
||||
};
|
||||
return packages[dep];
|
||||
}
|
||||
|
||||
function getTopLevelDependencyVersion(dep: string): string {
|
||||
const dependencyTree = JSON.parse(childProcess.execSync(`npm list ${dep} --depth=0 --json`, {
|
||||
env: process.env,
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
}).toString('utf-8'));
|
||||
|
||||
/*
|
||||
What a dependency tree looks like:
|
||||
{
|
||||
"version": "1.10.13",
|
||||
"name": "element-web",
|
||||
"dependencies": {
|
||||
"@matrix-org/react-sdk-module-api": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "file:../../../matrix-react-sdk-module-api"
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return dependencyTree["dependencies"][dep]["version"];
|
||||
}
|
||||
|
||||
function getModuleApiVersionFor(moduleName: string): string {
|
||||
// We'll just pretend that this isn't highly problematic...
|
||||
// Yarn is fairly stable in putting modules in a flat hierarchy, at least.
|
||||
const pkgJsonStr = fs.readFileSync(`./node_modules/${moduleName}/package.json`, "utf-8");
|
||||
return findDepVersionInPackageJson(moduleApiDepName, pkgJsonStr);
|
||||
}
|
||||
|
||||
function isModuleVersionCompatible(ourApiVersion: string, moduleApiVersion: string): boolean {
|
||||
if (!moduleApiVersion) return false;
|
||||
return semver.satisfies(ourApiVersion, moduleApiVersion);
|
||||
}
|
||||
|
||||
function writeModulesTs(content: string) {
|
||||
fs.writeFileSync("./src/modules.ts", content, "utf-8");
|
||||
}
|
||||
21
module_system/scripts/install.ts
Normal file
21
module_system/scripts/install.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2022 New Vector Ltd.
|
||||
|
||||
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 { readBuildConfig } from "../BuildConfig";
|
||||
import { installer } from "../installer";
|
||||
|
||||
const buildConf = readBuildConfig();
|
||||
installer(buildConf);
|
||||
61
package.json
61
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.11.0",
|
||||
"version": "1.11.4",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -35,32 +35,35 @@
|
||||
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
|
||||
"build:jitsi": "node scripts/build-jitsi.js",
|
||||
"build:res": "node scripts/copy-res.js",
|
||||
"build:genfiles": "yarn build:res && yarn build:jitsi",
|
||||
"build:genfiles": "yarn build:res && yarn build:jitsi && yarn build:module_system",
|
||||
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
|
||||
"build:bundle": "webpack --progress --bail --mode production",
|
||||
"build:bundle-stats": "webpack --progress --bail --mode production --json > webpack-stats.json",
|
||||
"build:module_system": "tsc --project ./tsconfig.module_system.json && node ./lib/module_system/scripts/install.js",
|
||||
"dist": "scripts/package.sh",
|
||||
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
|
||||
"start": "yarn build:module_system && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
|
||||
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --https\"",
|
||||
"start:res": "yarn build:jitsi && node scripts/copy-res.js -w",
|
||||
"start:js": "webpack-dev-server --host=0.0.0.0 --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --mode development --disable-host-check --hot",
|
||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
|
||||
"lint:js": "eslint --max-warnings 0 src",
|
||||
"lint:js-fix": "eslint --fix src",
|
||||
"lint:types": "tsc --noEmit --jsx react",
|
||||
"lint:style": "stylelint \"res/css/**/*.scss\"",
|
||||
"lint:js": "eslint --max-warnings 0 src module_system",
|
||||
"lint:js-fix": "eslint --fix src module_system",
|
||||
"lint:types": "tsc --noEmit --jsx react && tsc --noEmit --project ./tsconfig.module_system.json",
|
||||
"lint:style": "stylelint \"res/css/**/*.pcss\"",
|
||||
"test": "jest",
|
||||
"coverage": "yarn test --coverage"
|
||||
"coverage": "yarn test --coverage",
|
||||
"analyse:unused-exports": "node ./scripts/analyse_unused_exports.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
||||
"@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.3",
|
||||
"browser-request": "^0.3.3",
|
||||
"gfm.css": "^1.1.2",
|
||||
"jsrsasign": "^10.5.25",
|
||||
"katex": "^0.12.0",
|
||||
"matrix-js-sdk": "19.0.0",
|
||||
"matrix-react-sdk": "3.48.0",
|
||||
"matrix-widget-api": "^0.1.0-beta.18",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
|
||||
"matrix-widget-api": "^1.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
@@ -88,7 +91,7 @@
|
||||
"@sentry/webpack-plugin": "^1.18.1",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@types/flux": "^3.1.9",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/jest": "^28.0.0",
|
||||
"@types/modernizr": "^3.5.3",
|
||||
"@types/node": "^14.14.22",
|
||||
"@types/react": "17.0.14",
|
||||
@@ -99,7 +102,7 @@
|
||||
"@typescript-eslint/parser": "^5.6.0",
|
||||
"allchange": "^1.0.6",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-jest": "^28.0.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"chokidar": "^3.5.1",
|
||||
"concurrently": "^5.3.0",
|
||||
@@ -108,8 +111,9 @@
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "8.9.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-deprecate": "^0.7.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-matrix-org": "^0.4.0",
|
||||
"eslint-plugin-matrix-org": "^0.6.1",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
@@ -117,8 +121,8 @@
|
||||
"file-loader": "^5.1.0",
|
||||
"fs-extra": "^0.30.0",
|
||||
"html-webpack-plugin": "^4.5.2",
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-jsdom-sixteen": "^1.0.3",
|
||||
"jest": "^28.0.0",
|
||||
"jest-environment-jsdom": "^28.1.3",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jest-sonar-reporter": "^2.0.0",
|
||||
"json-loader": "^0.5.7",
|
||||
@@ -133,7 +137,7 @@
|
||||
"node-fetch": "^2.6.7",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||
"postcss-easings": "^2.0.0",
|
||||
"postcss-hexrgba": "^2.0.1",
|
||||
"postcss-hexrgba": "2.0.1",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-mixins": "^6.2.3",
|
||||
@@ -141,29 +145,34 @@
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"postcss-scss": "^2.1.1",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"postcss-strip-inline-comments": "^0.1.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.7",
|
||||
"shell-escape": "^0.2.0",
|
||||
"simple-proxy-agent": "^1.1.0",
|
||||
"string-replace-loader": "2",
|
||||
"style-loader": "2",
|
||||
"stylelint": "^13.9.0",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"stylelint-scss": "^3.18.0",
|
||||
"stylelint": "^14.9.1",
|
||||
"stylelint-config-standard": "^26.0.0",
|
||||
"stylelint-scss": "^4.2.0",
|
||||
"terser-webpack-plugin": "^2.3.8",
|
||||
"typescript": "^4.5.3",
|
||||
"ts-prune": "^0.10.3",
|
||||
"typescript": "^4.7.4",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"worker-loader": "^2.0.0",
|
||||
"worklet-loader": "^2.0.0"
|
||||
"worklet-loader": "^2.0.0",
|
||||
"yaml": "^2.0.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.14"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jest-environment-jsdom-sixteen",
|
||||
"testEnvironment": "jsdom",
|
||||
"testEnvironmentOptions": {
|
||||
"url": "http://localhost/"
|
||||
},
|
||||
"testMatch": [
|
||||
"<rootDir>/test/**/*-test.[tj]s?(x)"
|
||||
],
|
||||
@@ -171,7 +180,7 @@
|
||||
"<rootDir>/node_modules/matrix-react-sdk/test/setupTests.js"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|scss)$": "<rootDir>/__mocks__/cssMock.js",
|
||||
"\\.(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",
|
||||
|
||||
@@ -14,16 +14,28 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// import font-size variables manually, ideally this scss would get loaded by the theme which has all variables in context
|
||||
@import "../../../node_modules/matrix-react-sdk/res/css/_font-sizes.scss";
|
||||
// import font-size variables manually,
|
||||
// ideally this file would get loaded by the theme which has all variables in context
|
||||
@import "../../../node_modules/matrix-react-sdk/res/css/_font-sizes.pcss";
|
||||
|
||||
.mx_ErrorView {
|
||||
background: #c5e0f7;
|
||||
background: -moz-linear-gradient(top, #c5e0f7 0%, #ffffff 100%);
|
||||
background: -webkit-linear-gradient(top, #c5e0f7 0%, #ffffff 100%);
|
||||
background: linear-gradient(to bottom, #c5e0f7 0%, #ffffff 100%);
|
||||
/* stylelint-disable-next-line function-no-unknown */
|
||||
filter: progid:dximagetransform.microsoft.gradient(startColorstr='#c5e0f7', endColorstr='#ffffff', GradientType=0);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-family:
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
Helvetica,
|
||||
Arial,
|
||||
sans-serif,
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol";
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
48
scripts/analyse_unused_exports.js
Executable file
48
scripts/analyse_unused_exports.js
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fs = require("fs");
|
||||
const { exec } = require("node:child_process");
|
||||
|
||||
const includeJSSDK = process.argv.includes("--include-js-sdk");
|
||||
const ignore = [];
|
||||
|
||||
ignore.push(...Object.values(JSON.parse(fs.readFileSync(`${__dirname}/../components.json`))));
|
||||
ignore.push("/index.ts");
|
||||
// We ignore js-sdk by default as it may export for other non element-web projects
|
||||
if (!includeJSSDK) ignore.push("matrix-js-sdk");
|
||||
|
||||
const command = `yarn ts-prune --ignore "${ignore.join("|")}" | grep -v "(used in module)"`;
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) throw error;
|
||||
// We have to do this as piping the output of ts-prune causes the return
|
||||
// code to be 0
|
||||
if (stderr) throw Error(stderr);
|
||||
|
||||
let lines = stdout.split("\n");
|
||||
// Remove the first line as that is the command that was being run and we
|
||||
// log that only in case of an error
|
||||
lines.splice(0, 1);
|
||||
// Remove the last line as it is empty
|
||||
lines.pop();
|
||||
|
||||
// ts-prune has bug where if the unused export is in a dependency, the path
|
||||
// won't have an "/" character at the start, so we try to fix that for
|
||||
// better UX
|
||||
// TODO: This might break on Windows
|
||||
lines = lines.reduce((newLines, line) => {
|
||||
if (!line.startsWith("/")) newLines.push("/" + line);
|
||||
else newLines.push(line);
|
||||
return newLines;
|
||||
}, []);
|
||||
|
||||
// If an unused export has been found, we error
|
||||
if (lines.length > 0) {
|
||||
console.log(`Command that was run: ${command}`);
|
||||
console.log(lines.join("\n"));
|
||||
throw Error("Unused exports found!");
|
||||
}
|
||||
|
||||
console.log("Success - no unused exports found!");
|
||||
});
|
||||
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# download and unpack a element-web tarball.
|
||||
#
|
||||
# Allows `bundles` to be extracted to a common directory, and a link to
|
||||
# config.json to be added.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import errno
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
@@ -15,21 +14,26 @@ import sys
|
||||
import tarfile
|
||||
import shutil
|
||||
import glob
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
try:
|
||||
# python3
|
||||
from urllib.request import urlretrieve
|
||||
except ImportError:
|
||||
# python2
|
||||
from urllib import urlretrieve
|
||||
|
||||
class DeployException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def create_relative_symlink(linkname, target):
|
||||
relpath = os.path.relpath(target, os.path.dirname(linkname))
|
||||
print ("Symlink %s -> %s" % (linkname, relpath))
|
||||
os.symlink(relpath, linkname)
|
||||
print("Symlink %s -> %s" % (linkname, relpath))
|
||||
|
||||
try:
|
||||
os.symlink(relpath, linkname)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
# atomic modification
|
||||
os.symlink(relpath, linkname + ".tmp")
|
||||
os.rename(linkname + ".tmp", linkname)
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
def move_bundles(source, dest):
|
||||
@@ -50,33 +54,35 @@ def move_bundles(source, dest):
|
||||
for f in os.listdir(source):
|
||||
dst = os.path.join(dest, f)
|
||||
if os.path.exists(dst):
|
||||
print (
|
||||
print(
|
||||
"Skipping bundle. The bundle includes '%s' which we have previously deployed."
|
||||
% f
|
||||
)
|
||||
else:
|
||||
renames[os.path.join(source, f)] = dst
|
||||
|
||||
for (src, dst) in renames.iteritems():
|
||||
print ("Move %s -> %s" % (src, dst))
|
||||
for (src, dst) in renames.items():
|
||||
print("Move %s -> %s" % (src, dst))
|
||||
os.rename(src, dst)
|
||||
|
||||
|
||||
class Deployer:
|
||||
def __init__(self):
|
||||
self.packages_path = "."
|
||||
self.bundles_path = None
|
||||
self.should_clean = False
|
||||
self.symlink_latest = None
|
||||
# filename -> symlink path e.g 'config.localhost.json' => '../localhost/config.json'
|
||||
self.symlink_paths = {}
|
||||
self.verify_signature = True
|
||||
|
||||
def deploy(self, tarball, extract_path):
|
||||
"""Download a tarball if necessary, and unpack it
|
||||
def fetch(self, tarball, extract_path):
|
||||
"""Download a tarball, verifies it if needed, and unpacks it
|
||||
|
||||
Returns:
|
||||
(str) the path to the unpacked deployment
|
||||
(str) the path to the unpacked directory
|
||||
"""
|
||||
print("Deploying %s to %s" % (tarball, extract_path))
|
||||
print("Fetching %s to %s" % (tarball, extract_path))
|
||||
|
||||
name_str = os.path.basename(tarball).replace(".tar.gz", "")
|
||||
extracted_dir = os.path.join(extract_path, name_str)
|
||||
@@ -97,10 +103,15 @@ class Deployer:
|
||||
if self.should_clean and downloaded:
|
||||
os.remove(tarball)
|
||||
|
||||
print ("Extracted into: %s" % extracted_dir)
|
||||
print("Extracted into: %s" % extracted_dir)
|
||||
return extracted_dir
|
||||
|
||||
def deploy(self, extracted_dir):
|
||||
"""Applies symlinks and handles the bundles directory on an extracted tarball"""
|
||||
print("Deploying %s" % extracted_dir)
|
||||
|
||||
if self.symlink_paths:
|
||||
for link_path, file_path in self.symlink_paths.iteritems():
|
||||
for link_path, file_path in self.symlink_paths.items():
|
||||
create_relative_symlink(
|
||||
target=file_path,
|
||||
linkname=os.path.join(extracted_dir, link_path)
|
||||
@@ -117,7 +128,12 @@ class Deployer:
|
||||
target=self.bundles_path,
|
||||
linkname=extracted_bundles,
|
||||
)
|
||||
return extracted_dir
|
||||
|
||||
if self.symlink_latest:
|
||||
create_relative_symlink(
|
||||
target=extracted_dir,
|
||||
linkname=self.symlink_latest,
|
||||
)
|
||||
|
||||
def download_and_verify(self, url):
|
||||
tarball = self.download_file(url)
|
||||
@@ -139,6 +155,7 @@ class Deployer:
|
||||
print ("Done")
|
||||
return local_filename
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser("Deploy a Riot build on a web server.")
|
||||
parser.add_argument(
|
||||
@@ -173,8 +190,15 @@ if __name__ == "__main__":
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"tarball", help=(
|
||||
"filename of tarball, or URL to download."
|
||||
"-s", "--symlink", dest="symlink", default="./latest", help=(
|
||||
"Write a symlink to this location pointing to the extracted tarball. \
|
||||
New builds will keep overwriting this symlink. The symlink will point \
|
||||
to the webapp directory INSIDE the tarball."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"target", help=(
|
||||
"filename of extracted directory, tarball, or URL to download."
|
||||
),
|
||||
)
|
||||
|
||||
@@ -184,8 +208,18 @@ if __name__ == "__main__":
|
||||
deployer.packages_path = args.packages_dir
|
||||
deployer.bundles_path = args.bundles_dir
|
||||
deployer.should_clean = args.clean
|
||||
deployer.symlink_latest = args.symlink
|
||||
|
||||
for include in args.include:
|
||||
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
|
||||
|
||||
deployer.deploy(args.tarball, args.extract_path)
|
||||
if os.path.isdir(args.target):
|
||||
# If the given directory contains a single directory then use that instead, the ci package wraps in an extra dir
|
||||
files = os.listdir(args.target)
|
||||
if len(files) == 1 and os.path.isdir(os.path.join(args.target, files[0])):
|
||||
extracted_dir = os.path.join(args.target, files[0])
|
||||
else:
|
||||
extracted_dir = args.target
|
||||
else:
|
||||
extracted_dir = deployer.fetch(args.target, args.extract_path)
|
||||
deployer.deploy(extracted_dir)
|
||||
|
||||
@@ -5,7 +5,7 @@ set -ex
|
||||
# Automatically link to develop if we're building develop, but only if the caller
|
||||
# hasn't asked us to build something else
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
if [ $USE_CUSTOM_SDKS == false ] && [ $BRANCH == 'develop' ]
|
||||
if [[ $USE_CUSTOM_SDKS == false ]] && [[ $BRANCH == 'develop' ]]
|
||||
then
|
||||
echo "using develop dependencies for react-sdk and js-sdk"
|
||||
USE_CUSTOM_SDKS=true
|
||||
@@ -13,21 +13,21 @@ then
|
||||
REACT_SDK_BRANCH='develop'
|
||||
fi
|
||||
|
||||
if [ $USE_CUSTOM_SDKS == false ]
|
||||
if [[ $USE_CUSTOM_SDKS == false ]]
|
||||
then
|
||||
echo "skipping react-sdk and js-sdk installs: USE_CUSTOM_SDKS is false"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Linking js-sdk"
|
||||
git clone --depth 1 --branch $JS_SDK_BRANCH $JS_SDK_REPO js-sdk
|
||||
git clone --depth 1 --branch $JS_SDK_BRANCH "$JS_SDK_REPO" js-sdk
|
||||
cd js-sdk
|
||||
yarn link
|
||||
yarn --network-timeout=100000 install
|
||||
cd ../
|
||||
|
||||
echo "Linking react-sdk"
|
||||
git clone --depth 1 --branch $REACT_SDK_BRANCH $REACT_SDK_REPO react-sdk
|
||||
git clone --depth 1 --branch $REACT_SDK_BRANCH "$REACT_SDK_REPO" react-sdk
|
||||
cd react-sdk
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
TAG=$(git describe --tags)
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
DIST_VERSION=$TAG
|
||||
DIST_VERSION=$(git describe --abbrev=0 --tags)
|
||||
|
||||
# If the branch comes out as HEAD then we're probably checked out to a tag, so if the thing is *not*
|
||||
# coming out as HEAD then we're on a branch. When we're on a branch, we want to resolve ourselves to
|
||||
# a few SHAs rather than a version.
|
||||
# Docker Hub doesn't always check out the tag and sometimes checks out the branch, so we should look
|
||||
# for an appropriately tagged branch as well (heads/v1.2.3).
|
||||
if [[ $BRANCH != HEAD && ! $BRANCH =~ heads/v.+ ]]
|
||||
DIR=$(dirname "$0")
|
||||
|
||||
# If we're not using custom SDKs and on a branch other than master, generate a version akin go develop.element.io
|
||||
if [[ $USE_CUSTOM_SDKS == false ]] && [[ $BRANCH != 'master' ]]
|
||||
then
|
||||
DIST_VERSION=`$(dirname $0)/get-version-from-git.sh`
|
||||
DIST_VERSION=$("$DIR"/get-version-from-git.sh)
|
||||
fi
|
||||
|
||||
DIST_VERSION=`$(dirname $0)/normalize-version.sh ${DIST_VERSION}`
|
||||
DIST_VERSION=$("$DIR"/normalize-version.sh "$DIST_VERSION")
|
||||
VERSION=$DIST_VERSION yarn build
|
||||
echo $DIST_VERSION > /src/webapp/version
|
||||
echo "$DIST_VERSION" > /src/webapp/version
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
# - flask
|
||||
#
|
||||
from __future__ import print_function
|
||||
import json, requests, tarfile, argparse, os, errno
|
||||
import requests, argparse, os, errno
|
||||
import time
|
||||
import traceback
|
||||
from urlparse import urljoin
|
||||
import glob
|
||||
import re
|
||||
import shutil
|
||||
@@ -30,22 +29,11 @@ app = Flask(__name__)
|
||||
|
||||
deployer = None
|
||||
arg_extract_path = None
|
||||
arg_symlink = None
|
||||
arg_webhook_token = None
|
||||
arg_api_token = None
|
||||
|
||||
workQueue = Queue()
|
||||
|
||||
def create_symlink(source, linkname):
|
||||
try:
|
||||
os.symlink(source, linkname)
|
||||
except OSError, e:
|
||||
if e.errno == errno.EEXIST:
|
||||
# atomic modification
|
||||
os.symlink(source, linkname + ".tmp")
|
||||
os.rename(linkname + ".tmp", linkname)
|
||||
else:
|
||||
raise e
|
||||
|
||||
def req_headers():
|
||||
return {
|
||||
@@ -128,7 +116,7 @@ def on_receive_buildkite_poke():
|
||||
artifacts_resp = requests.get(artifacts_url, headers=req_headers())
|
||||
artifacts_resp.raise_for_status()
|
||||
artifacts_array = artifacts_resp.json()
|
||||
|
||||
|
||||
artifact_to_deploy = None
|
||||
for artifact in artifacts_array:
|
||||
if re.match(r"dist/.*.tar.gz", artifact['path']):
|
||||
@@ -173,7 +161,6 @@ def deploy_buildkite_artifact(artifact, pipeline_name, build_num):
|
||||
traceback.print_exc()
|
||||
abort(400, e.message)
|
||||
|
||||
create_symlink(source=extracted_dir, linkname=arg_symlink)
|
||||
|
||||
def deploy_tarball(artifact, build_dir):
|
||||
"""Download a tarball from jenkins and unpack it
|
||||
@@ -274,7 +261,6 @@ if __name__ == "__main__":
|
||||
|
||||
args = parser.parse_args()
|
||||
arg_extract_path = args.extract
|
||||
arg_symlink = args.symlink
|
||||
arg_webbook_token = args.webhook_token
|
||||
arg_api_token = args.api_token
|
||||
arg_buildkite_org = args.buildkite_org
|
||||
@@ -285,6 +271,7 @@ if __name__ == "__main__":
|
||||
deployer = Deployer()
|
||||
deployer.bundles_path = args.bundles_dir
|
||||
deployer.should_clean = args.clean
|
||||
deployer.symlink_latest = args.symlink
|
||||
|
||||
for include in args.include:
|
||||
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
|
||||
@@ -298,7 +285,7 @@ if __name__ == "__main__":
|
||||
(args.port,
|
||||
arg_extract_path,
|
||||
" (clean after)" if deployer.should_clean else "",
|
||||
arg_symlink,
|
||||
args.symlink,
|
||||
deployer.symlink_paths,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -19,8 +19,8 @@ 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
|
||||
// scss variables will be accessible.
|
||||
import "../../../res/css/structures/ErrorView.scss";
|
||||
// PostCSS variables will be accessible.
|
||||
import "../../../res/css/structures/ErrorView.pcss";
|
||||
|
||||
interface IProps {
|
||||
onAccept(): void;
|
||||
|
||||
@@ -18,8 +18,8 @@ import * as React from "react";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
|
||||
// directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk
|
||||
// scss variables will be accessible.
|
||||
import "../../../res/css/structures/ErrorView.scss";
|
||||
// PostCSS variables will be accessible.
|
||||
import "../../../res/css/structures/ErrorView.pcss";
|
||||
|
||||
interface IProps {
|
||||
// both of these should already be internationalised
|
||||
|
||||
@@ -37,10 +37,10 @@ const VectorAuthFooter = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_AuthFooter">
|
||||
<footer className="mx_AuthFooter" role="contentinfo">
|
||||
{ authFooterLinks }
|
||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t('Powered by Matrix') }</a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ export default class VectorAuthHeaderLogo extends React.PureComponent {
|
||||
const logoUrl = brandingConfig?.get("auth_header_logo_url") ?? "themes/element/img/logos/element-logo.svg";
|
||||
|
||||
return (
|
||||
<div className="mx_AuthHeaderLogo">
|
||||
<aside className="mx_AuthHeaderLogo">
|
||||
<img src={logoUrl} alt="Element" />
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class Favicon {
|
||||
|
||||
private isReady = false;
|
||||
// callback to run once isReady is asserted, allows for a badge to be queued for when it can be shown
|
||||
private readyCb = () => {};
|
||||
private readyCb?: () => void;
|
||||
|
||||
constructor(params: Partial<IParams> = {}) {
|
||||
this.params = { ...defaults, ...params };
|
||||
@@ -180,7 +180,7 @@ export default class Favicon {
|
||||
private ready() {
|
||||
if (this.isReady) return;
|
||||
this.isReady = true;
|
||||
this.readyCb();
|
||||
this.readyCb?.();
|
||||
}
|
||||
|
||||
private setIcon(canvas) {
|
||||
@@ -230,9 +230,9 @@ export default class Favicon {
|
||||
private static getLinks() {
|
||||
const icons: HTMLLinkElement[] = [];
|
||||
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
|
||||
for (let i = 0; i < links.length; i++) {
|
||||
if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute("rel"))) {
|
||||
icons.push(links[i]);
|
||||
for (const link of links) {
|
||||
if ((/(^|\s)icon(\s|$)/i).test(link.getAttribute("rel"))) {
|
||||
icons.push(link);
|
||||
}
|
||||
}
|
||||
return icons;
|
||||
|
||||
@@ -28,5 +28,6 @@
|
||||
"Failed to start": "فشل البدء",
|
||||
"Powered by Matrix": "تدعمه «ماترِكس»",
|
||||
"Use %(brand)s on mobile": "استعمل %(brand)s على المحمول",
|
||||
"Switch to space by number": "التبديل إلى المساحة بالرقم",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "محادثة لامركزية، مشفرة & تعمل بواسطة $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"The message from the parser is: %(message)s": "Sözügedən mesaj: %(message)s",
|
||||
"Dismiss": "Nəzərə almayın",
|
||||
"Welcome to Element": "Element-ə xoş gəlmişsiniz",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "[matrix] tərəfindən təchiz edilmiş mərkəziləşdirilməmiş, şifrələnmiş çat və əməkdaşlıq platforması",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "$matrixLogo tərəfindən dəstəklənən mərkəzləşdirilməmiş ,şifrələnmiş söhbət & əməkdaşlıq",
|
||||
"Failed to start": "Başlatmaq alınmadı",
|
||||
"Go to element.io": "element.io saytına keçin",
|
||||
|
||||
@@ -27,5 +27,6 @@
|
||||
"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",
|
||||
"Use %(brand)s on mobile": "Използвайте %(brand)s на мобилен телефон"
|
||||
"Use %(brand)s on mobile": "Използвайте %(brand)s на мобилен телефон",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Децентрализиран, криптиран чат & сътрудничество, захранено от $matrixlogo"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"Dismiss": "Afvis",
|
||||
"powered by Matrix": "Drevet af Matrix",
|
||||
"Unknown device": "Ukendt enhed",
|
||||
"Welcome to Element": "Velkommen til Element",
|
||||
"The message from the parser is: %(message)s": "Beskeden fra parseren er: %(message)s",
|
||||
|
||||
@@ -28,5 +28,6 @@
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s Desktop (%(platformName)s)",
|
||||
"Use %(brand)s on mobile": "Χρήση %(brand)s σε κινητό",
|
||||
"Switch to space by number": "Εναλλαγή σε space με αριθμό",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Αποκεντρωμένη, κρυπτογραφημένη συνομιλία και συνεργασία χρησιμοποιώντας το $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -27,5 +27,6 @@
|
||||
"Go to element.io": "برو به element.io",
|
||||
"Failed to start": "خطا در شروع",
|
||||
"Powered by Matrix": "راه اندازی شده با استفاده از ماتریکس",
|
||||
"Use %(brand)s on mobile": "از %(brand)s گوشی استفاده کنید"
|
||||
"Use %(brand)s on mobile": "از %(brand)s گوشی استفاده کنید",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "همکاری چت غیرمتمرکز و رمزگذاری شده & توسعه یافته با استفاده از $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s שולחן עבודה %(platformName)s",
|
||||
"The message from the parser is: %(message)s": "ההודעה מהמנתח היא: %(message)s",
|
||||
"Missing indexeddb worker script!": "סקריפט indexeddb worker חסר!",
|
||||
"Switch to space by number": "עבור 'למרחב' על פי המספר שלו",
|
||||
"Use %(brand)s on mobile": "השתמש ב-%(brand)s במכשיר הנייד",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "צ'אט מבוזר ומוצפן & מופעל בשיתוף פעולה ע\"י $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"Unable to load config file: please refresh the page to try again.": "A konfigurációs fájlt nem sikerült betölteni: frissítse az oldalt és próbálja meg újra.",
|
||||
"%(brand)s Desktop (%(platformName)s)": "Asztali %(brand)s (%(platformName)s)",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"Unsupported browser": "A böngésző nem támogatott",
|
||||
"Unsupported browser": "Nem támogatott böngésző",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "A legjobb élmény érdékében telepítsen <chromeLink>Chrome-ot</chromeLink>, <firefoxLink>Firefoxot</firefoxLink> vagy <safariLink>Safarit</safariLink>.",
|
||||
"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.": "Folytathatja a jelenlegi böngészőjével, de néhány vagy az összes funkció használhatatlan lehet, vagy hibák lehetnek az alkalmazás kinézetében és viselkedésében.",
|
||||
"I understand the risks and wish to continue": "Megértettem a kockázatot és folytatom",
|
||||
@@ -25,8 +25,8 @@
|
||||
"Your Element is misconfigured": "Az Element hibásan van beállítva",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Az Element érvénytelen JSON-t tartalmazó konfigurációval rendelkezik. Javítsa és töltse újra az oldalt.",
|
||||
"Your browser can't run %(brand)s": "A böngészője nem tudja futtatni ezt: %(brand)s",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s speciális böngészőfunkciókat használ, amelyeket a jelenlegi böngészője nem támogat.",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "A(z) %(brand)s speciális böngészőfunkciókat használ, amelyeket a jelenlegi böngészője nem támogat.",
|
||||
"Powered by Matrix": "A gépházban: Matrix",
|
||||
"Use %(brand)s on mobile": "Mobilon használd ezt: %(brand)s",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Elosztott, titkosított csevegés & együttműködés ezzel: $matrixLogo"
|
||||
"Use %(brand)s on mobile": "Mobilon használja ezt: %(brand)s",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Elosztott, titkosított csevegés és együttműködés ezzel: $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"Unknown device": "알 수 없는 기기",
|
||||
"Welcome to Element": "Element에 오신 것을 환영합니다",
|
||||
"The message from the parser is: %(message)s": "파서에서 온 메시지: %(message)s",
|
||||
"Invalid JSON": "잘못된 JSON",
|
||||
"Invalid JSON": "유효하지 않은 JSON",
|
||||
"Unexpected error preparing the app. See console for details.": "앱을 준비하는 동안 예기치 않은 오류가 발생했습니다. 자세한 내용은 콘솔을 확인하세요.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "잘못된 설정: default_server_config 와 defalut_server_name, default_hs_url 중 하나만 지정할 수 있습니다.",
|
||||
"Invalid configuration: no default server specified.": "잘못된 설정: 기본 서버가 지정되지 않았습니다.",
|
||||
@@ -16,9 +16,9 @@
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"Unsupported browser": "지원되지 않는 브라우저",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "최상의 경험을 위해 <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, 또는 <safariLink>Safari</safariLink>를 설치해주세요.",
|
||||
"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.": "현재 사용중이신 브라우저를 계속 사용하셔도 됩니다, 다만 일부 기능들이 작동하지 않을 수 있으며 애플리케이션이 잘못돼 보일 수 있습니다.",
|
||||
"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.": "현재 사용 중인 브라우저를 계속 사용할 수 있지만, 일부 기능들이 작동하지 않거나 애플리케이션이 올바르게 보여지지 않을 수 있습니다.",
|
||||
"I understand the risks and wish to continue": "위험하다는 것을 이해했으며 계속하고 싶습니다",
|
||||
"Go to element.io": "element.io으로 가기",
|
||||
"Go to element.io": "element.io 로 이동",
|
||||
"Failed to start": "시작 실패",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s 는 당신의 브라우저에서 지원되지 않는 고급 기능을 사용합니다.",
|
||||
"Your browser can't run %(brand)s": "당신의 브라우저는 %(brand)s 를 작동할 수 없습니다",
|
||||
|
||||
@@ -28,5 +28,8 @@
|
||||
"Your Element is misconfigured": "Element jest nieprawidłowo skonfigurowany",
|
||||
"Powered by Matrix": "Zasilane przez Matrix",
|
||||
"Use %(brand)s on mobile": "Użyj %(brand)s w telefonie",
|
||||
"Switch to space by number": "Przełącz na przestrzeń według numeru",
|
||||
"Next recently visited room or community": "Następne ostatnio odwiedzone pokoje i społeczności",
|
||||
"Previous recently visited room or community": "Ostatnio odwiedzone pokoje i społeczności",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Zdecentralizowany, szyfrowany czat i współpraca wspierana przez $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"Unsupported browser": "Неподдерживаемый браузер",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Пожалуйста поставьте <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, или <safariLink>Safari</safariLink> для лучшей совместимости.",
|
||||
"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.": "Вы можете продолжать пользоваться этим браузером но некоторые возможности будут недоступны и интерфейс может быть отрисован неправильно.",
|
||||
"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.": "Вы можете продолжать пользоваться этим браузером, но некоторые возможности будут недоступны и интерфейс может быть отрисован неправильно.",
|
||||
"I understand the risks and wish to continue": "Я понимаю риск и хочу продолжить",
|
||||
"Go to element.io": "К element.io",
|
||||
"Failed to start": "Старт не удался",
|
||||
|
||||
@@ -13,5 +13,20 @@
|
||||
"Your browser can't run %(brand)s": "ඔබගේ අතිරික්සුවට %(brand)s ධාවනය කළ නොහැකිය",
|
||||
"Unsupported browser": "සහය නොදක්වන අතිරික්සුව කි",
|
||||
"Go to your browser to complete Sign In": "පිවිසීම සම්පූර්ණ කිරීමට ඔබගේ අතිරික්සුව වෙත යන්න",
|
||||
"Download Completed": "බාගැනීම සම්පූර්ණයි"
|
||||
"Download Completed": "බාගැනීම සම්පූර්ණයි",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "ඔබගේ වත්මන් අතිරික්සුව සහාය නොදක්වන උසස් විශේෂාංග %(brand)s භාවිත කරයි.",
|
||||
"The message from the parser is: %(message)s": "විග්රහය වෙතින් පණිවිඩය: %(message)s",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "ඔබගේ ඉලෙමන්ට් වින්යාසයෙහි වැරදි JSON අඩංගුය. ගැටලුව නිවැරදි කර පිටුව නැවුම් කරන්න.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "වින්යාසය වැරදිය: default_server_config, default_server_name, හෝ default_hs_url න් එකක් සඳහන් කළ හැකිය.",
|
||||
"Invalid configuration: no default server specified.": "වින්යාසය වලංගු නොවේ: පෙරනිමි සේවාදායකයක් දක්වා නැත.",
|
||||
"Your Element is misconfigured": "ඉලෙමන්ට් වැරදියට වින්යාසගතයි",
|
||||
"Unable to load config file: please refresh the page to try again.": "වින්යාස ගොනුව පූරණය කළ නොහැකිය: පිටුව නැවුම් කරන්න.",
|
||||
"Unexpected error preparing the app. See console for details.": "යෙදුම සූදානමේදී අනපේක්ෂිත දෝෂයකි. විස්තර සඳහා හසුරුවම බලන්න.",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "ඉහළ අත්දැකීමකට <chromeLink>ක්රෝම්</chromeLink>, <firefoxLink>ෆයර්ෆොකස්</firefoxLink>, හෝ <safariLink>සෆාරි</safariLink> ස්ථාපනය කරන්න.",
|
||||
"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.": "වත්මන් අතිරික්සුව දිගටම භාවිතා කළ හැකිය, නමුත් සමහර හෝ සියළුම විශේෂාංග ක්රියා නොකරන අතර යෙදුමේ පෙනුම වෙනස් විය හැකිය.",
|
||||
"I understand the risks and wish to continue": "අවදානම වැටහේ, ඉදිරියට යාමට කැමැත්තෙමි",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "විමධ්යගත, සංකේතිත කතාබහ සහ amp; $matrixLogo මගින් බලගැන්වූ සහයෝගිත්වය",
|
||||
"Use %(brand)s on mobile": "දුරකථනය සඳහා %(brand)s",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s වැඩතලය (%(platformName)s)",
|
||||
"Invalid JSON": "JSON වලංගු නොවේ"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
{
|
||||
"Dismiss": "రద్దుచేసే",
|
||||
"Unknown device": "తెలుయని పరికరం"
|
||||
"Dismiss": "రద్దుచేయి",
|
||||
"Unknown device": "తెలియని పరికరము",
|
||||
"Go to element.io": "element.io కు వెళ్ళు",
|
||||
"I understand the risks and wish to continue": "నాకు పర్యవసానాలు తెలిసే ముందుకు కొనసాగుతా",
|
||||
"Explore rooms": "గదులను అన్వేెషించు",
|
||||
"Welcome to Element": "ఎలిమెంట్ కు స్వాగతం",
|
||||
"Failed to start": "ప్రారంభించుటలో విఫలం",
|
||||
"Create Account": "ఖాతా తెరువు",
|
||||
"Open": "తెరువు",
|
||||
"Download Completed": "దిగుమతి పూర్తయినది",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s డస్కటాప్ (%(platformName)s)",
|
||||
"Unexpected error preparing the app. See console for details.": "ఆప్ ని తయారు చేసే ప్రక్రియాలో అనుకోని లోపం తలెత్తింది. మరిన్ని వివరాల కోసం కాన్సోల్ ను చూడండి."
|
||||
}
|
||||
|
||||
@@ -3,30 +3,31 @@
|
||||
"Unknown device": "未知设备",
|
||||
"Welcome to Element": "欢迎来到 Element",
|
||||
"Sign In": "登录",
|
||||
"Create Account": "创建账号",
|
||||
"Explore rooms": "探索聊天室",
|
||||
"The message from the parser is: %(message)s": "语法分析器的信息:%(message)s",
|
||||
"Create Account": "创建账户",
|
||||
"Explore rooms": "探索房间",
|
||||
"The message from the parser is: %(message)s": "来自解析器的消息:%(message)s",
|
||||
"Invalid JSON": "无效的 JSON",
|
||||
"Unexpected error preparing the app. See console for details.": "软件准备时出错,详细信息请查看控制台。",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "配置无效: 只能设置 default_server_config、default_server_name 或 default_hs_url。",
|
||||
"Invalid configuration: no default server specified.": "配置无效:没有设置默认服务器。",
|
||||
"Unable to load config file: please refresh the page to try again.": "无法加载配置文件:请再次刷新页面。",
|
||||
"Unexpected error preparing the app. See console for details.": "准备软件时出现意外错误,详细信息请查看控制台。",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "配置无效:只能指定default_server_config、default_server_name或default_hs_url其中之一。",
|
||||
"Invalid configuration: no default server specified.": "配置无效:没有指定默认服务器。",
|
||||
"Unable to load config file: please refresh the page to try again.": "无法加载配置文件:请刷新页面以重试。",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s 桌面版(%(platformName)s)",
|
||||
"Go to your browser to complete Sign In": "转到您的浏览器以完成登录",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s(%(browserName)s,%(osName)s)",
|
||||
"Unsupported browser": "不支持的浏览器",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "请安装 <chromeLink>Chrome</chromeLink>、<firefoxLink>Firefox</firefoxLink> 或 <safariLink>Safari</safariLink> 以获得最佳体验。",
|
||||
"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.": "您可以继续使用您目前的浏览器,但部分或全部的功能可能无法正常工作,应用程序的外观可能也看起来不正确。",
|
||||
"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.": "您可以继续使用您目前的浏览器,但部分或全部功能可能无法正常工作,应用程序的外观可能也看起来不正确。",
|
||||
"I understand the risks and wish to continue": "我了解风险并希望继续",
|
||||
"Go to element.io": "前往 element.io",
|
||||
"Failed to start": "启动失败",
|
||||
"Your Element is misconfigured": "Element 配置错误",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Element 配置文件中包含无效的 JSON,请改正错误并重新加载页面。",
|
||||
"Download Completed": "下载成功",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Element 配置文件中包含无效的 JSON。请改正错误并重新加载页面。",
|
||||
"Download Completed": "下载完成",
|
||||
"Open": "打开",
|
||||
"Your browser can't run %(brand)s": "浏览器无法运行 %(brand)s",
|
||||
"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 驱动",
|
||||
"Use %(brand)s on mobile": "在移动设备上使用 %(brand)s",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "去中心化、加密的聊天与协作,威力本源 $matrixLogo"
|
||||
"Switch to space by number": "按数字切换空间",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "去中心化、加密的聊天与协作,由 $matrixLogo 驱动"
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@
|
||||
<% for (var i=0; i < htmlWebpackPlugin.tags.headTags.length; i++) {
|
||||
var tag = htmlWebpackPlugin.tags.headTags[i];
|
||||
var path = tag.attributes && tag.attributes.href;
|
||||
if (path.indexOf("Inter") !== -1) { %>
|
||||
<link rel="preload" as="font" href="<%= path %>" crossorigin="anonymous"/>
|
||||
if (path.includes("/Inter/")) { %>
|
||||
<link rel="preload" as="font" href=".<%= path %>" crossorigin="anonymous"/>
|
||||
<% }
|
||||
} %>
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ async function start() {
|
||||
loadLanguage,
|
||||
loadTheme,
|
||||
loadApp,
|
||||
loadModules,
|
||||
showError,
|
||||
showIncompatibleBrowser,
|
||||
_t,
|
||||
@@ -155,6 +156,11 @@ async function start() {
|
||||
// now that the config is ready, try to persist logs
|
||||
const persistLogsPromise = setupLogStorage();
|
||||
|
||||
// Load modules before language to ensure any custom translations are respected, and any app
|
||||
// startup functionality is run
|
||||
const loadModulesPromise = loadModules();
|
||||
await settled(loadModulesPromise);
|
||||
|
||||
// Load language after loading config.json so that settingsDefaults.language can be applied
|
||||
const loadLanguagePromise = loadLanguage();
|
||||
// as quickly as we possibly can, set a default theme...
|
||||
@@ -209,6 +215,7 @@ async function start() {
|
||||
// assert things started successfully
|
||||
// ##################################
|
||||
await loadOlmPromise;
|
||||
await loadModulesPromise;
|
||||
await loadThemePromise;
|
||||
await loadLanguagePromise;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
Copyright 2018 - 2021 New Vector Ltd
|
||||
Copyright 2018 - 2022 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -29,11 +29,15 @@ import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
import { setTheme } from "matrix-react-sdk/src/theme";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ModuleRunner } from "matrix-react-sdk/src/modules/ModuleRunner";
|
||||
|
||||
import ElectronPlatform from "./platform/ElectronPlatform";
|
||||
import PWAPlatform from "./platform/PWAPlatform";
|
||||
import WebPlatform from "./platform/WebPlatform";
|
||||
import { initRageshake, initRageshakeStore } from "./rageshakesetup";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - this path is created at runtime and therefore won't exist at typecheck time
|
||||
import { INSTALLED_MODULES } from "../modules";
|
||||
|
||||
export const rageshakePromise = initRageshake();
|
||||
|
||||
@@ -88,8 +92,8 @@ export function loadOlm(): Promise<void> {
|
||||
locateFile: () => olmWasmPath,
|
||||
}).then(() => {
|
||||
logger.log("Using WebAssembly Olm");
|
||||
}).catch((e) => {
|
||||
logger.log("Failed to load Olm: trying legacy version", e);
|
||||
}).catch((wasmLoadError) => {
|
||||
logger.log("Failed to load Olm: trying legacy version", wasmLoadError);
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = 'olm_legacy.js'; // XXX: This should be cache-busted too
|
||||
@@ -102,8 +106,8 @@ export function loadOlm(): Promise<void> {
|
||||
return window.Olm.init();
|
||||
}).then(() => {
|
||||
logger.log("Using legacy Olm");
|
||||
}).catch((e) => {
|
||||
logger.log("Both WebAssembly and asm.js Olm failed!", e);
|
||||
}).catch((legacyLoadError) => {
|
||||
logger.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -157,4 +161,12 @@ export async function showIncompatibleBrowser(onAccept) {
|
||||
document.getElementById('matrixchat'));
|
||||
}
|
||||
|
||||
export async function loadModules() {
|
||||
for (const InstalledModule of INSTALLED_MODULES) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - we know the constructor exists even if TypeScript can't be convinced of that
|
||||
ModuleRunner.instance.registerModule((api) => new InstalledModule(api));
|
||||
}
|
||||
}
|
||||
|
||||
export const _t = languageHandler._t;
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// TODO: Match the user's theme: https://github.com/vector-im/element-web/issues/12794
|
||||
/* TODO: Match the user's theme: https://github.com/vector-im/element-web/issues/12794 */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
@@ -57,8 +57,8 @@ body, html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
// Hidden by default to avoid flashing the prejoin screen at the user when
|
||||
// we're supposed to skip it anyways
|
||||
/* Hidden by default to avoid flashing the prejoin screen at the user when */
|
||||
/* we're supposed to skip it anyways */
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ body, html {
|
||||
}
|
||||
|
||||
#joinButton {
|
||||
// A mix of AccessibleButton styles
|
||||
/* A mix of AccessibleButton styles */
|
||||
cursor: pointer;
|
||||
padding: 7px 18px;
|
||||
text-align: center;
|
||||
@@ -89,7 +89,7 @@ body, html {
|
||||
|
||||
.icon {
|
||||
$icon-size: 42px;
|
||||
margin-top: -$icon-size; // to visually center the form
|
||||
margin-top: -$icon-size; /* to visually center the form */
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
@@ -102,7 +102,7 @@ body, html {
|
||||
display: block;
|
||||
width: $icon-size;
|
||||
height: $icon-size;
|
||||
margin: 0 auto; // center
|
||||
margin: 0 auto; /* center */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd.
|
||||
Copyright 2020-2022 New Vector Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +18,11 @@ import { KJUR } from 'jsrsasign';
|
||||
import {
|
||||
IOpenIDCredentials,
|
||||
IWidgetApiRequest,
|
||||
IWidgetApiRequestData,
|
||||
IWidgetApiResponseData,
|
||||
VideoConferenceCapabilities,
|
||||
WidgetApi,
|
||||
WidgetApiAction,
|
||||
} from "matrix-widget-api";
|
||||
import { ElementWidgetActions } from "matrix-react-sdk/src/stores/widgets/ElementWidgetActions";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
@@ -29,7 +32,7 @@ import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||
import { getVectorConfig } from "../getconfig";
|
||||
|
||||
// We have to trick webpack into loading our CSS for us.
|
||||
require("./index.scss");
|
||||
require("./index.pcss");
|
||||
|
||||
const JITSI_OPENIDTOKEN_JWT_AUTH = 'openidtoken-jwt';
|
||||
|
||||
@@ -52,14 +55,13 @@ let openIdToken: IOpenIDCredentials;
|
||||
let roomName: string;
|
||||
let startAudioOnly: boolean;
|
||||
let isVideoChannel: boolean;
|
||||
let supportsScreensharing: boolean;
|
||||
|
||||
let widgetApi: WidgetApi;
|
||||
let meetApi: any; // JitsiMeetExternalAPI
|
||||
let skipOurWelcomeScreen = false;
|
||||
|
||||
const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev.detail, {});
|
||||
|
||||
(async function() {
|
||||
const setupCompleted = (async () => {
|
||||
try {
|
||||
// Queue a config.json lookup asap, so we can use it later on. We want this to be concurrent with
|
||||
// other setup work and therefore do not block.
|
||||
@@ -89,24 +91,88 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
}
|
||||
|
||||
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||
let readyPromise: Promise<[void, void]>;
|
||||
let widgetApiReady: Promise<void>;
|
||||
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));
|
||||
widgetApi.requestCapabilities(VideoConferenceCapabilities);
|
||||
readyPromise = Promise.all([
|
||||
new Promise<void>(resolve => {
|
||||
widgetApi.once(`action:${ElementWidgetActions.ClientReady}`, ev => {
|
||||
ev.preventDefault();
|
||||
resolve();
|
||||
widgetApi.transport.reply(ev.detail, {});
|
||||
});
|
||||
}),
|
||||
new Promise<void>(resolve => {
|
||||
widgetApi.once("ready", () => resolve());
|
||||
}),
|
||||
]);
|
||||
widgetApi.start();
|
||||
|
||||
const handleAction = (
|
||||
action: WidgetApiAction,
|
||||
handler: (request: IWidgetApiRequestData) => void,
|
||||
): void => {
|
||||
widgetApi.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
await setupCompleted;
|
||||
|
||||
let response: IWidgetApiResponseData;
|
||||
try {
|
||||
await handler(ev.detail.data);
|
||||
response = {};
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
response = { error: { message: e.message } };
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await widgetApi.transport.reply(ev.detail, response);
|
||||
});
|
||||
};
|
||||
|
||||
handleAction(ElementWidgetActions.JoinCall, async ({ audioInput, videoInput }) => {
|
||||
joinConference(audioInput as string | null, videoInput as string | null);
|
||||
});
|
||||
handleAction(ElementWidgetActions.HangupCall, async ({ force }) => {
|
||||
if (force === true) {
|
||||
meetApi?.dispose();
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
closeConference();
|
||||
} else {
|
||||
meetApi?.executeCommand('hangup');
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.MuteAudio, async () => {
|
||||
if (meetApi && !await meetApi.isAudioMuted()) {
|
||||
meetApi.executeCommand('toggleAudio');
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.UnmuteAudio, async () => {
|
||||
if (meetApi && await meetApi.isAudioMuted()) {
|
||||
meetApi.executeCommand('toggleAudio');
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.MuteVideo, async () => {
|
||||
if (meetApi && !await meetApi.isVideoMuted()) {
|
||||
meetApi.executeCommand('toggleVideo');
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.UnmuteVideo, async () => {
|
||||
if (meetApi && await meetApi.isVideoMuted()) {
|
||||
meetApi.executeCommand('toggleVideo');
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.TileLayout, async () => {
|
||||
meetApi?.executeCommand('setTileView', true);
|
||||
});
|
||||
handleAction(ElementWidgetActions.SpotlightLayout, async () => {
|
||||
meetApi?.executeCommand('setTileView', false);
|
||||
});
|
||||
handleAction(ElementWidgetActions.StartLiveStream, async ({ rtmpStreamKey }) => {
|
||||
if (!meetApi) throw new Error("Conference not joined");
|
||||
meetApi.executeCommand('startRecording', {
|
||||
mode: 'stream',
|
||||
// this looks like it should be rtmpStreamKey but we may be on too old
|
||||
// a version of jitsi meet
|
||||
//rtmpStreamKey,
|
||||
youtubeStreamKey: rtmpStreamKey,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
logger.warn("No parent URL or no widget ID - assuming no widget API is available");
|
||||
}
|
||||
@@ -122,6 +188,7 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
roomName = qsParam('roomName', true);
|
||||
startAudioOnly = qsParam('isAudioOnly', true) === "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>{});
|
||||
@@ -134,7 +201,7 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
toggleConferenceVisibility(skipOurWelcomeScreen);
|
||||
|
||||
if (widgetApi) {
|
||||
await readyPromise;
|
||||
await widgetApiReady;
|
||||
|
||||
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
@@ -142,99 +209,6 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
openIdToken = await widgetApi.requestOpenIDConnectToken();
|
||||
logger.log("Got OpenID Connect token");
|
||||
}
|
||||
|
||||
widgetApi.on(`action:${ElementWidgetActions.JoinCall}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
const { audioDevice, videoDevice } = ev.detail.data;
|
||||
joinConference(audioDevice as string | null, videoDevice as string | null);
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.HangupCall}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
meetApi?.executeCommand('hangup');
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.ForceHangupCall}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
meetApi?.dispose();
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
closeConference();
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.MuteAudio}`,
|
||||
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
if (meetApi && !await meetApi.isAudioMuted()) {
|
||||
meetApi.executeCommand('toggleAudio');
|
||||
}
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.UnmuteAudio}`,
|
||||
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
if (meetApi && await meetApi.isAudioMuted()) {
|
||||
meetApi.executeCommand('toggleAudio');
|
||||
}
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.MuteVideo}`,
|
||||
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
if (meetApi && !await meetApi.isVideoMuted()) {
|
||||
meetApi.executeCommand('toggleVideo');
|
||||
}
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.UnmuteVideo}`,
|
||||
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
if (meetApi && await meetApi.isVideoMuted()) {
|
||||
meetApi.executeCommand('toggleVideo');
|
||||
}
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.TileLayout}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
meetApi?.executeCommand('setTileView', true);
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.SpotlightLayout}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
meetApi?.executeCommand('setTileView', false);
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.StartLiveStream}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
if (meetApi) {
|
||||
meetApi.executeCommand('startRecording', {
|
||||
mode: 'stream',
|
||||
// this looks like it should be rtmpStreamKey but we may be on too old
|
||||
// a version of jitsi meet
|
||||
//rtmpStreamKey: ev.detail.data.rtmpStreamKey,
|
||||
youtubeStreamKey: ev.detail.data.rtmpStreamKey,
|
||||
});
|
||||
ack(ev);
|
||||
} else {
|
||||
widgetApi.transport.reply(ev.detail, { error: { message: "Conference not joined" } });
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Now that everything should be set up, skip to the Jitsi splash screen if needed
|
||||
@@ -243,13 +217,6 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
}
|
||||
|
||||
enableJoinButton(); // always enable the button
|
||||
|
||||
// Inform the client that we're ready to receive events
|
||||
try {
|
||||
await widgetApi?.transport.send(ElementWidgetActions.WidgetReady, {});
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("Error setting up Jitsi widget", e);
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
@@ -344,11 +311,11 @@ function closeConference() {
|
||||
}
|
||||
|
||||
// event handler bound in HTML
|
||||
// An audio device of undefined instructs Jitsi to start unmuted with whatever
|
||||
// audio device it can find, while a device of null instructs it to start muted,
|
||||
// and a non-nullish device specifies the label of a specific device to use.
|
||||
// Same for video devices.
|
||||
function joinConference(audioDevice?: string | null, videoDevice?: string | null) {
|
||||
// An audio input of undefined instructs Jitsi to start unmuted with whatever
|
||||
// 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) {
|
||||
let jwt;
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
if (!openIdToken?.access_token) { // eslint-disable-line camelcase
|
||||
@@ -374,8 +341,8 @@ function joinConference(audioDevice?: string | null, videoDevice?: string | null
|
||||
parentNode: document.querySelector("#jitsiContainer"),
|
||||
roomName: conferenceId,
|
||||
devices: {
|
||||
audioInput: audioDevice,
|
||||
videoInput: videoDevice,
|
||||
audioInput,
|
||||
videoInput,
|
||||
},
|
||||
userInfo: {
|
||||
displayName,
|
||||
@@ -390,8 +357,8 @@ function joinConference(audioDevice?: string | null, videoDevice?: string | null
|
||||
configOverwrite: {
|
||||
subject: roomName,
|
||||
startAudioOnly,
|
||||
startWithAudioMuted: audioDevice === null,
|
||||
startWithVideoMuted: videoDevice === null,
|
||||
startWithAudioMuted: audioInput === null,
|
||||
startWithVideoMuted: videoInput === null,
|
||||
// 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
|
||||
@@ -408,11 +375,18 @@ function joinConference(audioDevice?: string | null, videoDevice?: string | null
|
||||
// deployments that have it enabled
|
||||
options.configOverwrite.prejoinConfig = { enabled: false };
|
||||
// Use a simplified set of toolbar buttons
|
||||
options.configOverwrite.toolbarButtons = [
|
||||
"microphone", "camera", "desktop", "tileview", "hangup",
|
||||
];
|
||||
options.configOverwrite.toolbarButtons = ["microphone", "camera", "tileview", "hangup"];
|
||||
// Note: We can hide the screenshare button in video rooms but not in
|
||||
// normal conference calls, since in video rooms we control exactly what
|
||||
// set of controls appear, but in normal calls we need to leave that up
|
||||
// to the deployment's configuration.
|
||||
// https://github.com/vector-im/element-web/issues/4880#issuecomment-940002464
|
||||
if (supportsScreensharing) options.configOverwrite.toolbarButtons.splice(2, 0, "desktop");
|
||||
// Hide all top bar elements
|
||||
options.configOverwrite.conferenceInfo = { autoHide: [] };
|
||||
// Remove the ability to hide your own tile, since we're hiding the
|
||||
// settings button which would be the only way to get it back
|
||||
options.configOverwrite.disableSelfViewSettings = true;
|
||||
}
|
||||
|
||||
meetApi = new JitsiMeetExternalAPI(jitsiDomain, options);
|
||||
|
||||
@@ -175,9 +175,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
* Return true if platform supports multi-language
|
||||
* spell-checking, otherwise false.
|
||||
*/
|
||||
public supportsMultiLanguageSpellCheck(): boolean {
|
||||
// Electron uses OS spell checking on macOS, so no need for in-app options
|
||||
if (isMac) return false;
|
||||
public supportsSpellCheckSettings(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -238,6 +236,10 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
electron.send('loudNotification');
|
||||
}
|
||||
|
||||
public needsUrlTooltips(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async getAppVersion(): Promise<string> {
|
||||
return this.ipc.call('getAppVersion');
|
||||
}
|
||||
@@ -301,7 +303,18 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
return this.ipc.call('setLanguage', preferredLangs);
|
||||
}
|
||||
|
||||
public setSpellCheckLanguages(preferredLangs: string[]) {
|
||||
public setSpellCheckEnabled(enabled: boolean): void {
|
||||
this.ipc.call('setSpellCheckEnabled', enabled).catch(error => {
|
||||
logger.log("Failed to send setSpellCheckEnabled IPC to Electron");
|
||||
logger.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
public async getSpellCheckEnabled(): Promise<boolean> {
|
||||
return this.ipc.call('getSpellCheckEnabled');
|
||||
}
|
||||
|
||||
public setSpellCheckLanguages(preferredLangs: string[]): void {
|
||||
this.ipc.call('setSpellCheckLanguages', preferredLangs).catch(error => {
|
||||
logger.log("Failed to send setSpellCheckLanguages IPC to Electron");
|
||||
logger.error(error);
|
||||
@@ -320,6 +333,11 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
return true;
|
||||
}
|
||||
|
||||
public supportsJitsiScreensharing(): boolean {
|
||||
// See https://github.com/vector-im/element-web/issues/4880
|
||||
return false;
|
||||
}
|
||||
|
||||
public async getAvailableSpellCheckLanguages(): Promise<string[]> {
|
||||
return this.ipc.call('getAvailableSpellCheckLanguages');
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export class IPCManager {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
private onIpcReply = (ev: {}, payload: IPCPayload): void => {
|
||||
private onIpcReply = (_ev: {}, payload: IPCPayload): void => {
|
||||
if (payload.id === undefined) {
|
||||
logger.warn("Ignoring IPC reply with no ID");
|
||||
return;
|
||||
|
||||
@@ -47,7 +47,8 @@ export default abstract class VectorBasePlatform extends BasePlatform {
|
||||
if (this._favicon) {
|
||||
return this._favicon;
|
||||
}
|
||||
return this._favicon = new Favicon();
|
||||
this._favicon = new Favicon();
|
||||
return this._favicon;
|
||||
}
|
||||
|
||||
private updateFavicon() {
|
||||
|
||||
@@ -80,7 +80,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
// annoyingly, the latest spec says this returns a
|
||||
// promise, but this is only supported in Chrome 46
|
||||
// and Firefox 47, so adapt the callback API.
|
||||
return new Promise(function(resolve, reject) {
|
||||
return new Promise(function(resolve) {
|
||||
window.Notification.requestPermission((result) => {
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ export function initRageshake() {
|
||||
logger.log("To fix line numbers in Chrome: " +
|
||||
"Meatball menu → Settings → Ignore list → Add /rageshake\\.js$");
|
||||
|
||||
window.addEventListener('beforeunload', (e) => {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
logger.log('element-web closing');
|
||||
// try to flush the logs to indexeddb
|
||||
rageshake.flush();
|
||||
|
||||
@@ -41,7 +41,7 @@ function routeUrl(location: Location) {
|
||||
(window.matrixChat as MatrixChatType).showScreen(s.screen, s.params);
|
||||
}
|
||||
|
||||
function onHashChange(ev: HashChangeEvent) {
|
||||
function onHashChange() {
|
||||
if (decodeURIComponent(window.location.hash) === lastLocationHashSet) {
|
||||
// we just set this: no need to route it!
|
||||
return;
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* joining.js: tests for the various paths when joining a room */
|
||||
|
||||
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
|
||||
import WebPlatform from '../../src/vector/platform/WebPlatform';
|
||||
import * as jssdk from "matrix-js-sdk/src/matrix";
|
||||
import "../jest-mocks";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import ReactTestUtils from "react-dom/test-utils";
|
||||
import {makeType} from "matrix-react-sdk/src/utils/TypeUtils";
|
||||
import {ValidatedServerConfig} from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
|
||||
import {sleep} from "../test-utils";
|
||||
import MockHttpBackend from "matrix-mock-request";
|
||||
import "fake-indexeddb/auto";
|
||||
import { RoomView as RoomViewClass } from 'matrix-react-sdk/src/components/structures/RoomView';
|
||||
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
|
||||
import RoomDirectory from "matrix-react-sdk/src/components/structures/RoomDirectory";
|
||||
import RoomPreviewBar from "matrix-react-sdk/src/components/views/rooms/RoomPreviewBar";
|
||||
|
||||
const HS_URL='http://localhost';
|
||||
const IS_URL='http://localhost';
|
||||
const USER_ID='@me:localhost';
|
||||
const ACCESS_TOKEN='access_token';
|
||||
|
||||
describe('joining a room', function() {
|
||||
describe('over federation', function() {
|
||||
let parentDiv;
|
||||
let httpBackend;
|
||||
let matrixChat;
|
||||
|
||||
beforeEach(function() {
|
||||
httpBackend = new MockHttpBackend();
|
||||
jssdk.request(httpBackend.requestFn);
|
||||
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);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
if (parentDiv) {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
parentDiv.remove();
|
||||
parentDiv = null;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Re-enable test
|
||||
// The test is currently disabled because the room directory now resides in a dialog,
|
||||
// which is not accessible from the MatrixChat component anymore. Convincing react that
|
||||
// the dialog does exist and is under a different tree is incredibly difficult though,
|
||||
// so for now the test has been disabled. We should revisit this test when someone has
|
||||
// the time to kill to figure this out. Problem area is highlighted within the test.
|
||||
xit('should not get stuck at a spinner', function() {
|
||||
const ROOM_ALIAS = '#alias:localhost';
|
||||
const ROOM_ID = '!id:localhost';
|
||||
|
||||
httpBackend.when('GET', '/pushrules').respond(200, {});
|
||||
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
||||
|
||||
// note that we deliberately do *not* set an expectation for a
|
||||
// presence update - setting one makes the first httpBackend.flush
|
||||
// return before the first /sync arrives.
|
||||
|
||||
// start with a logged-in client
|
||||
localStorage.setItem("mx_hs_url", HS_URL );
|
||||
localStorage.setItem("mx_is_url", IS_URL );
|
||||
localStorage.setItem("mx_access_token", ACCESS_TOKEN );
|
||||
localStorage.setItem("mx_user_id", USER_ID);
|
||||
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
|
||||
const config = {
|
||||
validated_server_config: makeType(ValidatedServerConfig, {
|
||||
hsUrl: HS_URL,
|
||||
hsName: "TEST_ENVIRONMENT",
|
||||
hsNameIsDifferent: false, // yes, we lie
|
||||
isUrl: IS_URL,
|
||||
}),
|
||||
};
|
||||
|
||||
const mc = (
|
||||
<MatrixChat
|
||||
config={config}
|
||||
serverConfig={config.validated_server_config}
|
||||
makeRegistrationUrl={()=>{throw new Error("unimplemented");}}
|
||||
initialScreenAfterLogin={{
|
||||
screen: 'directory',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
matrixChat = ReactDOM.render(mc, parentDiv);
|
||||
|
||||
let roomView;
|
||||
|
||||
// wait for /sync to happen. This may take some time, as the client
|
||||
// has to initialise indexeddb.
|
||||
console.log("waiting for /sync");
|
||||
httpBackend.when('GET', '/sync')
|
||||
.respond(200, {});
|
||||
|
||||
return httpBackend.flushAllExpected({
|
||||
timeout: 1000,
|
||||
}).then(() => {
|
||||
// wait for the directory requests
|
||||
httpBackend.when('POST', '/publicRooms').respond(200, {chunk: []});
|
||||
httpBackend.when('GET', '/thirdparty/protocols').respond(200, {});
|
||||
return httpBackend.flushAllExpected();
|
||||
}).then(() => {
|
||||
console.log(`${Date.now()} App made requests for directory view; switching to a room.`);
|
||||
|
||||
// TODO: Make this look in the right spot for the directory dialog.
|
||||
// See the comment block at the top of the test for a bit more information. The short
|
||||
// story here is that the RoomDirectory does not exist under matrixChat anymore, or even
|
||||
// the parentDiv we have access to. Asking React to find the RoomDirectory as a child of
|
||||
// the document results in it complaining that you didn't give it a component tree to
|
||||
// search in. When you do get a reference to the component tree based off the document
|
||||
// root and ask it to search, it races and can't find the component in time. To top it
|
||||
// all off, MatrixReactTestUtils can't find the element in time either even with a very
|
||||
// high number of attempts. Assuming we can get a reference to the RoomDirectory in a
|
||||
// dialog, the rest of the test should be fine.
|
||||
const roomDir = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, RoomDirectory,
|
||||
);
|
||||
|
||||
// enter an alias in the input, and simulate enter
|
||||
const input = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
roomDir, 'input') as HTMLInputElement;
|
||||
input.value = ROOM_ALIAS;
|
||||
ReactTestUtils.Simulate.change(input);
|
||||
ReactTestUtils.Simulate.keyUp(input, {key: 'Enter'});
|
||||
|
||||
// that should create a roomview which will start a peek; wait
|
||||
// for the peek.
|
||||
httpBackend.when('GET', '/directory/room/'+encodeURIComponent(ROOM_ALIAS)).respond(200, { room_id: ROOM_ID });
|
||||
httpBackend.when('GET', '/rooms/'+encodeURIComponent(ROOM_ID)+"/initialSync")
|
||||
.respond(401, {errcode: 'M_GUEST_ACCESS_FORBIDDEN'});
|
||||
|
||||
return httpBackend.flushAllExpected();
|
||||
}).then(() => {
|
||||
console.log(`${Date.now()} App made room preview request`);
|
||||
|
||||
// we should now have a roomview
|
||||
roomView = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, RoomViewClass);
|
||||
|
||||
// the preview bar may take a tick to be displayed
|
||||
return sleep(1);
|
||||
}).then(() => {
|
||||
const previewBar = ReactTestUtils.findRenderedComponentWithType(
|
||||
roomView, RoomPreviewBar);
|
||||
|
||||
const joinLink = ReactTestUtils.findRenderedDOMComponentWithTag(
|
||||
previewBar, 'a');
|
||||
|
||||
ReactTestUtils.Simulate.click(joinLink);
|
||||
|
||||
httpBackend.when('POST', '/join/'+encodeURIComponent(ROOM_ALIAS))
|
||||
.respond(200, {room_id: ROOM_ID});
|
||||
}).then(() => {
|
||||
// wait for the join request to be made
|
||||
return sleep(1);
|
||||
}).then(() => {
|
||||
// and again, because the state update has to go to the store and
|
||||
// then one dispatch within the store, then to the view
|
||||
// XXX: This is *super flaky*: a better way would be to declare
|
||||
// that we expect a certain state transition to happen, then wait
|
||||
// for that transition to occur.
|
||||
return sleep(1);
|
||||
}).then(() => {
|
||||
// the roomview should now be loading
|
||||
expect(roomView.state.room).toBe(null);
|
||||
expect(roomView.state.joining).toBe(true);
|
||||
|
||||
// there should be a spinner
|
||||
ReactTestUtils.findRenderedDOMComponentWithClass(
|
||||
roomView, "mx_Spinner");
|
||||
|
||||
// flush it through
|
||||
return httpBackend.flush('/join/'+encodeURIComponent(ROOM_ALIAS));
|
||||
}).then(() => {
|
||||
httpBackend.verifyNoOutstandingExpectation();
|
||||
|
||||
return sleep(1);
|
||||
}).then(() => {
|
||||
// NB. we don't expect the 'joining' flag to reset at any point:
|
||||
// it will stay set and we observe whether we have Room object for
|
||||
// the room and whether our member event shows we're joined.
|
||||
|
||||
// now send the room down the /sync pipe
|
||||
httpBackend.when('GET', '/sync').
|
||||
respond(200, {
|
||||
rooms: {
|
||||
join: {
|
||||
[ROOM_ID]: {
|
||||
state: {},
|
||||
timeline: {
|
||||
events: [],
|
||||
limited: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
// now the room should have loaded
|
||||
expect(roomView.state.room).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -32,7 +32,7 @@ import * as test_utils from '../test-utils';
|
||||
import MockHttpBackend from 'matrix-mock-request';
|
||||
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
|
||||
import {makeType} from "matrix-react-sdk/src/utils/TypeUtils";
|
||||
import {ValidatedServerConfig} from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
|
||||
import { ValidatedServerConfig } from 'matrix-react-sdk/src/utils/ValidatedServerConfig';
|
||||
import {sleep} from "../test-utils";
|
||||
import "fake-indexeddb/auto";
|
||||
import {cleanLocalstorage} from "../test-utils";
|
||||
|
||||
@@ -48,16 +48,16 @@ describe('getVectorConfig()', () => {
|
||||
it('requests specific config for document domain', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
await getVectorConfig();
|
||||
|
||||
expect(request.mock.calls[0][0]).toEqual({ method: "GET", url: 'config.app.element.io.json', qs: { cachebuster: now } })
|
||||
});
|
||||
|
||||
|
||||
it('adds trailing slash to relativeLocation when not an empty string', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
await getVectorConfig('..');
|
||||
|
||||
expect(request.mock.calls[0][0]).toEqual(expect.objectContaining({ url: '../config.app.element.io.json' }))
|
||||
@@ -67,7 +67,7 @@ describe('getVectorConfig()', () => {
|
||||
it('returns parsed specific config when it is non-empty', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig))
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
const result = await getVectorConfig();
|
||||
expect(result).toEqual(specificConfig);
|
||||
});
|
||||
@@ -75,7 +75,7 @@ describe('getVectorConfig()', () => {
|
||||
it('returns general config when specific config succeeds but is empty', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify({}))
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
const result = await getVectorConfig();
|
||||
expect(result).toEqual(generalConfig);
|
||||
});
|
||||
@@ -83,7 +83,7 @@ describe('getVectorConfig()', () => {
|
||||
it('returns general config when specific config 404s', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 404 })
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
const result = await getVectorConfig();
|
||||
expect(result).toEqual(generalConfig);
|
||||
});
|
||||
@@ -91,7 +91,7 @@ describe('getVectorConfig()', () => {
|
||||
it('returns general config when specific config is fetched from a file and is empty', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 0 }, '')
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
const result = await getVectorConfig();
|
||||
expect(result).toEqual(generalConfig);
|
||||
});
|
||||
@@ -99,7 +99,7 @@ describe('getVectorConfig()', () => {
|
||||
it('returns general config when specific config returns a non-200 status', async () => {
|
||||
setRequestMockImplementationOnce(undefined, { status: 401 })
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
const result = await getVectorConfig();
|
||||
expect(result).toEqual(generalConfig);
|
||||
});
|
||||
@@ -107,7 +107,7 @@ describe('getVectorConfig()', () => {
|
||||
it('returns general config when specific config returns an error', async () => {
|
||||
setRequestMockImplementationOnce('err1')
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig))
|
||||
|
||||
|
||||
const result = await getVectorConfig();
|
||||
expect(result).toEqual(generalConfig);
|
||||
});
|
||||
@@ -119,4 +119,12 @@ describe('getVectorConfig()', () => {
|
||||
await expect(() => getVectorConfig()).rejects.toEqual({"err": "err-general", "response": undefined});
|
||||
});
|
||||
|
||||
it('rejects with an error when config is invalid JSON', async () => {
|
||||
setRequestMockImplementationOnce('err-specific');
|
||||
setRequestMockImplementationOnce(undefined, { status: 200 }, '{"invalid": "json",}');
|
||||
|
||||
await expect(() => getVectorConfig()).rejects.toEqual({
|
||||
err: new SyntaxError("Unexpected token } in JSON at position 19"),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
33
test/unit-tests/vector/platform/PWAPlatform-test.ts
Normal file
33
test/unit-tests/vector/platform/PWAPlatform-test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2022 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 PWAPlatform from "../../../../src/vector/platform/PWAPlatform";
|
||||
|
||||
describe('PWAPlatform', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("setNotificationCount", () => {
|
||||
it("should call Navigator::setAppBadge", () => {
|
||||
navigator.setAppBadge = jest.fn().mockResolvedValue(undefined);
|
||||
const platform = new PWAPlatform();
|
||||
expect(navigator.setAppBadge).not.toHaveBeenCalled();
|
||||
platform.setNotificationCount(123);
|
||||
expect(navigator.setAppBadge).toHaveBeenCalledWith(123);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -30,6 +30,13 @@ describe('WebPlatform', () => {
|
||||
expect(platform.getHumanReadableName()).toEqual('Web Platform');
|
||||
});
|
||||
|
||||
it('registers service worker', () => {
|
||||
// @ts-ignore - mocking readonly object
|
||||
navigator.serviceWorker = { register: jest.fn() };
|
||||
new WebPlatform();
|
||||
expect(navigator.serviceWorker.register).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('notification support', () => {
|
||||
const mockNotification = {
|
||||
requestPermission: jest.fn(),
|
||||
@@ -50,7 +57,7 @@ describe('WebPlatform', () => {
|
||||
it('supportsNotifications returns true when platform supports notifications', () => {
|
||||
expect(new WebPlatform().supportsNotifications()).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('maySendNotifications returns true when notification permissions are not granted', () => {
|
||||
expect(new WebPlatform().maySendNotifications()).toBe(false);
|
||||
});
|
||||
@@ -109,78 +116,76 @@ describe('WebPlatform', () => {
|
||||
});
|
||||
|
||||
describe('pollForUpdate()', () => {
|
||||
|
||||
it('should return not available and call showNoUpdate when current version matches most recent version', async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
|
||||
const showUpdate = jest.fn();
|
||||
const showNoUpdate = jest.fn();
|
||||
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
|
||||
|
||||
|
||||
expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
|
||||
expect(showUpdate).not.toHaveBeenCalled();
|
||||
expect(showNoUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should strip v prefix from versions before comparing', async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
setRequestMockImplementation(undefined, { status: 200}, `v${prodVersion}`);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
|
||||
const showUpdate = jest.fn();
|
||||
const showNoUpdate = jest.fn();
|
||||
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
|
||||
|
||||
|
||||
// versions only differ by v prefix, no update
|
||||
expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable });
|
||||
expect(showUpdate).not.toHaveBeenCalled();
|
||||
expect(showNoUpdate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should return ready and call showUpdate when current version differs from most recent version', async () => {
|
||||
process.env.VERSION = '0.0.0'; // old version
|
||||
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
|
||||
const showUpdate = jest.fn();
|
||||
const showNoUpdate = jest.fn();
|
||||
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
|
||||
|
||||
|
||||
expect(result).toEqual({ status: UpdateCheckStatus.Ready });
|
||||
expect(showUpdate).toHaveBeenCalledWith('0.0.0', prodVersion);
|
||||
expect(showNoUpdate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should return ready without showing update when user registered in last 24', async () => {
|
||||
process.env.VERSION = '0.0.0'; // old version
|
||||
jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(true);
|
||||
setRequestMockImplementation(undefined, { status: 200}, prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
|
||||
const showUpdate = jest.fn();
|
||||
const showNoUpdate = jest.fn();
|
||||
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
|
||||
|
||||
|
||||
expect(result).toEqual({ status: UpdateCheckStatus.Ready });
|
||||
expect(showUpdate).not.toHaveBeenCalled();
|
||||
expect(showNoUpdate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should return error when version check fails', async () => {
|
||||
setRequestMockImplementation('oups');
|
||||
const platform = new WebPlatform();
|
||||
|
||||
|
||||
const showUpdate = jest.fn();
|
||||
const showNoUpdate = jest.fn();
|
||||
const result = await platform.pollForUpdate(showUpdate, showNoUpdate);
|
||||
|
||||
|
||||
expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: 'Unknown Error' });
|
||||
expect(showUpdate).not.toHaveBeenCalled();
|
||||
expect(showNoUpdate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
17
tsconfig.module_system.json
Normal file
17
tsconfig.module_system.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"declaration": false,
|
||||
"outDir": "./lib/module_system",
|
||||
"lib": [
|
||||
"es2019"
|
||||
],
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./module_system/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -21,14 +21,14 @@ if (!process.env.VERSION) {
|
||||
|
||||
const cssThemes = {
|
||||
// CSS themes
|
||||
"theme-legacy-light": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
|
||||
"theme-legacy-dark": "./node_modules/matrix-react-sdk/res/themes/legacy-dark/css/legacy-dark.scss",
|
||||
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.scss",
|
||||
"theme-legacy-light": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.pcss",
|
||||
"theme-legacy-dark": "./node_modules/matrix-react-sdk/res/themes/legacy-dark/css/legacy-dark.pcss",
|
||||
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.pcss",
|
||||
"theme-light-high-contrast":
|
||||
"./node_modules/matrix-react-sdk/res/themes/light-high-contrast/css/light-high-contrast.scss",
|
||||
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.scss",
|
||||
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.scss",
|
||||
"theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.scss",
|
||||
"./node_modules/matrix-react-sdk/res/themes/light-high-contrast/css/light-high-contrast.pcss",
|
||||
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.pcss",
|
||||
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.pcss",
|
||||
"theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.pcss",
|
||||
};
|
||||
|
||||
function getActiveThemes() {
|
||||
@@ -187,8 +187,9 @@ module.exports = (env, argv) => {
|
||||
"react": path.resolve(__dirname, 'node_modules/react'),
|
||||
"react-dom": path.resolve(__dirname, 'node_modules/react-dom'),
|
||||
|
||||
// same goes for js-sdk - we don't need two copies.
|
||||
// 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 prop-types and sanitize-html
|
||||
"prop-types": path.resolve(__dirname, 'node_modules/prop-types'),
|
||||
"sanitize-html": path.resolve(__dirname, 'node_modules/sanitize-html'),
|
||||
@@ -286,7 +287,6 @@ module.exports = (env, argv) => {
|
||||
// plain CSS together for the bundler.
|
||||
|
||||
require("postcss-simple-vars")(),
|
||||
require("postcss-strip-inline-comments")(),
|
||||
require("postcss-hexrgba")(),
|
||||
|
||||
// It's important that this plugin is last otherwise we end
|
||||
@@ -300,10 +300,10 @@ module.exports = (env, argv) => {
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
test: /\.pcss$/,
|
||||
use: [
|
||||
/**
|
||||
* This code is hopeful that no .scss outside of our themes will be directly imported in any
|
||||
* This code is hopeful that no .pcss outside of our themes will be directly imported in any
|
||||
* of the JS/TS files.
|
||||
* Should be MUCH better with webpack 5, but we're stuck to this solution for now.
|
||||
*/
|
||||
@@ -349,14 +349,12 @@ module.exports = (env, argv) => {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
plugins: () => [
|
||||
// Note that we use slightly different plugins for SCSS.
|
||||
|
||||
// Note that we use slightly different plugins for PostCSS.
|
||||
require('postcss-import')(),
|
||||
require("postcss-mixins")(),
|
||||
require("postcss-simple-vars")(),
|
||||
require("postcss-nested")(),
|
||||
require("postcss-easings")(),
|
||||
require("postcss-strip-inline-comments")(),
|
||||
require("postcss-hexrgba")(),
|
||||
|
||||
// It's important that this plugin is last otherwise we end
|
||||
@@ -502,7 +500,7 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
issuer: /\.(scss|css)$/,
|
||||
issuer: /\.(pcss|scss|css)$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
@@ -528,7 +526,7 @@ module.exports = (env, argv) => {
|
||||
oneOf: [
|
||||
{
|
||||
// Assets referenced in CSS files
|
||||
issuer: /\.(scss|css)$/,
|
||||
issuer: /\.(pcss|scss|css)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
esModule: false,
|
||||
@@ -633,6 +631,10 @@ module.exports = (env, argv) => {
|
||||
new SentryCliPlugin({
|
||||
release: process.env.VERSION,
|
||||
include: "./webapp/bundles",
|
||||
errorHandler: (err, invokeErr, compilation) => {
|
||||
compilation.warnings.push('Sentry CLI Plugin: ' + err.message);
|
||||
console.log(`::warning title=Sentry error::${err.message}`);
|
||||
},
|
||||
}),
|
||||
new webpack.EnvironmentPlugin(['VERSION']),
|
||||
].filter(Boolean),
|
||||
|
||||
Reference in New Issue
Block a user